#! /bin/ksh
#
# @(#) backout_mu 8.0 2001/01/15 SMI
#
# Copyright (c) 1994-2001 Sun Microsystems, Inc.  All Rights Reserved. Sun
# considers its source code as an unpublished, proprietary trade secret, and
# it is available only under strict license provisions.  This copyright
# notice is placed here only to protect Sun in the event the source is
# deemed a published work.  Dissassembly, decompilation, or other means of
# reducing the object code to human readable form is prohibited by the
# license agreement under which this code is provided to the user or company
# in possession of this copy.
#
# RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the Government
# is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the
# Rights in Technical Data and Computer Software clause at DFARS 52.227-7013
# and in similar clauses in the FAR and NASA FAR Supplement.
#
# backout_mu for 2.8. Removes a set of patches quickly.
#
# Exit Codes:
#	0	No error
#	1       User interrupt
#	2	Cannot find INST_RELEASE file
#	3       Required utility not found
#	6	-B (backout directory) option is not a directory
#	8	-R (root directory) option is not a directory
#	9	/export/<service_area> is not a valid service area
#	10	Unrecognized option
#	11	Can't write to log file
#	12	Client SUNWcar (core architecture root) package missing
#	13	Unsupported architecture (not sparc or i386)
#	16	Script run by non-root user
#	17	OS version of target system is not 2.7
#	18	Tools directory missing
#       19      Tools directory does not contain executable 
#                  installpatch.fast and/or backoutpatch.fast
#	20      Service area is not Solaris_2.7
#	21      -S and -R options are mutually exclusive
#	26	Service area doesn't have a valid /var/sadm/pkg directory
#	27	Cannot find a state file needed to determine backout list
#	28	-T (tool directory) option is not a directory
#	29	Cannot find installpatch.fast file in directory specified by -T
#	30	Searched for but could not find Tools directory

set -A PatchRemoveError

PatchRemoveError[1]="Usage error"
PatchRemoveError[2]="Attempt to backout a patch that hasn't been applied"
PatchRemoveError[3]="Effective UID is not root"
PatchRemoveError[4]="No saved files to restore"
PatchRemoveError[5]="pkgrm failed"
PatchRemoveError[6]="Attempt to back out an obsoleted patch"
PatchRemoveError[7]="Attempt to restore CPIO archived files failed"
PatchRemoveError[8]="Invalid patch id format"
PatchRemoveError[9]="Prebackout script failed"
PatchRemoveError[10]="Postbackout script failed"
PatchRemoveError[11]="Suspended due to administrative defaults"  
PatchRemoveError[12]="Backoutpatch could not locate the backout data"
PatchRemoveError[13]="The relative directory supplied can't be found"
PatchRemoveError[14]="Installpatch has been interrupted, re-invoke installpatch"
PatchRemoveError[15]="This patch is required by a patch already installed, can't back it out."

# The current build of MU
MU_BUILD="04"

# The current version of the state file 
STATE_VERSION="1.0" 

# File containing release and MU information
RELEASE_FILE="/etc/release"

# backout_mu Version Number
VERSION="8."$MU_BUILD

# Supported OS
MU_OS="8"

# If we don't completely read the list of patches then
# we assume the user interrupted the program.
 
UserInterrupted="yes"

# Version Name
# MU_VERSION_NAME="MU8_"$MU_BUILD
MU_VERSION_NAME="Solaris_8MU4"

MS_NAME="MU Backout"
RELEASE=
LOGFILE=
logname=
MU_INFO=

succeeded_list=""
typeset -i success_count=0
failed_list=""

# Determine the plaform in use and pass results to CDROMLOC
MU_PLATFORM=$(uname -p)

	if [ $MU_PLATFORM = "sparc" ]; then
		CDROMLOC="/cdrom/s8_maintenance_update_4_sparc"
	
	else

		CDROMLOC="/cdrom/s8_maintenance_update_4_x86"
	
	fi

STATE_FILE=""

# No need to remove this file. It gets renamed away,
TMP_STATE_FILE="/tmp/state.$$"

#
# Force the path to first look in certain directories
#
PATH=/usr/bin:/usr/sbin:/sbin:/bin:$PATH
export PATH

# Needed Utilities
AWK=/usr/bin/awk
CAT=/usr/bin/cat
CHGRP=/usr/bin/chgrp
CHMOD=/usr/bin/chmod
CHOWN=/usr/bin/chown
CP=/usr/bin/cp
DATECMD=/usr/bin/date
ECHO=/usr/bin/echo
EXPR=/usr/bin/expr
GETTEXT=/usr/bin/gettext
GREP=/usr/bin/grep
HEAD=/usr/bin/head
LN=/usr/bin/ln
LS=/usr/bin/ls
MKDIR=/usr/bin/mkdir
MV=/usr/bin/mv
NAWK=/usr/bin/nawk
PATCHRM=/usr/sbin/patchrm
PR=/usr/bin/pr
## PWDCMD=/usr/bin/pwd
RM=/usr/bin/rm
SED=/usr/bin/sed
WC=/usr/bin/wc
XARGS=/usr/bin/xargs
 
