#!/bin/ksh
#
# @(#) install_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.

# install_mu for 2.8. Installs a set of patches quickly.
#
# Exit Codes:
#	0	No error
#	1	User interrupt
#	2	Cannot find INST_RELEASE file
#	3	Required utility not found
#	4	-B and -d options can't be used together
#	5	-p (patch directory) option is not a directory
#	6	-B (backout directory) option is not a directory
#	7	Permissions on -B (backoutdir) option too restrictive
#	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)
#	14	Cannot find .order file in directory specified by -p
#	15	Searched for but could not find .order file
#	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
#	22	Not enough disk space to apply patch set
#	23	Not enough disk space to save backout data
#	24	pkgadd dryrun failure
#	25	-f and -D options are mutually exclusive
#	26	Service area doesn't have a valid /var/sadm/pkg directory

# The current build of MU
MU_BUILD="07"

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

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

# The template file from which /etc/release is built
RELEASE_INFO_TEMPLATE="/var/sadm/install_data/.solaris_relinfo"

# One of these lines gets appended to release file after install_mu runs

MU_RELEASE_LINE='Solaris 8 Maintenance Update 3 applied'

# install_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="Solaris_8MU3"

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

# array of patchadd error codes

set -A PatchAddError
 
PatchAddError[1]="Usage error"
PatchAddError[2]="Attempt to apply a patch that's already been applied"
PatchAddError[3]="Effective UID is not root"
PatchAddError[4]="Attempt to save original files failed"
PatchAddError[5]="pkgadd failed"
PatchAddError[6]="Patch is obsoleted"
PatchAddError[7]="Invalid package directory"
PatchAddError[8]="Attempting to patch a package that is not installed"
PatchAddError[9]="Cannot access /usr/sbin/pkgadd (client problem)"
PatchAddError[10]="Package validation errors"
PatchAddError[11]="Error adding patch to root template"
PatchAddError[12]="Patch script terminated due to signal"
PatchAddError[13]="Symbolic link included in patch"
PatchAddError[14]="NOT USED"
PatchAddError[15]="The prepatch script had a return code other than 0."
PatchAddError[16]="The postpatch script had a return code other than 0."
PatchAddError[17]="Mismatch of the -d option between a previous patch install and the current one."
PatchAddError[18]="Not enough space in the file systems that are targets of the patch."
PatchAddError[19]="$SOFTINFO/INST_RELEASE file not found"
PatchAddError[20]="A direct instance patch was required but not found"
PatchAddError[21]="The required patches have not been installed on the manager"
PatchAddError[22]="A progressive instance patch was required but not found"
PatchAddError[23]="A restricted patch is already applied to the package"
PatchAddError[24]="An incompatible patch is applied"
PatchAddError[25]="A required patch is not applied"
PatchAddError[26]="The user specified backout data can't be found"
PatchAddError[27]="The relative directory supplied can't be found"
PatchAddError[28]="A pkginfo file is corrupt or missing"
PatchAddError[29]="Bad patch ID format"
PatchAddError[30]="Dryrun failure(s)"
PatchAddError[31]="Path given for -C option is invalid"
PatchAddError[32]="Must be running Solaris 2.6 or greater"
PatchAddError[33]="Bad formatted patch file or patch file not found"

ARCH=""
LPROC=""

NEW_SOFTINFO="/var/sadm/system/admin/INST_RELEASE"
MGRSOFTINFO="none"
TRGSOFTINFO="none"

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

TMP_LIST="/tmp/plist.$$"

# Used in command line processing
xsaveold=" "
saveold="yes"
PKGDBARG=" "
BACKOUTARG=""
PATCHDIR=""
uPATCHDIR=""
TOP_DIR=""
SKIP_DRYRUN="false"
DRYRUN_ONLY="false"

# Holds list of patches successfully and not-successfully installed
succeeded_list=""
failed_list=""

## TOOLSDIR=""

ADMINFILE=/tmp/admin.$$
PKGADDLOGFILE=/tmp/pkgaddlog.$$
DRYRUN_TMP_DIR=/tmp/dryrun.$$
DRYRUN_JUNK_DIR=/tmp/dryrunjunk.$$
PKGLISTFILE=/tmp/pkglist.$$
DRYRUN_REPORT=/tmp/dryrun_report.$$

MS_NAME="Maintenance Update"
RELEASE=
LOGFILE=
logname=

ROOTDIR="/"
SERVICE_AREA=""

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

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

		CDROMLOC="/cdrom/s8_maintenance_update_3_x86/Solaris_8_MU3"
	
	fi

# Needed utilities

AWK=/usr/bin/awk
BASENAME=/usr/bin/basename
CAT=/usr/bin/cat
CHGRP=/usr/bin/chgrp
CHMOD=/usr/bin/chmod
CHOWN=/usr/bin/chown
CP=/usr/bin/cp
DATECMD=/usr/bin/date
DF=/usr/bin/df
ECHO=/usr/bin/echo
EGREP=/usr/bin/egrep
EXPR=/usr/bin/expr
FIND=/usr/bin/find
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
PATCHADD=/usr/sbin/patchadd
PKGADD=/usr/sbin/pkgadd
PR=/usr/bin/pr
PWDCMD=/usr/bin/pwd
RM=/usr/bin/rm
SED=/usr/bin/sed
TAIL=/usr/bin/tail
VALPATH=/usr/sadm/bin/valpath
WC=/usr/bin/wc
XARGS=/usr/bin/xargs


REQD_CMDS="$AWK $BASENAME $CAT $CHGRP $CHMOD $CHOWN $CP $DATECMD $DF $ECHO $EGREP $EXPR $FIND $GETTEXT $GREP \
           $HEAD $LN $LS $MKDIR $MV $NAWK $PATCHADD $PKGADD $PR $PWDCMD $RM $SED $TAIL $VALPATH $WC $XARGS"

disable_do_busy="no"
backoutdir_specified="no"

INSTALL_MU_PID=0
umask 022

CLEAN_UP_FILES="$ADMINFILE $PKGADDLOGFILE $PKGLISTFILE $DRYRUN_REPORT $TMP_LIST"
CLEAN_UP_DIRS="$DRYRUN_TMP_DIR $DRYRUN_JUNK_DIR"

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

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

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