REQD_CMDS="$AWK $CAT $CHGRP $CHMOD $CHOWN $CP $DATECMD $ECHO $EXPR $GETTEXT $GREP $LN $LS \
           $MKDIR $MV $NAWK $PATCHRM $PR $RM $SED $WC $XARGS"

disable_do_busy="no"

BACKOUT_MU_PID=0
## uTOOLDIR=""
## TOOLDIR=`$PWDCMD`
MU_TOP="MU"
TOP_DIR=""

ROOTDIR="/"
SERVICE_AREA=""
ROOTARG=""
BACKOUTDIR=""
BACKOUTARG=""
DO_PKGADD_FILE="/tmp/check_pkgadd.$$"

ARCH=""
LPROC=""

MGRSOFTINFO="none"
TRGSOFTINFO="none"

Mgrprodver=""
prodver=""
Product=""
TrgOSVers=""

TMP_LIST="/tmp/plist.$$"

NEW_SOFTINFO="/var/sadm/system/admin/INST_RELEASE"
PATCHLIST=/tmp/patchlist.$$

CLEAN_UP_FILES="$PATCHLIST $TMP_LIST"
CLEAN_UP_DIRS=

umask 022

COMPACT_SLASHES="$SED s,//*,/,g"

## ####################  set_tooldir ####################  
## 
## # Description: 
## #	Figure out where the Tools directory is.
## # Parameters: 
## #	None.
## # Globals set:  
## #	TOOLDIR, ARCH, LPROC
## # Globals used: 
## #	TOP_DIR, ROOTDIR, LOGFILE, PWDCMD, uTOOLDIR
## # Comments:
## #	The need for set_tooldir() to check for the presence of the TOOLDIR,
## #	installpatch.fast and backoutpatch.fast no longer exists. The routine
## #	has not been removed from the code however as a future need for the
## #	TOOLDIR may arise and I'd rather not have to reconstruct the code.
## #
## 
## function set_tooldir {
## 
## # Set TOOLDIR to location of Patch Tools (installpatch.fast and backoutpatch.fast)
## # Here's the variable setting and search algorithm:
## #
## #
## #   LPROC is set to sparc or x86 depending on ARCH field of $ROOTDIR/var/sadm/pkg/SUNWcsr/pkginfo
## #
## #   If uTOOLDIR is set and if uTOOLDIR/installpatch.fast
## #     exists then      					TOOLDIR=uTOOLDIR
## #
## #   If uTOOLDIR is set and if
## #     uTOOLDIR/MU_TOP/common/Tools/installpatch.fast 
## #     exists then          				TOOLDIR=./uTOOLDIR/MU_TOP/common/Tools
## #
## #   If uTOOLDIR is set but we couldn't find a TOOLDIR
## #     in the last two places we looked then error out.
## #
## #   If ./common/Tools exists then                       TOOLDIR=./common/Tools
## #
## #   If ./MU_TOP/common/Tools exists then                TOOLDIR=./MU_TOP/common/Tools
## #
## #   If /cdrom/cdrom0/MU_TOP/common/Tools then           TOOLDIR=/cdrom/cdrom0/MU_TOP/LPROC/Patches
## #
## #   If TOOLDIR hasn't been set by now, error out.
## #
## 
##	if [ "$TOP_DIR" = "" ]; then
##		MU_TOP="MU"
##	else
##		MU_TOP=$TOP_DIR
##	fi
## 
##	pkginfo -R $ROOTDIR -q SUNWcar
##	if [ $? != 0 ]; then
##		$GETTEXT "backout_mu: SUNWcar (core architecture root) package" | tee -a $LOGFILE
##		$GETTEXT "does not exist in $ROOTDIR/var/sadm/pkg.\n" | tee -a $LOGFILE
##		do_file_cleanup
##		exit 12
##	fi
## 
##	ARCH=`$GREP "^ARCH=" $ROOTDIR/var/sadm/pkg/SUNWcar/pkginfo | $SED 's/ARCH=//'`
##	LPROC=`$EXPR $ARCH : '\(.*\)\..*'`
##	if [[ "$LPROC" != "sparc" && "$LPROC" != "i386" ]]; then
##		$GETTEXT "backout_mu only supports sparc and i386 architectures.\n" | tee -a $LOGFILE
##		$GETTEXT "backout_mu has detected ARCH=$LPROC.\n" | tee -a $LOGFILE
##		do_file_cleanup
##		exit 13
##	fi
## 
##	# save the value of the current working directory
##	xdir=`$PWDCMD`
## 
##	if [[ "$uTOOLDIR" != "" && -x $uTOOLDIR/installpatch.fast ]]; then
##		# uTOOLDIR is Tools directory
##		cd $uTOOLDIR
##		TOOLDIR=`$PWDCMD`
##		cd $xdir
##		return
##	fi
## 
##	if [[ "$uTOOLDIR" != "" &&  -x $uTOOLDIR/$MU_TOP/common/Tools/installpatch.fast ]]; then
##		# uTOOLDIR/MU_TOP/common/Tools is the patch directory
##		cd $uTOOLDIR/$MU_TOP/common/Tools
##		TOOLDIR=`$PWDCMD`
##		cd $xdir
##		return
##	fi
## 
## 	# If we've gotten to here then the -T parameter was set but we couldn't find installpatch.fast
## 	# in uTOOLDIR or in uTOOLDIR/MU_TOP/common/Tools
## 
## 	if [ "$uTOOLDIR" != "" ]; then
## 		$GETTEXT "-T parameter does not point to a directory containing patching tools.\n" | tee -a ${LOGFILE}
## 		$GETTEXT "Looked in $uTOOLDIR and in $uTOOLDIR/$MU_TOP/common/Tools.\n" | tee -a ${LOGFILE}
## 		print_usage
## 		exit 29
## 	fi       
## 
## 	if [ -x ./common/Tools/installpatch.fast ]; then
## 		# common/Tools is the tools directory
## 		cd common/Tools
## 		TOOLDIR=`$PWDCMD`
## 		cd $xdir
## 		return
## 	fi
## 
## 	if [ -x $MU_TOP/common/Tools/installpatch.fast ]; then
## 		# MU_TOP/common/Tools/installpatch.fast is the tools directory
## 		cd $MU_TOP/common/Tools
## 		TOOLDIR=`$PWDCMD`
## 		cd $xdir
## 		return
## 	fi
## 
## 	if [ -x $CDROMLOC/$MU_TOP/common/Tools/installpatch.fast ]; then
## 		# /cdrom/cdrom0/MU_TOP/common/Tools is the tools directory
## 		cd $CDROMLOC/$MU_TOP/common/Tools
## 		TOOLDIR=`$PWDCMD`
## 		cd $xdir
## 		return
## 	fi
## 
## 	# Cannot find tools directory. Give up!
## 
## 	cd $xdir
## 
## 	$GETTEXT "backout_mu cannot locate tools directory.\n" | tee -a $LOGFILE
## 	$GETTEXT "Paths searched:" | tee -a $LOGFILE
## 	$GETTEXT " ./common/Tools, $MU_TOP/common/Tools, $CDROMLOC/$MU_TOP/common/Tools\n" | tee -a $LOGFILE
## 	if [ "$uTOOLDIR" != "" ]; then
## 		$GETTEXT " ./$uTOOLDIR, and ./$uTOOLDIR/$MU_TOP/common/Tools\n"    | tee -a $LOGFILE
## 	fi
## 	print_usage
## 	exit 30
## }

####################  validate_uid  ####################  

# Description:  
#	Make sure effective UID is '0'
# Parameters:   
#	None.
# Globals set:  
#	None.
# Globals used:  
#	None.
 
function validate_uid { 
        typeset -i uid 
        uid=$(id | $SED 's/uid=\([0-9]*\)(.*/\1/') 
        if (( uid != 0 )) 
        then 
                $GETTEXT "You must be root to execute this script.\n" 
                do_file_cleanup 
                exit 16 
        fi 
} 

#################### handle_trap ####################

# Description:
#	Handle keyboard interrupt.
# Parameters:
#	$1	signal
# Globals set:
#	None.
# Globals used:
#	LOGFILE

function handle_trap {
        $GETTEXT "backout_mu: signal detected ($1).\n" | tee -a ${LOGFILE}
        $GETTEXT "backout_mu is terminating.\n" | tee -a ${LOGFILE}
        do_file_cleanup
        exit 1
}

#################### activate_do_busy ####################

# Description: 
#	Start the do_busy dots
# Parameters:  
#	None.
# Globals set: 
#	BACKOUT_MU_PID
# Globals used: 
#	disable_do_busy

function activate_do_busy {
        if [ "$disable_do_busy" = "no" ]; then
                if [ $BACKOUT_MU_PID -eq 0 ]; then
                        (while :; do
                                $ECHO ".\c"
                                sleep 5
                        done)&
                        BACKOUT_MU_PID=$!
                fi
        fi
}

#################### stop_do_busy ####################

# Description: 
#	Stop the do_busy dots
# Parameters: 
#	None.
# Globals set: 
#	BACKOUT_MU_PID
# Globals used: 
#	disable_do_busy