function handle_trap { 
        $GETTEXT "\ninstall_mu: signal detected ($1).\n" | tee -a ${LOGFILE}
        $GETTEXT "install_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: 
#       None. 
# Globals used:
#    	disable_do_busy, INSTALL_MU_PID
 
function activate_do_busy {
	if [ "$disable_do_busy" = "no" ]; then
		if [ $INSTALL_MU_PID -eq 0 ]; then
			(while :; do
				$ECHO ".\c"
				sleep 5
			done)&
			INSTALL_MU_PID=$!
		fi
        fi
}

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

# Description: 
#      Stop the do_busy dots
# Parameters: 
#      None. 
# Globals set: 
#      None. 
# Globals used:
#      disable_do_busy, INSTALL_MU_PID
 
function stop_do_busy {
        if [ "$disable_do_busy" = "no" ]; then
		if [ $INSTALL_MU_PID -ne 0 ]; then
			kill $INSTALL_MU_PID >/dev/null 2>&1
			kill $INSTALL_MU_PID >/dev/null 2>&1
		fi
        fi
	INSTALL_MU_PID=0
	$ECHO | tee -a ${LOGFILE}
}

####################  handle_mu_applied  #################### 
 
# Description
#       Put in .mu_applied file, or update it.
# Parameters
#	None.
# Globals set:
#	None.
# Globals used:
#	ROOTDIR, CHMOD, CHOWN, CHGRP, CAT, NAWK, MU_VERSION_NAME

function handle_mu_applied
{
	MU_APPLIED="$ROOTDIR/var/sadm/patch/.mu_applied"
        if [ ! -f $MU_APPLIED ]; then
                $ECHO "VERSION=$MU_VERSION_NAME" >$MU_APPLIED
                $CHMOD 644 $MU_APPLIED
                $CHOWN root $MU_APPLIED
                $CHGRP bin $MU_APPLIED
        else
                mu_version_found=no
                $CAT $MU_APPLIED | $NAWK -F= ' { print $2 } ' |
                while read mu_version_name
                do 
                        if [ "$mu_version_name" = "$MU_VERSION_NAME" ]; then
                                mu_version_found=yes
                                break;
                        fi
                done   
                if [ "$mu_version_found" = "no" ]; then
                        $ECHO "VERSION=$MU_VERSION_NAME" >>$MU_APPLIED
                fi
        fi
}

####################  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, MgrOSVers, 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 "install_mu is unable to find the INST_RELEASE file for the" | tee -a ${LOGFILE}
			$GETTEXT "target filesystem.\nThis file must be present for install_mu" | tee -a ${LOGFILE}
			$GETTEXT "to function correctly.\n" | tee -a ${LOGFILE}
		else
			$GETTEXT "install_mu is unable to find the INST_RELEASE file for the"
			$GETTEXT "target filesystem.\nThis file must be present for install_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 "install_mu is unable to find the INST_RELEASE file for the" | tee -a ${LOGFILE}
			$GETTEXT "target filesystem.\nThis file must be present for install_mu" | tee -a ${LOGFILE}
                        $GETTEXT "to function correctly.\n" | tee -a ${LOGFILE}
		else
			$GETTEXT "install_mu is unable to find the INST_RELEASE file for the" 
                        $GETTEXT "target filesystem.\nThis file must be present for install_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 installed successfully and of those that didn't. 
# Parameters: 
#      None. 
# Globals set: 
#      None. 
# Globals used:
#      succeeded_list, failed_list, LOGFILE, PR, PatchAddError

function display_patches_succeeded_failed { 
# List the patches that succeeded and failed installation (if any).
#
	set -A PatchAddError

	PatchAddError[1]="Usage error"
	PatchAddError[2]="Attempt to apply a patch that's already been applied"
	PatchAddError[3]="Effective UID is not root"
	PatchAddError[4]="Attempt to save original files failed"
	PatchAddError[5]="pkgadd failed"
	PatchAddError[6]="Patch is obsoleted"
	PatchAddError[7]="Invalid package directory"
	PatchAddError[8]="Attempting to patch a package that is not installed"
	PatchAddError[9]="Cannot access /usr/sbin/pkgadd (client problem)"
	PatchAddError[10]="Package validation errors"
	PatchAddError[11]="Error adding patch to root template"
	PatchAddError[12]="Patch script terminated due to signal"
	PatchAddError[13]="Symbolic link included in patch"
	PatchAddError[14]="NOT USED"
	PatchAddError[15]="The prepatch script had a return code other than 0."
	PatchAddError[16]="The postpatch script had a return code other than 0."
	PatchAddError[17]="Mismatch of the -d option between a previous patch install and the current one."
	PatchAddError[18]="Not enough space in the file systems that are targets of the patch."
	PatchAddError[19]="$SOFTINFO/INST_RELEASE file not found"
	PatchAddError[20]="A direct instance patch was required but not found"
	PatchAddError[21]="The required patches have not been installed on the manager"
	PatchAddError[22]="A progressive instance patch was required but not found"
	PatchAddError[23]="A restricted patch is already applied to the package"
	PatchAddError[24]="An incompatible patch is applied"
	PatchAddError[25]="A required patch is not applied"
	PatchAddError[26]="The user specified backout data can't be found"
	PatchAddError[27]="The relative directory supplied can't be found"
	PatchAddError[28]="A pkginfo file is corrupt or missing"
	PatchAddError[29]="Bad patch ID format"
	PatchAddError[30]="Dryrun failure(s)"
	PatchAddError[31]="Path given for -C option is invalid"
	PatchAddError[32]="Must be running Solaris 2.6 or greater"
	PatchAddError[33]="Bad formatted patch file or patch file not found"
	PatchAddError[34]="The appropriate kernel jumbo patch needs to be installed"

        $GETTEXT "The following patches installed 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 installed:\n" | tee -a ${LOGFILE}
	if [ " ${failed_list}" != " " ]; then
                $ECHO ${failed_list} | tr ':' '\012' | while read patch_id failure_code; do
			$ECHO " $patch_id   ${PatchAddError[$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_DIRS" != "" ]; then
                 $ECHO $CLEAN_UP_DIRS | $XARGS $RM -rf
                 $ECHO "" >/dev/null
        fi

        if [ "$CLEAN_UP_FILES" != "" ]; then
                 $ECHO $CLEAN_UP_FILES | $XARGS  $RM -f
                 $ECHO "" >/dev/null
        fi

	if [ "$DRYRUN_ONLY" = "false" ]; then
		display_patches_succeeded_failed
	fi

	move_log_files
	
	if [ "$DRYRUN_ONLY" = "false" ]; then
		update_release_file
	fi
}

####################  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 Used:
#       LOGFILE, MU_VERSION_NAME, DATECMD, LN, MV, logname, STATE_FILE
# Globals Set:
#     

function move_log_files {
        $GETTEXT "install_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 applied remind the user to reboot his system. 
	# Then save a list of patches applied plus MU information from /etc/release
	# into a state file.
 
        if [ " ${succeeded_list}" != " " ]; then
		if [ "$service_specified" = "yes" ]; then
			root_or_usr="usr"
		else
		        root_or_usr="root"
		fi

		# Look for existing state file for this MU
		STATE_FILE=""
		if [ -r $ROOTDIR/var/sadm/install_data/.mu_state.${root_or_usr}.* ]; then
			STATE_FILE=$($GREP -l $MU_VERSION_NAME $ROOTDIR/var/sadm/install_data/.mu_state.${root_or_usr}.* | \
			$TAIL -1 | $AWK '{print $1}')
		fi

		if [ "$STATE_FILE" != "" ]; then 
			# Append new patches that applied to existing state file
			$ECHO ${succeeded_list} | tr -s " " "\n" > $TMP_LIST
			while read p; do
				$GREP "$p" $STATE_FILE > /dev/null
				if [ $? -ne 0 ]; then
					# This patch not in state file. Add it.
					echo "$p" >> $STATE_FILE
				fi
			done < $TMP_LIST
			$GETTEXT "\nUpdated state file `$ECHO $STATE_FILE | $COMPACT_SLASHES`.\n" | tee -a ${LOGFILE}
		else
			# Create and populate a new state file
			STATE_FILE="$ROOTDIR/var/sadm/install_data/.mu_state.${root_or_usr}.$ISDATETIME"
			$ECHO "# $STATE_VERSION $MU_VERSION_NAME $($DATECMD)" > $STATE_FILE
	                MU_INFO=$($GREP "Maintenance" $ROOTDIR/$RELEASE_FILE | $TAIL -1)
	                $ECHO "# $MU_INFO" >> $STATE_FILE
	                $ECHO ${succeeded_list} | tr -s " " "\n" >> $STATE_FILE
			$GETTEXT "\nCreated new state file: `$ECHO $STATE_FILE | $COMPACT_SLASHES`\n"
		fi
		$GETTEXT "\n-=- Please REBOOT your system -=-\n\n"
        fi
}

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