function stop_do_busy {
        if [ "$disable_do_busy" = "no" ]; then
		if [ $BACKOUT_MU_PID -ne 0 ]; then
                        kill $BACKOUT_MU_PID >/dev/null 2>&1
                        kill $BACKOUT_MU_PID >/dev/null 2>&1
                fi
        fi
        BACKOUT_MU_PID=0
        $GETTEXT "\n" | tee -a ${LOGFILE}
}

####################  get_OS_version  ####################
 
# Description:
#       Get the product version <name>_<version> of local Solaris installation
# Parameters:
#       $1      target host softinfo directory path
#       $2      managing host softinfo directory path
#       $3      root of the target host
# Globals Set:
#       Product, TrgOSVers, prodver, Mgrprodver
# Globals used:
#       SED
 
 
function get_OS_version
{
        if [ "$2" != "none" ];
        then
                MgrProduct=`$SED -n 's/^OS=\(.*\)/\1/p' $2`
                MgrOSVers=`$SED -n 's/^VERSION=\(.*\)/\1/p' $2`
                Mgrprodver=$MgrProduct"_"$MgrOSVers
        else
                if [ "${LOGFILE}" != "" ]; then
                        $GETTEXT "backout_mu is unable to find the INST_RELEASE file for the" | tee -a ${LOGFILE}
                        $GETTEXT "target filesystem.\nThis file must be present for backout_mu" | tee -a ${LOGFILE}
                        $GETTEXT "to function correctly.\n" | tee -a ${LOGFILE}
                else
                        $GETTEXT "backout_mu is unable to find the INST_RELEASE file for the"
                        $GETTEXT "target filesystem.\nThis file must be present for backout_mu"
                        $GETTEXT "to function correctly.\n"
                fi
                do_file_cleanup
                exit 2
        fi

        if [ $3 = "/" ];        # If there's not a client
        then
                Product=$MgrProduct
                TrgOSVers=$MgrOSVers
                prodver=$Mgrprodver

        # OK, there is a client
        elif [ "$1" = "none" ]; # but no softinfo file
        then
                if [ "$LOGFILE" != "" ]; then
                        $GETTEXT "backout_mu is unable to find the INST_RELEASE file for the" | tee -a $LOGFILE
                        $GETTEXT "target filesystem.\nThis file must be present for backout_mu" | tee -a $LOGFILE
                        $GETTEXT "to function correctly.\n" | tee -a $LOGFILE
                else
                        $GETTEXT "backout_mu is unable to find the INST_RELEASE file for the"
                        $GETTEXT "target filesystem.\nThis file must be present for backout_mu"
                        $GETTEXT "to function correctly.\n"
                fi
                do_file_cleanup
                exit 2
        else
                Product=`$SED -n 's/^OS=\(.*\)/\1/p' $1`
                TrgOSVers=`$SED -n 's/^VERSION=\(.*\)/\1/p' $1`
                prodver=$Product"_"$TrgOSVers
        fi
}

#################### display_patches_succeeded_failed ####################

# Description: 
#	Display lists of the patches that backed out successfully and of those that didn't.
# Parameters:  
#	None.
# Globals set: 
#	None.
# Globals used: 
#	succeeded_list, failed_list, LOGFILE, PR, PatchRemoveError

function display_patches_succeeded_failed {

# List the patches that succeeded and failed installation (if any).
#

        $GETTEXT "\nThe following patches backed out successfully:\n" | tee -a ${LOGFILE}
        if [ " ${succeeded_list}" != " " ]; then
                $ECHO ${succeeded_list} | tr ' ' '\012' | $PR -5 -t -o 1 | tee -a ${LOGFILE}
        else
                        $GETTEXT " None.\n" | tee -a $LOGFILE
        fi
        $GETTEXT "\n" | tee -a ${LOGFILE}
 
        $GETTEXT "The following patches were not backed out:\n" | tee -a ${LOGFILE}
        if [ " ${failed_list}" != " " ]; then
                $ECHO ${failed_list} | tr ':' '\012' | while read patch_id failure_code; do
                        $ECHO " $patch_id   ${PatchRemoveError[$failure_code]}" | tee -a $LOGFILE
                done
 
        else
                $GETTEXT " None.\n" | tee -a $LOGFILE
        fi
        $GETTEXT "\n" | tee -a ${LOGFILE}
}

#################### find_softinfos ####################

# Description:
#       Find the appropriate softinfo files for the manager and the target.
# Parameters:
#       $1      ROOT of target filesystem
# Globals set:
#       TRGSOFTINFO
#       MGRSOFTINFO
# Globals used:
#       NEW_SOFTINFO

function find_softinfos
{
        if [ -f $NEW_SOFTINFO ]; then
                MGRSOFTINFO=$NEW_SOFTINFO
        fi
        if [ "$1" = "/" ]; then
                TRGSOFTINFO=$MGRSOFTINFO
        elif [ "$1" = "" ]; then
                TRGSOFTINFO=$MGRSOFTINFO
        elif [ -f $1$NEW_SOFTINFO ]; then
                TRGSOFTINFO=$1$NEW_SOFTINFO
        fi
}

####################  do_file_cleanup ####################

# Description: 
#	Stop do_busy dots, remove temp files and dirs, show patches installed and 
# 	not installed, rename and relink log file.
# Parameters:  
#	None.
# Globals set: 
#	None.
# Globals used: 
#	disable_do_busy, CLEAN_UP_FILES, CLEAN_UP_DIRS, RM
 
function do_file_cleanup {
	stop_do_busy
 
        if [ "$CLEAN_UP_FILES" != "" ]; then
                $ECHO $CLEAN_UP_FILES | $XARGS  $RM -f
                $ECHO "" >/dev/null
        fi
        if [ "$CLEAN_UP_DIRS" != "" ]; then
                $ECHO $CLEAN_UP_DIRS | $XARGS $RM -rf
                $ECHO "" >/dev/null
        fi
 
        display_patches_succeeded_failed
 
        move_log_files

	update_release_file
}

####################  move_log_files ####################	

# Description: 
#	Rename LOGFILE to have a date and time stamp on it.
# 	Link LOGFILE to this newly renamed file.
# Parameters:  
#	None.
# Globals set: 
#	LOGFILE, MU_VERSION_NAME, DATECMD, LN, MV, logname
# Globals used: 
#	None.
 