# Description:
#   Make sure all required commands are available.
#
# 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 install_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 {

# The -D option is still available for system test engineers, though the
# space checking feature is done via ksh rather than using the data from
# pkgadd. The -D and -f options are mutually exclusive.
 
$GETTEXT "\n\
usage: `basename $0` [-p patchdir] [-d | -B backoutdir]\n\
                    [-f] [-R rootdir | -S servicedir] [-q]\n\n\
    [-p patchdir]   - specifies directory that contains the patch set\n\
    [-d]            - don't backup patches (Note: cannot use -B also)\n\
    [-B backoutdir] - specifies directory patches will be saved in (Note: cannot\n\
                      use -d also)\n\
    [-f]            - force installation of patch set without checking for\n\
                      sufficient disk space (Note: cannot use -D also)\n\
    [-R rootdir]    - install root directory, prepended to all absolute\n\
                      path names of the install root (Note: cannot use -S also)\n\
    [-S servicedir] - specifies alternate service directory (Note: cannot\n\
                      use -R also)\n\
    [-q]            - quiet mode, no do_busy dots\n\n"
}

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

# Description:
#      Parse command line arguments.
# Parameters:
#      $*       : all options and arguments on command line
# Globals set:
#      xsaveold, saveold, uPATCHDIR, TOP_DIR, disable_do_busy, backoutdir_specified,
#      BACKOUTARG, ROOTDIR, DRYRUN_ONLY, SKIP_DRYRUN
# Globals used:
#      PWDCMD, MKDIR, LOGFILE, PKGDBARG
 
function parse_args {

command="$0 $*"
root_specified="no"
service_specified="no"
backoutdir_specified="no"
PATCH_NO_UNDO="false"

while [ "$1" != "" ]
do
	case $1 in

		-d) 	PATCH_NO_UNDO="true"	
			if [ "$backoutdir_specified" = "yes" ]; then
			          $GETTEXT "The -B and -d arguments are mutually exclusive.\n"   
                                  print_usage
                                  exit 4 
			fi
			xsaveold="-d"
			saveold="no"
			shift;;

		-p) 	shift
			uPATCHDIR=$1  # the user-supplied patch dir path
			if [ ! -d $uPATCHDIR ]; then
				$GETTEXT "The -p parameter must be a directory.\n"
				$GETTEXT "$uPATCHDIR is not a directory.\n"
				print_usage
			        exit 5
		        fi
		        shift;;

		-q) 	disable_do_busy="yes"; shift;;

		-f)	SKIP_DRYRUN="true" 
			if [ "$DRYRUN_ONLY" = "true" ]; then
				print_usage
				exit 25		
			fi
			shift;;

		# The -D option is still available for system test engineers, though the
		# space checking feature is done via ksh rather than using the data from
		# pkgadd. The -D and -f options are mutually exclusive.
 
		-D)	DRYRUN_ONLY="true"
			if [ "$SKIP_DRYRUN" = "true" ]; then
                                print_usage    
                                exit 25          
                        fi
                        shift;;  

		-B) 	shift
			backoutdir_specified="yes"
			if [ "$PATCH_NO_UNDO" = "true" ]; then
		                  $GETTEXT "The -B and -d arguments are mutually exclusive.\n"
				  print_usage
				  exit 4
			fi
		        if [ ! -d $1 ]; then
			          $GETTEXT "The -B parameter must be a directory.\n"
			          $GETTEXT "$1 is not a directory.\n"
			          print_usage
			          exit 6
		        fi
		        bo_cdir=`$PWDCMD`
		        cd $1
		        BACKOUTDIR=`$PWDCMD`
		        $MKDIR -p $BACKOUTDIR/test/file
		        if [ $? != 0 ]; then
			          $GETTEXT "Permissions on backout directory $BACKOUTDIR not adequate.\n"
			          exit 7
       			fi
		        $RM -rf $BACKOUTDIR/test
		        cd $bo_cdir
          		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.\n"
		 	        print_usage
			        exit 8
       			fi
		        root_specified="yes"
		        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"
			shift;;
		-*) $GETTEXT "Invalid option.\n"; print_usage; exit 10;;
		*)  $GETTEXT "Invalid option.\n"; print_usage; exit 10;;
	      esac
done
 
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
	$ECHO "Can't write to Log File: ${LOGFILE}\n"
 	print_usage
	exit 11
fi
$RM ${LOGFILE}

# if there are more parameters, usage is wrong.
if [ $# -gt 0 ]; then
        print_usage
	exit 10
fi
if [ "$ROOTDIR" != "" ]; then
        PKGDBARG="-R $ROOTDIR "
fi

if [ "$SERVICE_AREA" != "" ]; then
	PKGDBARG="-S $SERVICE_AREA "
fi

if [ "$backoutdir_specified" = "yes" ]; then
        BACKOUTARG="-B $BACKOUTDIR"
else
	BACKOUTDIR=$(print $ROOTDIR/var/sadm/pkg | $COMPACT_SLASHES)
fi
}

####################  set_patchdir  ####################  

# Description: 
#       Figure out where the .order file is in the patch set.
# Parameters: 
#       None. 
# Globals set: 
#       PATCHDIR, ARCH, LPROC, 
# Globals used:
#       TOP_DIR, ROOTDIR, LOGFILE, PWDCMD, uPATCHDIR, 