function move_log_files {
        $GETTEXT "backout_mu completed at `$DATECMD`.\n" | tee -a ${LOGFILE}
        if [ "$LOGFILE" != "" ]; then
                # ISDATETIME=`$DATECMD +%m%d%y%H%M`
		# Make Year 2000 friendly and sortable
                ISDATETIME=$($DATECMD +%Y%m%d%H%M%S)

                $MV $LOGFILE $LOGFILE.$MU_VERSION_NAME.$ISDATETIME
                $LN -s ./$logname.$MU_VERSION_NAME.$ISDATETIME $LOGFILE
        fi

        # If at least one patch was removed remind the user to reboot his system and clean
	# up state file.

        if [ " ${succeeded_list}" != " " ]; then
		# At least one patch was backed out.
		#
		# We need to remove the state file if all patches listed
		# were actually backed out, else rewrite state file to contain
		# the list of patches that weren't backed out

		typeset -i pcount
		pcount=$($GREP -v "^#" $STATE_FILE | $WC -l | $AWK '{print $1}')

		# Save the Maintenance Update string from the state file for
		# update_release_file to use since we may be destroying the
		# state file soon.

		MU_INFO=$($GREP "Maintenance" $STATE_FILE | $HEAD -1 | $SED 's/#//')

		if [ "$success_count" -eq "$pcount" ]; then 
			# all patches were actually backed out
			$GETTEXT "All patches removed. Removing MU state file: `$ECHO $STATE_FILE | $COMPACT_SLASHES` \n\n" \
				| tee -a ${LOGFILE}
			$RM $STATE_FILE	
		else
			# need to rewrite state file; not all patches backed out.
			$ECHO ${succeeded_list} | tr -s " " "\n" > $TMP_LIST
			$GREP "^#" $STATE_FILE > $TMP_STATE_FILE
			while read p; do
       				p=$($ECHO ${p%%#*})
			        if [ "$p" != "" ]; then
					$GREP "$p" $TMP_LIST > /dev/null
					if [ $? -ne 0 ]; then
						# patch not backed out, add to new state file
						$ECHO $p >> $TMP_STATE_FILE	
					fi
				fi
			done < $STATE_FILE
			$GETTEXT "Not all patches in state file were removed.\n" | tee -a ${LOGFILE}
			$GETTEXT "Rewriting state file: $STATE_FILE\n" | tee -a ${LOGFILE}

			$MV $TMP_STATE_FILE $STATE_FILE
		fi
                $GETTEXT "\n-=- Please REBOOT your system -=-\n\n"
        fi

}

####################  eval_utilities ####################  

# Description: 
#	Make sure all required commands are available.
# Parameters:
#	None.
# Globals used: 
#	REQD_CMDS, LOGFILE
# Globals set: 
#	None.

function eval_utilities {
        for xcommand in $REQD_CMDS; do
                if [ ! -x $xcommand ]; then
                        $GETTEXT "ERROR: Cannot find $xcommand which is required for proper" \
                        | tee -a $LOGFILE
                        $GETTEXT "execution of backout_mu, or $xcommand is not executable.\n" \
			| tee -a $LOGFILE
                        do_file_cleanup
                        exit 3;
                fi
        done
}

####################  print_usage ####################  

# Description:
#      Print usage message.
# Parameters:
#      None. 
# Globals set:
#      None. 
# Globals used:
#      None. 

function print_usage {

$GETTEXT "\n\
usage: `basename $0` [-q] [-B backoutdir] [-R rootdir | -S service_area] \n
##    [-T patchir]    - specifies location of tools directory\n\
    [-q]            - quiet mode, no do_busy dots\n\
    [-B backoutdir] - specifies directory patches were saved in\n\
    [-R rootdir]    - root directory, prepended to all absolute\n\
                      path names of the backout root (Note: cannot\n\
                      use -S also)\n\
    [-S servicedir] - specifies alternate service directory (Note: cannot\n\
                      use -R also)\n\n"
}

####################  parse_args ####################  

# Description: 
#	Parse command line arguments.
# Parameters: 
#	$*       all options and arguments on command line
# Globals set: 
#	uTOOLDIR, disable_do_busy, BACKOUTDIR, BACKOUTARG, ROOTARG, logname, LOGFILE
# Globals used: 
#	CAT

function parse_args {

#
# Command invocation will be recorded in $LOGFILE.
#
command="$0 $*"
root_specified="no"
service_specified="no"
backoutdir_specified="no"
disable_do_busy="no"

while [ "$1" != "" ]
do
	case $1 in
## 	-T)	shift
## 		# There are currently no tools being distributed in the 
## 		# Tools directory. This portion of the code is being 
## 		# retained in the event this chages in the future.
## 		uTOOLDIR=$1  # the user-supplied tool dir path
## 		if [ ! -d $uTOOLDIR ]; then
## 			$GETTEXT "The -T parameter must be a directory.\n"
## 			$GETTEXT "$uTOOLDIR is not a directory.\n"
## 			print_usage 
## 			exit 28;
## 		fi
## 		shift;;
	-q)	disable_do_busy="yes"; shift;;
	-B)	shift; backoutdir_specified="yes";
		BACKOUTDIR=$1
		if [ ! -d $BACKOUTDIR ]; then
			$GETTEXT "The -B parameter must be a directory.\n"
			$GETTEXT "$BACKOUTDIR is not a directory.\n"
			print_usage
			exit 6
		fi
		BACKOUTARG="-B $BACKOUTDIR"
		shift;;

	-R)	shift
		if [ "$service_specified" = "yes" ]; then
                        $GETTEXT "The -S and -R arguments are mutually exclusive.\n"
                        print_usage
                        exit 21
                fi
		ROOTDIR=$1
		if [ ! -d $ROOTDIR ]; then
			$GETTEXT "The -R parameter must be a directory\n"
			$GETTEXT "$ROOTDIR is not a directory"
			print_usage
			exit 8
		fi
		root_specified="yes"
		ROOTARG="-R $ROOTDIR"
		shift;;

	-S)	shift
		if [ "$root_specified" = "yes" ]; then
                        $GETTEXT "The -S and -R arguments are mutually exclusive.\n"
                        print_usage
                        exit 21
                fi
                if [ "$1" != "Solaris_${MU_OS}" ]; then
                        $GETTEXT "The service area must be Solaris_${MU_OS}.\n"
                        print_usage
                        exit 20
                fi
                if [ ! -d /export/$1 ]; then
                        $GETTEXT "The -S parameter must be a directory.\n"
                        $GETTEXT "/export/$1 is not a directory.\n"
                        print_usage
                        exit 9
                fi
                find_softinfos /export/$1
                get_OS_version "$TRGSOFTINFO" "$MGRSOFTINFO" "$1"
                if [ "$1" != "$Mgrprodver" ]; then
                        if [ -d /export/$1/var/sadm/pkg ]; then
                                 ROOTDIR=/export/$1
                                 SERVICE_AREA=$1
                        else
                                 $GETTEXT "The $1 service cannot be found on this system.\n"
                                 print_usage
                                 exit 26
                        fi
                else
                        SERVICE_AREA=$1
                fi
                service_specified="yes"
		ROOTARG="-S $SERVICE_AREA"
                shift;;

	-*)	$GETTEXT "Invalid option.\n"; print_usage; exit 10;;
	*)	$GETTEXT "Invalid option.\n"; print_usage; exit 10;;
	esac
done

# if there are more parameters, usage is wrong.
if [ $# -gt 0 ]; then
	print_usage
	exit 10
fi
logname=`$ECHO ${MS_NAME} | $SED 's/ /_/g'`_log
LOGFILE=`$ECHO $ROOTDIR/var/sadm/install_data/${logname} | $COMPACT_SLASHES`
if [ -h $LOGFILE ] ; then
        $RM $LOGFILE
fi
$CAT /dev/null >$LOGFILE
if [ $? != 0 ]; then
        $GETTEXT "Can't write to Log File: $LOGFILE" 
        print_usage
        exit 11 
fi
}

####################  PrintCenter  ####################
# Description:
#       Prints contents of stdin centered in 80 columns
# Parameters:
#       None. (reads stdin)
# Globals Used:
#       None. 
# Globals Set:
#       None.

function PrintCenter {
        $AWK '{
                n = length($0)
                fmt = "%" 40-(n/2) "s%s\n"
                printf(fmt, " ", $0)
        }'
}

####################  update_release_file  ####################  

# Description:
#	Updates contents of $ROOTDIR/etc/release to remove any
#	lines containing "Solaris 2.7 Maintenance Update"
# Parameters:
#	None.
# Globals Used:
#	ROOTDIR
# Globals Set:
#	None.

function update_release_file {
	# Don't update release file if backout_mu was interrupted by user.
	if [ "$UserInterrupted" = "yes" ]; then
		return
	fi

        # Don't update release file if we've backed out from a Service area
        if [ "$service_specified" = "yes" ]; then
                return
        fi

        # Also, don't update release file if no patches were actually installed.
        if [ " ${succeeded_list}" = " " ]; then
                return
        fi

	if [ ! -f $ROOTDIR/$RELEASE_FILE ]; then
		$GETTEXT "\nRelease file, $ROOTDIR/$RELEASE_FILE, not found.\n" | tee -a ${LOGFILE}
		return
	fi

	$HEAD -3 $ROOTDIR/$RELEASE_FILE > $ROOTDIR/$RELEASE_FILE.tmp$$
	$MV $ROOTDIR/$RELEASE_FILE.tmp$$ $ROOTDIR/$RELEASE_FILE

	# Then add a line containing the old (saved) MU info from the state file.
	$ECHO $MU_INFO | PrintCenter >> $ROOTDIR/$RELEASE_FILE

        $GETTEXT "\nUpdated `$ECHO $ROOTDIR/$RELEASE_FILE | $COMPACT_SLASHES` successfully.\n" | tee -a $LOGFILE
}

####################  Main program ####################  

# Make sure the required commands are present.
eval_utilities

# Make sure effective UID is '0'
validate_uid

# Parse command-line arguments.
ARGS=$*
parse_args $*

# Identify softinfo files for Manager and Target systems
find_softinfos $ROOTDIR

# Get the OS Version
get_OS_version $TRGSOFTINFO $MGRSOFTINFO $ROOTDIR

if [ "$MgrOSVers" != "$MU_OS" ]; then
        $GETTEXT "System running backout_mu must be Solaris $MU_OS.\n" | tee -a $LOGFILE
        $GETTEXT "System is running Solaris $MgrOSVers.\n" | tee -a $LOGFILE
        exit 17
fi

if [ "$TrgOSVers" != "$MU_OS" ]; then
        $GETTEXT "backout_mu can only patch version $MU_OS systems.\n" | tee -a $LOGFILE
        $GETTEXT "Target system is version $TrgOSVers.\n" | tee -a $LOGFILE
        exit 17
fi

## # The need for set_tooldir() to check for the presence of the TOOLDIR,
## # installpatch.fast and backoutpatch.fast no longer exists. The routine
## # has not been removed from the code however as a future need for the
## # TOOLDIR may arise and I'd rather not have to reconstruct the code.
##
## # Set TOOLDIR to absolute path to patch tools
## 	set_tooldir
## 
## if [ ! -d $TOOLDIR ]; then
## 	$GETTEXT "Directory with patch tools, $TOOLDIR, not found.\n"
## 	print_usage
## 	exit 18
## fi
## 
## if [ ! -x $TOOLDIR/installpatch.fast -o ! -r $TOOLDIR/installpatch.fast ]; then
## 	$GETTEXT "$TOOLDIR/installpatch.fast does not exist or is not executable.\n"
## 	print_usage
## 	exit 19
## fi
## 
## if [ ! -x $TOOLDIR/backoutpatch.fast -o ! -r $TOOLDIR/backoutpatch.fast ]; then
## 	$GETTEXT "$TOOLDIR/backoutpatch.fast does not exist or is not executable.\n"
## 	print_usage
## 	exit 19
## fi

# Set up trap handler
trap 'handle_trap 1' 1
trap 'handle_trap 2' 2
trap 'handle_trap 3' 3
trap 'handle_trap 15' 15

# Broadcast what we are doing
#
$GETTEXT "\nBacking out Maintenance Update ${RELEASE} (using backout_mu $VERSION) \n   `date`\n" | tee -a ${LOGFILE}
$GETTEXT "\nLogging output to log file: $LOGFILE\n" | tee -a ${LOGFILE}
## $GETTEXT "\nUsing $TOOLDIR as tools directory.\n\n" | tee -a ${LOGFILE}
if [ "$ARGS" = "" ]; then
        ARGS="None."