function set_patchdir {

# Set PATCHDIR to location of .order file in patch directory.
# Here's the variable setting and search algorithm:
#
#   If TOP_DIR is set then 				MU_TOP=TOP_DIR
#   else						MU_TOP="MU"
#
#   LPROC is set to sparc or x86 depending on ARCH field of $ROOTDIR/var/sadm/pkg/SUNWcsr/pkginfo
#
#   If uPATCHDIR is set and if uPATCHDIR/.order exists then   PATCHDIR=uPATCHDIR
#
#   If uPATCHDIR is set and if
#     uPATCHDIR/MU_TOP/LPROC/Patches/.order exists then   PATCHDIR=./uPATCHDIR/MU_TOP/LPROC/Patches
#
#   If uPATCHDIR is set but we couldn't find a PATCHDIR 
#     in the last two places we looked then error out.
#
#   If ./LPROC/Patches/.order exists then		PATCHDIR=./LPROC/Patches
#
#   If ./MU_TOP/LPROC/Patches/.order exists then	PATCHDIR=./MU_TOP/LPROC/Patches
#
#   If /cdrom/cdrom0/LPROC/Patches/.order exists then  	PATCHDIR=/cdrom/cdrom0/MU_TOP/LPROC/Patches 
#
#   If PATCHDIR 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 "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 "install_mu only supports sparc and i386 architectures.\n" | tee -a ${LOGFILE}
                $GETTEXT "install_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 [[ "$uPATCHDIR" != "" &&  -f $uPATCHDIR/.order ]]; then
		# uPATCHDIR is patch directory
                cd $uPATCHDIR
                PATCHDIR=`$PWDCMD`
                cd $xdir
                return
        fi

        if [[ "$uPATCHDIR" != "" &&  -f $uPATCHDIR/$MU_TOP/$LPROC/Patches/.order ]]; then
        # uPATCHDIR/MU_TOP/LPROC/Patches is the patch directory
                cd $uPATCHDIR/$MU_TOP/$LPROC/Patches
                PATCHDIR=`$PWDCMD`
                cd $xdir
                return
        fi

	# If we've gotten to here the -p parameter was set but we couldn't find a .order file
	# in uPATCHDIR or in uPATCHDIR/MU_TOP/LPROC/Patches

	if [ "$uPATCHDIR" != "" ]; then
		$GETTEXT "-p parameter does not point to a directory containing a .order file.\n" | tee -a ${LOGFILE}
		$GETTEXT "Looked in $uPATCHDIR and in $uPATCHDIR/$MU_TOP/$LPROC/Patches.\n" | tee -a ${LOGFILE}
		print_usage
		exit 14
	fi	


	if [ -f ./$LPROC/Patches/.order ]; then
		# LPROC/Patches is the patch directory
		cd $LPROC/Patches
		PATCHDIR=`$PWDCMD`	
		cd $xdir
		return
	fi

	if [ -f $MU_TOP/$LPROC/Patches/.order ]; then
		# MU_TOP/LPROC/Patches is the patch directory
		cd $MU_TOP/$LPROC/Patches
                PATCHDIR=`$PWDCMD`
                cd $xdir
                return
        fi

	if [ -f $CDROMLOC/$LPROC/Patches/.order ]; then
		# /cdrom/cdrom0/LPROC/Patches is the patch directory
		cd $CDROMLOC/$LPROC/Patches
		PATCHDIR=`$PWDCMD` 
                cd $xdir
                return
        fi

	# Cannot find patch directory. Give up!

	cd $xdir

	$GETTEXT "install_mu cannot locate patch order (.order) file.\n" | tee -a ${LOGFILE}	
	$GETTEXT "Paths searched:\n" | tee -a ${LOGFILE}
	$GETTEXT " ./$LPROC/Patches, $MU_TOP/$LPROC/Patches, $CDROMLOC/$LPROC/Patches\n" | tee -a ${LOGFILE}
	if [ "$uPATCHDIR" != "" ]; then
		$GETTEXT " ./$uPATCHDIR, and ./$uPATCHDIR/$MU_TOP/$LPROC/Patches.\n"    | tee -a ${LOGFILE}
	fi
        print_usage
        exit 15
}

####################  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"
           	exit 16
        fi
}

####################  build_admin_file  ####################  

# Description:
#       Build the admin file for later use by non-interactive pkgadd
# Parameters:
#       none
# Globals Used:
#       ADMINFILE, CAT

function build_admin_file {
$CAT > $ADMINFILE << EOF
mail=
instance=overwrite
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=quit
setuid=nocheck
conflict=nocheck
action=nocheck
EOF
}

####################  eval_dryrun_failures  ####################  

# Descriprtion:
#	Report errors from pkgadd dryrun mode
# Parameters:
#	Dryrun directory name
# Globals used:
# 
# Globals set:
#

function eval_dryrun_failures {

	$GETTEXT "The following errors were reported by pkgadd dryrun...\n\n"

	exitCheck=$($NAWK -F= ' $2 ~ /!0/ { print $1 } ' $1/dryrun.isum.asc )

	for code in $exitCheck
	do   
	        case $code in
	        	CHECKINSTALLEXITCODE)   $GETTEXT "  The checkinstall script failed.\n" ;;
	        	REQUESTEXITCODE)        $GETTEXT "  The request script failed.\n" ;;
	    	esac
	done 

	dryrunFailures=$($NAWK -F= ' $2 ~ /NOT_OK/ { print $1 } ' $1/dryrun.isum.asc )
	failed="  Installation failed due to"
	for param in $dryrunFailures
	do   
	        case $param in
	            SPACE)      $GETTEXT "$failed lack of space reported by pkgadd dryrun\n" ;;
	            PARTIAL)    $GETTEXT "$failed partial install reported by pkgadd dryrun\n" ;;
	            RUNLEVEL)   $GETTEXT "$failed incorrect run level\n" ;;
	            PKGFILES)   $GETTEXT "$failed bad pkg reported by pkgadd dryrun\n" ;;
	            DEPEND)     $GETTEXT "$failed incorrect depend file\n" ;;
	            CONFLICT)   $GETTEXT "$failed conflicts reported by pkgadd dryrun\n" ;;
	            SETUID)     $GETTEXT "$failed incorrect uid\n" ;;
	            PKGDIRS)    $GETTEXT "$failed package directories not found\n" ;;
	        esac
	done 

}

####################  parse_sizes  ####################