fi
$GETTEXT "\nArguments to backout_mu:   $ARGS\n" | tee -a ${LOGFILE}
$GETTEXT "$ROOTARG $BACKOUTARG <patch_id>\n\n" | sed 's/[        ][      ]*/ /g' | tee -a ${LOGFILE}

if [ "$ROOTDIR" != "/" ]; then
        $GETTEXT "\nNote: You are removing the Maintenance Update from a client.\n" | tee -a $LOGFILE
        $GETTEXT "You will need to invoke backout_mu a second time with the\n" | tee -a $LOGFILE
        $GETTEXT "-S option to remove the patch set from the client's Service area.\n" | tee -a $LOGFILE
        $GETTEXT "Failure to reinvoke install_mu will result in an improperly\n" | tee -a $LOGFILE
        $GETTEXT "patched and unstable client.\n\n" | tee -a $LOGFILE
fi
if [ "$service_specified" = "yes" ]; then
        $GETTEXT "\nNote: You are removing the Maintenance Update from a Service area.\n" | tee -a $LOGFILE
        $GETTEXT "You will need to invoke backout_mu again with the -R option\n" | tee -a $LOGFILE
        $GETTEXT "for each client using this Service area. Failure to do so\n" | tee -a $LOGFILE
        $GETTEXT "will leave you with an improperly patched and unstable client.\n" | tee -a $LOGFILE
	$GETTEXT "If the Service area is shared by the server then you need to\n" | tee -a $LOGFILE 
        $GETTEXT "remove the same patch set from your server's root area as well.\n\n" | tee -a $LOGFILE
fi

$GETTEXT "Please REBOOT your system after patch set has been backed out.\n\n"

typeset -i count=0
typeset -i num_patches

# Generate list of installed patches so we can compare patches about to be backed
# out to installed patches to determine if we should bother trying to back them out
# or not.

grep PATCHLIST $ROOTDIR/var/sadm/pkg/*/pkginfo | awk -F= '{print $2}' | tr -s " " "\n" | sort -u \
	> $PATCHLIST
 
## # This cd is done to maintain the cwd to TOOLDIR to suppress auto unmounts
## cd $TOOLDIR

if [ "$service_specified" = "yes" ]; then
	root_or_usr="usr"
else
	root_or_usr="root"
fi

if [ -r $ROOTDIR/var/sadm/install_data/.mu_state.${root_or_usr}.* ]; then
	STATE_FILE=$($LS $ROOTDIR/var/sadm/install_data/.mu_state.${root_or_usr}.* | tail -1)
	$GETTEXT "Backing out patches listed in MU state file: `$ECHO $STATE_FILE | $COMPACT_SLASHES` \n\n" | tee -a ${LOGFILE}
else
	$GETTEXT "Cannot find state file. Looked for a file of the form:\n" | tee -a ${LOGFILE}
	$GETTEXT "  `$ECHO $ROOTDIR/var/sadm/install_data/.mu_state | $COMPACT_SLASHES`.{$root_or_usr}.*\n" | tee -a ${LOGFILE}
	$GETTEXT "Don't know what patches to back out.\n" | tee -a ${LOGFILE}
	do_file_cleanup
	exit 27
fi

num_patches=$($GREP -v "^#" $STATE_FILE | $WC -l | awk '{print $1}')

# backout patches in the reverse order in which they were applied

$CAT -n $STATE_FILE | sort -rn | $SED 's/^[ ]*[0-9]*[ 	]//' |
while read patchnum
do
	# strip off comment in line
	patchnum=$(echo ${patchnum%%#*}) 

	if [ "$patchnum" != "" ]; then
		count=count+1
 		print -n "Backing out ${patchnum} ($count of $num_patches) " | tee -a $LOGFILE
		grep $patchnum $PATCHLIST > /dev/null 2>&1
		if [ $? != 0 ]; then
			$GETTEXT "  \n  Patch ${patchnum} is not applied. Skipping.\n" | tee -a ${LOGFILE}
			failed_list="${failed_list} ${patchnum} 2 : "
		else
			activate_do_busy
			$PATCHRM $ROOTARG $BACKOUTARG $patchnum >>$LOGFILE 2>&1
			result=$?
			stop_do_busy
			if [ $result != 0 ]; then
				$ECHO "  Backout of ${patchnum} failed:\n  ${PatchRemoveError[$result]}\n"
				failed_list="${failed_list} ${patchnum} ${result} : "
			else
				succeeded_list="${succeeded_list} ${patchnum}"
				success_count=success_count+1
			fi
		fi
	fi
done

UserInterrupted="no"

# List the patches that succeeded and failed installation (if any).
# Clean up log file and we're done!
do_file_cleanup

exit 0