# Description:
#   Used in the file system space calculation.  Determine where each
#   identified file will be placed, and add its size to the correct
#   running total.
# Parameters:
#       $pkgdir_and_name      - patch id and package name
# Globals Used:
#       Openwin_Kbytes_Needed
#       Usr_Kbytes_Needed
#       Opt_Kbytes_Needed
#       Var_Kbytes_Needed
#       Root_Kbytes_Needed
#
function parse_sizes
{
	typeset -i size=0
	typeset -i installedSize=0

	$CAT $pkgdir_and_name/pkgmap | while read part ftype f3 f4 f5 f6 f7 f8 Junk
	do
		case $ftype in
			f|e|v)
				$($VALPATH -a $f4)
				ret=$?
				if [[ "$ret" != 0 ]]
				then
					# Prepend the basedir to
					# the relocatable objects
					if [[ "$Pkgpatchbase" = "/" ]]
					then
						pathname="$Pkgpatchbase$f4"
					else
						pathname="$Pkgpatchbase/$f4"
					fi
				else
					pathname=$f4
				fi

				# Calculate the difference from the
				# installing object with the object
				# that is already installed.
				if [[ "$ROOTDIR" != "/" ]]
				then
					# Prepend the root path to the installed object
					tmpPath="$ROOTDIR$pathname"
				else
					tmpPath="$pathname"
				fi

				if [[ -f "$tmpPath" ]]
				then
					installedSize=$($WC -c $tmpPath | $NAWK '{ print $1 }')
					
					# Keep a running total of the installed size of the
					# data to do the backout size computation. Also track
					# the installed size by pkg to do the computation for
					# fudge factor needed during the compression of the pkg.
					# In both calculations convert bytes to kbytes.

					backout_kbytes_needed=backout_kbytes_needed+installedSize/1024
					pkg_size=pkg_size+installedSize/1024

					size=$f8
					size=size-installedSize
					if (( size < 0 ))
					then
						size=0
					fi
				else
					size=$f8
				fi

				case $pathname in
					usr\/openwin\/*|\/usr\/openwin\/*|openwin\/*|\/openwin\/*)
						Openwin_Kbytes_Needed=Openwin_Kbytes_Needed+size ;;
					usr\/*|\/usr\/*)
						Usr_Kbytes_Needed=Usr_Kbytes_Needed+size ;;
					var\/*|\/var\/*)
						Var_Kbytes_Needed=Var_Kbytes_Needed+size ;;
					opt\/*|\/opt\/*)
						Opt_Kbytes_Needed=Opt_Kbytes_Needed+size ;;
					*)
						Root_Kbytes_Needed=Root_Kbytes_Needed+size ;;
				esac
				;;
			i)
				size=$f4
				Var_Kbytes_Needed=Var_Kbytes_Needed+size
				;;
			d|l|s|p|b|c|x)
				$($VALPATH -a $f4)
				ret=$?
				if [[ "$ret" != 0 ]]
				then
					# Prepend the basedir to
					# the relocatable objects
					if [[ "$Pkgpatchbase" = "/" ]]
					then
						pathname="$Pkgpatchbase$f4"
					else
						pathname="$Pkgpatchbase/$f4"
					fi
				else
					pathname=$f4
				fi

				case $pathname in
					usr\/openwin\/*|\/usr\/openwin\/*|openwin\/*|\/openwin\/*)
						Openwin_Kbytes_Needed=Openwin_Kbytes_Needed+512 ;;
					usr\/*|\/usr\/*)
						Usr_Kbytes_Needed=Usr_Kbytes_Needed+512 ;;
					var\/*|\/var\/*)
						Var_Kbytes_Needed=Var_Kbytes_Needed+512 ;;
					opt\/*|\/opt\/*)
						Opt_Kbytes_Needed=Opt_Kbytes_Needed+512 ;;
					*)
						Root_Kbytes_Needed=Root_Kbytes_Needed+512 ;;
				esac
				;;
			*)
				;;
		esac
	done
}

####################  compute_fs_space_requirements  ####################

# Description:
#   Compute the file system space requirements for /, /var, /opt,
#   /usr, and /usr/openwin to determine if there is enough free space
#   in which to place the patch.
#
# Parameters:
#
# Globals Used:
#
# Globals Set:
#
function compute_fs_space_requirements
{
        typeset -i size=0

        if [[ "$ROOTDIR" != "/" ]]
        then
                VarFS=$($DF -a $ROOTDIR/var 2>/dev/null | $GREP var)
                OptFS=$($DF -a $ROOTDIR/opt 2>/dev/null | $GREP opt)
                UsrFS=$($DF -a $ROOTDIR/usr 2>/dev/null | $GREP usr)
                OpenwinFS=$($DF -a $ROOTDIR/usr/openwin 2>/dev/null | $GREP openwin)
        else
                VarFS=$($DF -a /var 2>/dev/null | $GREP var)
                OptFS=$($DF -a /opt 2>/dev/null | $GREP opt)
                UsrFS=$($DF -a /usr 2>/dev/null | $GREP usr)
                OpenwinFS=$($DF -a /usr/openwin 2>/dev/null | $GREP openwin)
        fi

        # All sizes gathered to this point are in bytes. Need to divide by a K.

        if [[ -z "$OpenwinFS" ]]
        then
                Usr_Kbytes_Needed=Usr_Kbytes_Needed+Openwin_Kbytes_Needed
                Openwin_Kbytes_Needed=0
        else
                if (( Openwin_Kbytes_Needed > 0 ))
                then
                        Openwin_Kbytes_Needed=Openwin_Kbytes_Needed/1024
			Total_Kbytes_Needed=Total_Kbytes_Needed+Openwin_Kbytes_Needed
                fi
        fi

        if [[ -z "$UsrFS" ]]
        then
                Root_Kbytes_Needed=Root_Kbytes_Needed+Usr_Kbytes_Needed
                Usr_Kbytes_Needed=0
        else
                if (( Usr_Kbytes_Needed > 0 ))
                then
                        Usr_Kbytes_Needed=Usr_Kbytes_Needed/1024
			Total_Kbytes_Needed=Total_Kbytes_Needed+Usr_Kbytes_Needed
                fi
        fi

        if [[ -z "$OptFS" ]]
        then
                Root_Kbytes_Needed=Root_Kbytes_Needed+Opt_Kbytes_Needed
                Opt_Kbytes_Needed=0
        else
                if (( Opt_Kbytes_Needed > 0 ))
                then
                        Opt_Kbytes_Needed=Opt_Kbytes_Needed/1024
			Total_Kbytes_Needed=Total_Kbytes_Needed+Opt_Kbytes_Needed
                fi
        fi

        if [[ -z "$VarFS" ]]
        then
                Root_Kbytes_Needed=Root_Kbytes_Needed+Var_Kbytes_Needed
                Var_Kbytes_Needed=0
        else
                if (( Var_Kbytes_Needed > 0 ))
                then
                        Var_Kbytes_Needed=Var_Kbytes_Needed/1024
			Total_Kbytes_Needed=Total_Kbytes_Needed+Var_Kbytes_Needed
                fi
        fi

        Root_Kbytes_Needed=Root_Kbytes_Needed/1024
	Total_Kbytes_Needed=Total_Kbytes_Needed+Root_Kbytes_Needed
}


####################  check_fs_space  ####################
# Description:
#   Check space needed against space available
#
# Parameters:
#       None
#
# Globals Used:
#       ROOTDIR
#       VarFS
#       OptFS
#       UsrFS
#       OpenwinFS
#       Root_Kbytes_Needed
#       Var_Kbytes_Needed
#       Opt_Kbytes_Needed
#       Usr_Kbytes_Needed
#       Openwin_Kbytes_Needed
#
# Globals Set:
#       None
#
function check_fs_space
{
        typeset -i Opt_Available=0
        typeset -i Openwin_Available=0
        typeset -i Root_Available=0
        typeset -i Usr_Available=0
        typeset -i Var_Available=0

        # Bear in mind that df -b gives the total kbytes available to
        # the super-user. That means we have to be conservative since
        # there's no pad.

        Tmp_Available=$($DF -b $ROOTDIR | $SED -e '1d')
        Root_Available=${Tmp_Available:#* }

        # The root file system must have at least 1Mb of free
        # space or there will be problems after rebooting

        Root_Available=Root_Available-1000

        enoughFsSpace="YES"
        dryrun_space_fail="no"
        enough_to_install="yes"

        printf "\nSpace Available/Needed to apply patch set, including swap space\n\n" >> $DRYRUN_REPORT
        printf "%-44s%10s%10s%8s\n" "    Filesystem" "Kb Avail" "Kb Needed" "Enough?" >> $DRYRUN_REPORT

        if (( Root_Kbytes_Needed > Root_Available ))
        then
		enoughFsSpace="NO"
		enough_to_install="no"
		dryrun_space_fail="yes"
        fi
	printf "%-44s%10d%10d%8s\n" "    $ROOTDIR" $Root_Available $Root_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT
                                 
        if [[ -n "$UsrFS" ]]
        then
                Tmp_Available=$($DF -b $ROOTDIR/usr | $SED -e '1d')
                Usr_Available=${Tmp_Available:#* }
		ReportFS=$(print $UsrFS | nawk '{print $1}')
		enoughFsSpace="YES"
                if (( Usr_Kbytes_Needed > Usr_Available ))
                then
                        enoughFsSpace="NO"
                        enough_to_install="no"
                        dryrun_space_fail="yes"
                fi
                
                printf "%-44s%10d%10d%8s\n" "    $ReportFS" $Usr_Available $Usr_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT
        fi

        if [[ -n "$OptFS" ]]
        then
                Tmp_Available=$($DF -b $ROOTDIR/opt | $SED -e '1d')
                Opt_Available=${Tmp_Available:#* }
		ReportFS=$(print $OptFS | nawk '{print $1}')
		enoughFsSpace="YES"
                if (( Opt_Kbytes_Needed > Opt_Available ))
                then
                        enoughFsSpace="NO"
                        enough_to_install="no"
                        dryrun_space_fail="yes"

                fi
                
                printf "%-44s%10d%10d%8s\n" "    $ReportFS" $Opt_Available $Opt_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT
        fi

        if [[ -n "$VarFS" ]]
        then
                Tmp_Available=$($DF -b $ROOTDIR/var | $SED -e '1d')
                Var_Available=${Tmp_Available:#* }
		ReportFS=$(print $VarFS | nawk '{print $1}')
		enoughFsSpace="YES"
                if (( Var_Kbytes_Needed > Var_Available ))
                then
                        enoughFsSpace="NO"
                        enough_to_install="no"
                        dryrun_space_fail="yes"
                fi
                printf "%-44s%10d%10d%8s\n" "    $ReportFS" $Var_Available $Var_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT
        fi

        if [[ -n "$OpenwinFS" ]]
        then
                Tmp_Available=$($DF -b $ROOTDIR/usr/openwin | $SED -e '1d')
                Openwin_Available=${Tmp_Available:#* }
		ReportFS=$(print $OpenwinFS | nawk '{print $1}')
		enoughFsSpace="YES"
                if (( Openwin_Kbytes_Needed > Openwin_Available ))
                then
                        enoughFsSpace="NO"
                        enough_to_install="no"
                        dryrun_space_fail="yes"
                fi
                printf "%-44s%10d%10d%8s\n" "    $ReportFS" $Openwin_Available $Openwin_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT
        fi

	
        printf "------------------------------------------------------------------------\n" >> $DRYRUN_REPORT
        printf "%-10s%54s\n\n" "    Total:" $Total_Kbytes_Needed >> $DRYRUN_REPORT

}


####################  check_disk_space  ####################  
# Description:
#	Check for sufficient disk space to apply patch set, possibly with
#	backout data.
# Parameters:
#
# Globals used:
#
# Globals set:
#

function check_disk_space {

	typeset -i link_count=1
        typeset -i kbytes_avail
        typeset -i backout_kbytes_needed
        typeset -i backout_kbytes_avail
	typeset -i pkg_size_max=0
	typeset -i pkg_size=0
	typeset -i tmp_space_avail=0
	typeset -i rec_tmp_space=0
        typeset -i Openwin_Kbytes_Needed=0
        typeset -i Usr_Kbytes_Needed=0
        typeset -i Opt_Kbytes_Needed=0
        typeset -i Var_Kbytes_Needed=0
        typeset -i Root_Kbytes_Needed=0
        typeset -i Total_Kbytes_Needed=0


	# Here's how we compute/guesstimate install and backout space needed:
	#
	#	Install
	#		Compute pkgadd dryrun for all patch packages that will install.
	#		Use the blocks used ("bused") value to determine how much
	#		extra space will be needed to apply the patch packages. Convert
	#		blocks to KB.
	#
	#		this value, for each filesystem needing more than 0 blocks, 
	#		add a fudge factor (three times the largest number of blocks 
	#		needed by any one package as reported in the pkgmap file) and we're done.
	#
	#	Backout
	#		This is a guess as to how much space is needed in the backout
	#		directory:
	#
	#		Compute the sum of the space used by all new packages, as
	#		reported in the pkgmap files. Divide this number by two
	#		to convert blocks to KB. To this number, add a fudge
	#		factor, twice the number of blocks needed by any one package as
	#		reported in the pkgmap file, and we're done!
	#

	# If we're only doing a dryrun to check disk space inform the user that it is
	# unnecesary and that he/she can/should abort

	if [ "$DRYRUN_ONLY" = "true" ]; then
	
		$GETTEXT "\nThe dryrun -D argument is no longer necessary to check disk space. The\n"
		$GETTEXT "disk space check is always done independently of the dryrun process unless\n"
		$GETTEXT "it is specifically disabled with the -f argument.\n"
		
		$GETTEXT "\nIn it's current form, dryrun is only useful to Sun internal test engineers\n"
		$GETTEXT "performing MU content validation. Since the time required to fully execute\n"
		$GETTEXT "dryrun could exceed 12 hours on some systems, it is not recommended for general\n"
		$GETTEXT "use.\n"
		
		$GETTEXT "\nYou can abort this program (via Control-C) then rerun install_mu without\n"
		$GETTEXT "the -D argument. Patchadd will still perform a dryrun during the individual\n"
		$GETTEXT "patch installation, and will report any problems that may be found.\n"
		
	fi

	$GETTEXT "\nAnalyzing space requirements for patch set..." | tee -a ${LOGFILE}

	# pkgadd dryrun will have problems with low /tmp disk space

	tmp_space_avail=$($DF -k /tmp | tail -1 | $AWK '{print $4}')
	rec_tmp_space=$($LS -l $ROOTDIR/var/sadm/install/contents | $AWK '{print $5}')*3/1024
	if (( tmp_space_avail < rec_tmp_space )); then
		$GETTEXT "\n\nWarning: You have only ${tmp_space_avail}KB available in /tmp.\n" | tee -a ${LOGFILE}
		$GETTEXT "${rec_tmp_space}KB or more is recommended.\n" | tee -a ${LOGFILE}
		$GETTEXT "\npkgadd dryrun may report errors because of this.\n\n" | tee -a ${LOGFILE}
	fi

	activate_do_busy

	# Create directory to contain links to all packages in all patches

	if [ -d $DRYRUN_TMP_DIR ]; then
		$RM -rf $DRYRUN_TMP_DIR
	fi

	$MKDIR $DRYRUN_TMP_DIR
	$CP /dev/null $PKGLISTFILE
	my_dir=$(pwd)
	cd $PATCHDIR

	# Make sure the directory where space check will leave temp stuff
	# exists and is empty.

	if [ ! -d $DRYRUN_JUNK_DIR ]; then
		$MKDIR $DRYRUN_JUNK_DIR
	fi

	$RM -f $DRYRUN_JUNK_DIR/*
	
	# Create list of patch pkginfo files
	# line format of $PKGLISTFILE = <patchid>/<pkgdir>/

	$CAT .order | $GREP -v "^#" | while read a_patch; do
		$LS $a_patch/*/pkginfo | $SED 's/pkginfo//g' >> $PKGLISTFILE
	done


	# Build a set of links to package directories for packages that will actually install
	# on the target system

	while read pkgdir_and_name; do          # e.g. 105181-03/SUNWcar.c/
		pkgname=${pkgdir_and_name#*/}   # e.g. SUNWcar.c/
		pkgname=${pkgname%/}            # e.g. SUNWcar.c
		pkgabbrev=$(pkgparam -f $pkgdir_and_name/pkginfo PKG)   # e.g. SUNWcar
		pkgdir=${pkgdir_and_name%/*/}   # e.g. 105181-03
		if [[ -d $ROOTDIR/var/sadm/pkg/$pkgabbrev || -d $ROOTDIR/var/sadm/pkg/$pkgabbrev.* ]]; then
		        Pkgarch=$(pkgparam -f $pkgdir_and_name/pkginfo ARCH)
 			Pkgpatchver=$(pkgparam -f $pkgdir_and_name/pkginfo VERSION)
                        Pkgpatchbase=$(pkgparam -f $pkgdir_and_name/pkginfo BASEDIR)
			pkginst=$(pkginfo -R $ROOTDIR -a $Pkgarch -v $Pkgpatchver $pkgabbrev.\* 2>/dev/null \
				| $HEAD -1 | awk ' { print $2 } ')
			if [[ -n $pkginst ]]; then  # package exists with matching ARCH and VERSION fields
						    # Now check if patch package is already applied.
				pkgpatchlist=$(pkgparam -f $ROOTDIR/var/sadm/pkg/$pkginst/pkginfo PATCHLIST \
					| $GREP "$pkgdir")
				if [ "$pkgpatchlist" = "" ]; then     # Patch is NOT applied. Now we need to add
								      # a link to later check space for this unpatched pkg
					$LN -s $PATCHDIR/$pkgdir_and_name $DRYRUN_TMP_DIR/$pkgname.$link_count
					link_count=link_count+1

					# Determine the size of the files to be installed and difference in
					# size compared to the same files already installed. Keep track of 
					# of the comparison by filesystem

					parse_sizes

                                        # Compute pkg_size_max to use for backout fudge factor
					# pkg_size is computed in the parse_sizes function.

                                        if (( pkg_size > pkg_size_max )); then
                                                pkg_size_max=pkg_size
                                        fi

					pkg_size=0

				fi
			fi
		fi

	done < $PKGLISTFILE

	cd $my_dir

	if (( link_count == 1 )); then
		stop_do_busy
		$GETTEXT "\nAll patches already applied. No disk space needed.\n"
		return
	fi

	# Check to determine which filesystems need to be reported and if there is
	# sufficient space in each.

	compute_fs_space_requirements

	# Now do dryrun pkgadd for all the patch packages. This will take a while!
	# The user is going to wish they had paid attention to the previous message!
	
	if [ "$DRYRUN_ONLY" = "true" ]; then
		$PKGADD -S -n -d $DRYRUN_TMP_DIR -R $ROOTDIR -D $DRYRUN_JUNK_DIR -a $ADMINFILE all > /dev/null 2>&1
		
		# Report if pkgadd dryrun fails completely.
		
		if [ ! -r $DRYRUN_JUNK_DIR/dryrun.isum.asc -o ! -r $DRYRUN_JUNK_DIR/dryrun.fs.asc ]; then
			$GETTEXT "\n\nDryrun package check failed.\n" | tee -a ${LOGFILE}
                	do_file_cleanup
                	exit 24
		fi
		if [ ! -r $DRYRUN_JUNK_DIR/dryrun.isum.asc -o ! -r $DRYRUN_JUNK_DIR/dryrun.fs.asc ]; then
			$GETTEXT "\n\nDryrun package check failed.\n" | tee -a ${LOGFILE}
                	do_file_cleanup
                	exit 24
		fi
		dryrunExit=$($NAWK -F= ' $1 ~ /^EXITCODE$/ { print $2 } ' $DRYRUN_JUNK_DIR/dryrun.isum.asc )

		# Check and report dryrun failures.

		if [[ "$dryrunExit" != "0" ]]; then
			eval_dryrun_failures $DRYRUN_JUNK_DIR
		fi
	fi

	stop_do_busy

	# Check the space required against the space available and report it

	check_fs_space

	# Add backout space information to the report

        if [[ "$saveold" = "yes" ]]; then

		# Compute the final backout space required by dividing by two in consideration
		# that the compressed, saved data will be smaller. To this backoutsize, add three
		# times the size of the largest package size (pkg_size_max). This provides the
		# 'fudge factor' that needs to be included to account for the space needed
                # to do the compression of the saved data.

		backout_kbytes_needed=backout_kbytes_needed/2+pkg_size_max*3
		
		# Determine how much space is available in the backout filesystem
		
                backout_kbytes_avail=$($DF -k $BACKOUTDIR | tail -1 | $NAWK '{print $4}')

		backout_filesystem=$($DF -k $BACKOUTDIR | tail -1 | $NAWK '{print $6}')
				
		# Determine if the required backout space is greater than the amount
		# of space available. If it is, report the space descrepancy and 
		# exit install_mu.
		
                backout_enough="YES"
                
                if (( backout_kbytes_needed > backout_kbytes_avail )); then
                        backout_enough="NO"
                        dryrun_space_fail="yes"
                fi
                printf "Total space Available/Needed in $backout_filesystem filesystem to include backout data,\n" >> \
                	$DRYRUN_REPORT | tee -a ${LOGFILE}
		printf "and working space for compression.\n\n" >> $DRYRUN_REPORT | tee -a ${LOGFILE}
                printf "%-44s%10s%10s%8s\n" "    Backout Dir" "Kb Avail" "Kb Needed" "Enough?" >> $DRYRUN_REPORT
                printf "%-44s%10s%10s%8s\n" "    $BACKOUTDIR" $backout_kbytes_avail \
			$backout_kbytes_needed $backout_enough >> $DRYRUN_REPORT
        fi

	# Due to problems in the past in which we would have benefitted 
	# from the logging of the space check, we will always display and 
	# log the report

	cat $DRYRUN_REPORT | tee -a ${LOGFILE}
	
	if [ "$enough_to_install" = "no" ]; then
		$GETTEXT "\nNot enough disk space to apply entire patch set.\n" | tee -a ${LOGFILE}
		do_file_cleanup
		exit 22
	fi
	
	if [ "$backout_enough" = "NO" ]; then
		$GETTEXT "\nNot enough disk space to save patch backout data.\n" | tee -a ${LOGFILE}
		do_file_cleanup
                exit 23
	fi

	if [ "$saveold" = "yes" ]; then
		$GETTEXT "\nThere is sufficient space, including backout space.\n\n" | tee -a ${LOGFILE}
	else
		$GETTEXT "\nThere is sufficient disk space, not including backout space.\n\n" | tee -a ${LOGFILE}
	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:
#	Update the contents of /etc/release to reflect the
#	new MU that was applied.
# Parameters:
#	None.
# Globals Used:
#	ROOTDIR, RELEASE_INFO_TEMPLATE, HEAD, RELEASE_FILE, MU_RELEASE_LINE
# Globals Set:
#	None.

function update_release_file {
        # Don't update release file if we're patching to 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 

	# And add release information to the end of the release file
	if [ "$UserInterrupted" = "no" ]; then
		$ECHO $MU_RELEASE_LINE | PrintCenter >> $ROOTDIR/$RELEASE_FILE
	        $GETTEXT "\nUpdated `$ECHO $ROOTDIR/$RELEASE_FILE | $COMPACT_SLASHES` successfully.\n" | tee -a ${LOGFILE}
	fi
}

####################  add_SUNWsolnm  ####################  
 
# Description:
#	Deliver the SUNWsolnm package.
# Parameters:
#	None.
# Globals Used:
#	PATCHDIR, PKGDBARG, ADMINFILE, PKGADDLOGFILE, GREP, LOGFILE
# Globals Set:
#       None.

function add_SUNWsolnm {
	# Don't apply SUNWsolnm package if we're patching to a Service area
        if [ "$service_specified" = "yes" ]; then
		return
	fi

        if [ ! -d $PATCHDIR/Pkg/SUNWsolnm ]; then
        	$GETTEXT "No SUNWsolnm package exists in $PATCHDIR/Pkg.\n" | tee -a ${LOGFILE}
       		return
	fi

	$GETTEXT "Doing pkgadd of SUNWsolnm.\n" | tee -a ${LOGFILE}

        $PKGADD $PKGDBARG -S -a $ADMINFILE -n -R $ROOTDIR -d $PATCHDIR/Pkg SUNWsolnm >$PKGADDLOGFILE 2>&1
        sp_pkgadderr=$?
        sp_pkgaddfailed=0
        if [ $sp_pkgadderr = 2 ]; then
                $GREP ERROR $PKGADDLOGFILE >/dev/null 2>&1
                if [ $? = 0 ]; then
                        sp_pkgaddfailed=1
                fi
        elif [ $sp_pkgadderr != 0 -a $sp_pkgadderr != 10 -a $sp_pkgadderr != 20 ]; then
                sp_pkgaddfailed=1
        fi
        if [ $sp_pkgaddfailed = 1 ]; then
                $ECHO "SUNWsolnm pkgadd failed, error=$sp_pkgadderr.\n" | tee -a ${LOGFILE}
                $GETTEXT "pkgadd error messages follow:\n" | tee -a ${LOGFILE}
                $GETTEXT "********************************************\n" | tee -a ${LOGFILE}
                $CAT $PKGADDLOGFILE | tee -a ${LOGFILE}
                $GETTEXT "********************************************\n" | tee -a ${LOGFILE}
        else
		$GETTEXT "\nSUNWsolnm package successfully installed.\n" | tee -a ${LOGFILE}
	fi
}

####################  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 of the target system
get_OS_version $TRGSOFTINFO $MGRSOFTINFO $ROOTDIR

if [ "$MgrOSVers" != "$MU_OS" ]; then
	$GETTEXT "System running install_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 "install_mu can only patch Solaris $MU_OS systems.\n" | tee -a ${LOGFILE}
        $GETTEXT "Target system is version $TrgOSVers.\n" | tee -a ${LOGFILE}
	exit 17
fi

# Set PATCHDIR to absolute path to patch set
set_patchdir

## # Set the path to installpatch.fast and backoutpatch.fast
## TOOLSDIR=$PATCHDIR/../../common/Tools  
## 
## if [ ! -d $TOOLSDIR ]; then
## 	$GETTEXT "Directory with patch tools, $TOOLSDIR, not found.\n"
## 	print_usage
## 	exit 18
## fi
## 
## tmpdir=`$PWDCMD`
## cd $TOOLSDIR
## TOOLSDIR=`$PWDCMD`
## 
## if [ ! -x $TOOLSDIR/installpatch.fast -o ! -r $TOOLSDIR/installpatch.fast ]; then
## 	$GETTEXT "$TOOLSDIR/installpatch.fast does not exist or is not executable.\n"
## 	print_usage
## 	exit 19
## fi
##  
## if [ ! -x $TOOLSDIR/backoutpatch.fast -o ! -r $TOOLSDIR/backoutpatch.fast ]; then
## 	$GETTEXT "$TOOLSDIR/backoutpatch.fast does not exist or is not executable.\n"
## 	print_usage
## 	exit 19   
## fi
## 
## cd $tmpdir

# 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.
#
# From now on we write to LOGFILE
#

if [ "$DRYRUN_ONLY" = "true" ]; then
	$GETTEXT "\nRunning ${MS_NAME} ${RELEASE} (version $VERSION)    `date`\n" tee -a ${LOGFILE}
	$GETTEXT "in Dry Run Mode. No patches will be applied.\n"
	$GETTEXT "\nLogging output to log file: ${LOGFILE}\n" | tee -a ${LOGFILE}
	$GETTEXT "\nUsing patch set in ${PATCHDIR}\n"
else
	$GETTEXT "\nInstalling ${MS_NAME} ${RELEASE} (using install_mu $VERSION) \n   `date`\n" | tee -a ${LOGFILE}
	$GETTEXT "\nLogging output to log file: ${LOGFILE}\n" | tee -a ${LOGFILE}
	$GETTEXT "\nInstalling patches located in ${PATCHDIR}\n"                    | tee -a ${LOGFILE}
	if [ "$ARGS" = "" ]; then 
		ARGS="None."
	fi
	$GETTEXT "\nArguments to install_mu:  " | tee -a ${LOGFILE}
	$GETTEXT "${xsaveold} $PKGDBARG \
		$BACKOUTARG $PATCHDIR/<patch_id>\n\n" | sed 's/[ 	][ 	]*/ /g' | tee -a ${LOGFILE}
	if [ "$ROOTDIR" != "/" ]; then
		$GETTEXT "\nNote: You are applying the Maintenance Update to a client.\n" | tee -a ${LOGFILE}
		$GETTEXT "You will need to invoke install_mu a second time with the\n" | tee -a ${LOGFILE}
		$GETTEXT "-S option to patch the client's Service area (unless the\n" | tee -a ${LOGFILE}
		$GETTEXT "client's Service area has already been patched with the\n" | tee -a ${LOGFILE}
		$GETTEXT "Maintenance Update.) Failure to reinvoke install_mu will\n" | tee -a ${LOGFILE} 
		$GETTEXT "leave you with an improperly patched and unstable client.\n\n" | tee -a ${LOGFILE}
		$GETTEXT "Note also that clients have only a subset of the packages\n" | tee -a ${LOGFILE}
		$GETTEXT "that a server has - you will see a number of messages about\n" | tee -a ${LOGFILE}
		$GETTEXT "patches not being installed due to none of the patch packages\n" | tee -a ${LOGFILE}
		$GETTEXT "being installed on the system. This is normal.\n\n" | tee -a ${LOGFILE}
	fi
	if [ "$service_specified" = "yes" ]; then
		$GETTEXT "\nNote: You are applying the Maintenance Update to a Service area.\n" | tee -a ${LOGFILE}
		$GETTEXT "You will need to invoke install_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 "apply the same patch set to your server's root area as well.\n\n" | tee -a ${LOGFILE}
	fi
	$GETTEXT "Please REBOOT your system after patch set has been applied.\n\n"
fi

# Create the admin file for pkgadd
build_admin_file

# Make sure there's enough disk space. Quit if there isn't enough space.

if [ "$SKIP_DRYRUN" = "false" ]; then
	check_disk_space
else
	$GETTEXT "Skipping disk space check.\n\n" | tee -a ${LOGFILE}
fi

# If dryrun mode then quit now.

if [ "$DRYRUN_ONLY" = "true" ]; then
	do_file_cleanup
        exit 0
fi

# Install all the patches in PATCHDIR 

typeset -i count=0
typeset -i num_patches

num_patches=$(wc -l $PATCHDIR/.order | awk '{print $1}')

# This cd is done to maintain the cwd to PATCHDIR to suppress auto unmounts
cd $PATCHDIR

for patch in `cat $PATCHDIR/.order`; do
	count=count+1
	print -n "Installing ${patch} ($count of $num_patches) " | tee -a ${LOGFILE}
	activate_do_busy
	$PATCHADD ${xsaveold} $PKGDBARG $BACKOUTARG $PATCHDIR/$patch >> ${LOGFILE} 2>&1
        result=$?
	stop_do_busy
	if [ ${result} -ne 0 ]; then
                $GETTEXT "  Installation of ${patch} failed: \n  ${PatchAddError[$result]}\n"
		failed_list="${failed_list} ${patch} ${result} : "
        else
		succeeded_list="${succeeded_list} ${patch}"
	fi
done

UserInterrupted="no"

# Install or update .mu_applied.
handle_mu_applied

# List the patches that succeeded and failed installation (if any).
# Clean up log file
do_file_cleanup

# Install SUNWsolnm package if it exists
# add_SUNWsolnm

exit 0
