#!/bin/ksh -h
# @(#) installpatch 5.0 95/09/07 SMI
# is_server feature is disabled for now....
#
# Exit Codes:
#		0	No error
#		1	Usage error
#		2	Attempt to apply a patch that's already been applied
#		3	Effective UID is not root
#		4	Attempt to save original files failed
#		5	pkgadd failed
#		6	Patch is obsoleted
#		7	Invalid package directory
#		8	Attempting to patch a package that is not installed
#		9	Cannot access /usr/sbin/pkgadd (client problem)
#		10	Package validation errors
#		11	Error adding patch to root template
#		12	Patch script terminated due to signal
#		13	Symbolic link included in patch
#		14	NOT USED
#		15	The prepatch script had a return code other than 0.
#		16	The postpatch script had a return code other than 0.
#		17	Mismatch of the -d option between a previous patch
#			install and the current one.
#		18	Not enough space in the file systems that are targets
#			of the patch.
#		19	$SOFTINFO/INST_RELEASE file not found
#

# Set the path for use with these scripts.
PATH=/usr/sbin:/usr/bin:$PATH
export PATH

umask 022

# Global Files
EXISTFILES=/tmp/existfiles.$$
PATCHFILES=/tmp/patchfiles.$$
PKGCOFILE=/tmp/pkgchk.out.$$ 
VALERRFILE=/tmp/valerr.$$
VALWARNFILE=/tmp/valwarn.$$
ADMINTFILE=/tmp/admin.tmp.$$
ADMINFILE=/tmp/admin.$$
KBYTESFILE=/tmp/kbytes_required.$$
LOGFILE=/tmp/pkgaddlog.$$
TMP_ARCHIVE=/tmp/TmpArchive.$$
TMP_FILELIST=/tmp/FileList.$$
TMP_LIB_DIR=/tmp/TmpLibDir.$$
MYSHOWREV_FILE=/tmp/MyShowrevFile.$$
PARAMS_FILE=/tmp/ParamsFile.$$

typeset -i Kbytes_Required=0
typeset -i Opt_Kbytes_Needed=0
typeset -i Openwin_Kbytes_Needed=0
typeset -i Root_Kbytes_Needed=0
typeset -i Usr_Kbytes_Needed=0
typeset -i Var_Kbytes_Needed=0

Root_Equation_File=/tmp/Root_Equation_File.$$
Var_Equation_File=/tmp/Var_Equation_File.$$
Opt_Equation_File=/tmp/Opt_Equation_File.$$
Usr_Equation_File=/tmp/Usr_Equation_File.$$
Openwin_Equation_File=/tmp/Openwin_Equation_File.$$

client=no
is_a_root_pkg=no
patchdir=
patchnum=
patchbase=
patchrev=
pkglist=
printpatches="no"
rootlist=
saveold="yes"
validate="yes"
isapplied="no"

OpenwinFS=
Openwin_Equation=
OptFS=
Opt_Equation=
Root_Equation=
UsrFS=
Usr_Equation=
VarFS=
Var_Equation=

ROOTDIR="/"
PATCHDB="/var/sadm/patch"
PKGDB="/var/sadm/pkg"
SOFTINFO="/var/sadm/softinfo"
PKGDBARG=""

# Description:
#   Usage message
# Parameters:
#   none
#
function print_usage
{
cat << EOF

Usage: installpatch [-u -d -p -V] [-S <service> | -R <root_path>] <patch directory>
Options:
        -u    Turn off file validation.  Allows the patch to be
              applied even if some of the files to be patched have
              been modified since original installation.
        -d    Don't back up the files to be patched.  This means
              that the patch can't be backed out.
        -p    Print a list of the patches currently applied
        -V    Print script version number
        -S <service>
              Specify an alternate service (e.g. Solaris_2.3) for
              patch package processing references. Cannot be specified
              with the -R option.
        -R <root_path>
              Define the full path name of a subdirectory to use as the
              root_path. All package system information files are assumed
              to be located in a directory tree starting in the
              specified root_path. All patch files generated from the
              installpatch will be located in the same directory tree.
              Cannot be specified with the -S option.
EOF
}

#
# Description:
#	If a prepatch executable exists in the $1 directory, execute it.
#	If the return code is 0, continue. Otherwise, exit with code 15.
#
# Parameters:
#	$1	- patch directory.
# Globals Set:
#	none
#
function execute_prepatch
{
	typeset -i retcode=0
	if [ -x "$1/prepatch" ]
	then
		echo "Executing prepatch script..."
		$1/prepatch
		retcode=$?
		if (( retcode != 0 ))
		then
			echo "The prepatch script exited with return code $retcode."
			echo "Installpatch is terminating."
			rm -fr /tmp/*.$$
			exit 15
		fi
	fi
}

#
# Description:
#	If a postpatch executable exists in the $1 directory, execute it.
#	If the return code is 0, continue. Otherwise, if this is not
#	a re-installation of the patch, execute the 
#	backoutpatch script and exit with a return code 16.
#	If this is a re-installation, don't backout the patch. Instead,
#	send a message to the user.
#
# Parameters:
#	$1	- patch database directory
#	$2	- patch number
#	$3	- patch directory.
# Globals Set:
#	none
#
function execute_postpatch
{
	typeset -i retcode=0
	if [ -x "$3/postpatch" ]
	then
		echo "Executing postpatch script..."
		$3/postpatch
		retcode=$?
		if (( retcode != 0 ))
		then
			echo "The postpatch script exited with return code $retcode."
			if [ "$isapplied" = "no" ]
			then
				cp $1/$2/log /tmp/log.$2
				echo "Backing out patch:"
				cd $3
				if [ "$ROOTDIR" != "/" ]
				then
					./backoutpatch $PKGDBARG $2
				else
					./backoutpatch $2
				fi
				echo "Installpatch is terminating."
				echo "See /tmp/log.$2 for more details."
			else
				echo "Not backing out patch because this is a re-installation." |tee -a $1/$2/log
				echo "The system may be in an unstable state!" |tee -a $1/$2/log
				echo "Installpatch is terminating."
				echo "See $1/$2/log for more details."
			fi
			rm -fr /tmp/*.$$
			exit 16
		fi
	fi
}

#
#
# Description:
#	Give a list of applied patches similar in format to the showrev -p
#	command. Had to write my own because the showrev command won't take
#	a -R option.
#
# Parameters:
#	$1	- package database directory
# Globals Set:
#	none
#
function myshowrev
{
	if [ -f $MYSHOWREV_FILE ]
	then
		cat $MYSHOWREV_FILE
		return
	fi

	olddir=$(pwd)
	cd $1
	patches=
	patches=$(grep -l SUNW_PATCHID ./*/pkginfo | \
			xargs sed -n 's/^SUNW_PATCHID=//p' | sort -u)
	if [ "$patches" != "" ]
	then
		for apatch in $patches
		do
			outstr="Patch: $apatch Obsoletes: "
			patchvers=$(grep -l "SUNW_PATCHID=$apatch" ./*/pkginfo | \
						sed 's,^./\(.*\)/pkginfo$,\1,')
			obsoletes_printed="n"
			for vers in $patchvers
			do
				if [ "$obsoletes_printed" = "n" ]
				then
					outstr="$outstr$(sed -n 's/SUNW_OBSOLETES=//p' ./$vers/pkginfo) Packages: "
					outstr="$outstr$vers $(sed -n 's/VERSION=//p' ./$vers/pkginfo)"
					obsoletes_printed="y"
				else
					outstr="$outstr, $vers $(sed -n 's/VERSION=//p' ./$vers/pkginfo)"
				fi
			done
			echo $outstr | tee -a $MYSHOWREV_FILE
		done
	else
		echo "No patches are installed"
	fi
	cd $olddir
}

#
# Description:
#	Validate the patch directory, and parse out the patch number and
#	patch revision from the first pkginfo file found in the patch
#	packages.
# Parameters:
#	$1	- patch directory
# Globals Set:
#	patchnum
#	patchbase
#	patchrev

function activate_patch
{
	cd $1
	for i in */pkginfo
	do
		#
		# Find the patch number in one of the pkginfo files. If there is 
		# no pkginfo file having a SUNW_PATCHID=xxxxxx entry, send an 
		# error to the user and exit.
		#
		patchnum=$(grep '^SUNW_PATCHID' $i 2>/dev/null | sed 's/.*=[ 	]*\([^ 	]*\).*/\1/')
		break;
	done
	if [ "$patchnum" = "" ]
	then
		echo "$1 packages are not proper patch packages."
		echo 'See "Instructions for applying the patch" in the README file.'
		exit 7
	else
		#
		# Get the patch base number (the patch number up to the -) 
		# and the patch revision number (the patch number after the -).
		#
		patchbase=${patchnum%-*}
		patchrev=${patchnum##*-}
	fi
}

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

function build_admin_file
{
cat > $ADMINTFILE << EOF
mail=
instance=unique
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=quit
setuid=nocheck
conflict=nocheck
action=nocheck
EOF
}

# Description:
#	See if there is any work to be done. If none of the packages to
#	which the patch applies are installed and there is no spooling work
#	to do for the client root templates, then you're done.
#	NEW:
#	If SUNWcar, SUNWcsd or SUNWcsr is included in the patch,
#	but the package is not on the list to be patched, then print an
#	error message and die. At least one instance of these packages
#	should be patched if included in the patch.
# Parameters:
#	$1	- client status
#	$2	- were any of the packages root packages?
# Globals Used:
#	pkglist
#	rootlist
#	patchdir

function check_for_action
{
	if [ "$pkglist" = "" -a "$rootlist" = "" ]
	then
		#
		# In the first case, the system is not a client, however, 
		# there are still no packages to patch. This will only 
		# occur if the packages in question have not been installed 
		# on the system.
		#
		if [ $1 = no -o $2 = yes ]
		then
 			echo "None of the packages to patch are installed on this system."
			echo "Installpatch is terminating."
			exit 8
		else
			#
			# In the second case, the system is a client system. 
			# There are two types of packages for client systems: 
			# root packages (those packages installed on the client 
			# machines) and packages installed only on the server. 
			# Installpatch will exit if the machine is a client, and 
			# there are no root packages to be patched.
			#
			echo "This patch is not applicable to client systems."
			echo "Installpatch is terminating."
			exit 0
		fi
	fi
}

# Description:
#	Check to see if the patch has already been applied
# Parameters:
#	$1	- patch database directory
#	$2	- patch number
# Globals Set:
#	isapplied will be set to "yes" if this is a re-application of a patch. This
#	will not necessarily cause a bail out if there are packages that should be
#	installed that were not installed the first time the patch was applied.
#
function check_if_applied
{
	if myshowrev $PKGDB | grep -s "^Patch:[ 	]*$2" > /dev/null 2>&1
	then
		isapplied="yes"
	else
		rm -fr $1/$2
	fi
}

# Description:
#   Print space error message
#
function space_error_msg
{
    echo "Not enough space in $1 to apply patch"
    echo "\t $1 has   $2 Kbytes available"
    echo "\t $1 needs $3 Kbytes free"
}

# 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
	typeset -i Openwin_Available
	typeset -i Root_Available
	typeset -i Usr_Available
	typeset -i Var_Available
	typeset -i exit_status=0

	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

	if (( Root_Kbytes_Needed > Root_Available ))
	then
		space_error_msg $ROOTDIR $Root_Available $Root_Kbytes_Needed
		exit_status=18
	fi

	if [ -n "$UsrFS" ]
	then
		Tmp_Available=$(df -b $ROOTDIR/usr | sed -e '1d')
		Usr_Available=${Tmp_Available##* }
		if (( Usr_Kbytes_Needed > Usr_Available ))
		then
			space_error_msg $ROOTDIR/usr $Usr_Available $Usr_Kbytes_Needed
			exit_status=18
		fi
	fi

	if [ -n "$OptFS" ]
	then
		Tmp_Available=$(df -b $ROOTDIR/opt | sed -e '1d')
		Opt_Available=${Tmp_Available##* }
		if (( Opt_Kbytes_Needed > Opt_Available ))
		then
			space_error_msg $ROOTDIR/opt $Opt_Available $Opt_Kbytes_Needed
			exit_status=18
		fi
	fi

	if [ -n "$VarFS" ]
	then
		Tmp_Available=$(df -b $ROOTDIR/var | sed -e '1d')
		Var_Available=${Tmp_Available##* }
		if (( Var_Kbytes_Needed > Var_Available ))
		then
			space_error_msg $ROOTDIR/var $Var_Available $Var_Kbytes_Needed
			exit_status=18
		fi
	fi

	if [ -n "$OpenwinFS" ]
	then
		Tmp_Available=$(df -b $ROOTDIR/usr/openwin | sed -e '1d')
		Openwin_Available=${Tmp_Available##* }
		if (( Openwin_Kbytes_Needed > Openwin_Available ))
		then
			space_error_msg $ROOTDIR/usr/openwin $Openwin_Available $Openwin_Kbytes_Needed
			exit_status=18
		fi
	fi

	if (( exit_status != 0 ))
	then
		exit $exit_status
	fi
}



# 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:
#	None
#
# Globals Used:
#
# Globals Set:
#
function compute_fs_space_requirements
{
	typeset -i size=0

	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)

	if [ -z "$OpenwinFS" ]
	then
		Usr_Kbytes_Needed=Usr_Kbytes_Needed+Openwin_Kbytes_Needed
		Openwin_Kbytes_Needed=0
	else
		Openwin_Kbytes_Needed=Openwin_Kbytes_Needed/1000
	fi

	if [ -z "$UsrFS" ]
	then
		Root_Kbytes_Needed=Root_Kbytes_Needed+Usr_Kbytes_Needed
		Usr_Kbytes_Needed=0
	else
		Usr_Kbytes_Needed=Usr_Kbytes_Needed/1000
	fi

	if [ -z "$OptFS" ]
	then
		Root_Kbytes_Needed=Root_Kbytes_Needed+Opt_Kbytes_Needed
		Opt_Kbytes_Needed=0
	else
		Opt_Kbytes_Needed=Opt_Kbytes_Needed/1000
	fi

	Var_Kbytes_Needed=Var_Kbytes_Needed+Kbytes_Required
	if [ -z "$VarFS" ]
	then
		Root_Kbytes_Needed=Root_Kbytes_Needed+Var_Kbytes_Needed
		Var_Kbytes_Needed=0
	else
		Var_Kbytes_Needed=Var_Kbytes_Needed/1000
	fi

	Root_Kbytes_Needed=Root_Kbytes_Needed/1000
}


# Description:
#	Generate a list of packages to be installed. Remove from the previously
#	generated $pkglist any packages that have already been patched. This
#	procedure is called only for a patch re-installation.
# Parameters:
#	$1	- package database directory (PKGDB)
#	$2	- patch database directory (PATCHDB)
#	$3	- patch number
# Globals Used:
#	pkglist
# Globals Set:
#	pkglist

function gen_uninstalled_pkgs
{
	pkg=
	for i in $pkglist
	do
		if myshowrev $1 | grep "^Patch:[ 	]*$3" | \
			grep -s $i > /dev/null 2>&1 ; then
			continue
		else
			pkg="$pkg $i"
		fi
	done
	if [ "$pkg" = "" ]
	then
		echo "Patch $3 has already been applied.  See README file for\\c"
		echo " instructions.\nInstallpatch is terminating."
		rm -f /tmp/*.$$
		exit 2
	else
		echo "Re-installing patch $3..."
		echo "" >> $2/$3/log
		echo "Re-installing Patch." >> $2/$3/log
	fi
	pkglist="$pkg"
}


# Description:
#	Check to see if the patch is obsoleted by an earlier patch
# Parameters:
#	none
# Globals used:
#	PKGDB
#	patchbase
#	patchrev
# Globals set:
#	isapplied

function check_if_obsolete
{
	currentdir=$(pwd)
	#
	# Search for patches with the same patch base, but a greater than
	# rev. If an equal to rev, set the isapplied global to "yes"
	#
	oldRevs=
	cd $PKGDB
	oldRevs=$(grep "SUNW_PATCHID=$patchbase" ./*/pkginfo | \
	         sed 's/^.*-\([0-9][0-9]\).*$/\1/' | sort -u)
	if [ "$oldRevs" != "" ]
	then
		oldRevs=$(echo $oldRevs | sort -u)
		for ii in $oldRevs X
		do
			if [ "$ii" = "X" ]
			then
				break;
			fi
			if [ "$ii" = "$patchrev" ]
			then
				isapplied="yes"
				continue
			elif [ "$ii" -gt "$patchrev" ]
			then
				print_obsolete_msg "$patchbase-$ii"
				exit 6
			fi
		done
	fi
	#
	# Now search for patches that specifically obsolete the current 
	# patch.  Ignore if the patchpase of the obsoletor is the same 
	# as the obsoletee.
	#
	if myshowrev $PKGDB | grep -v "Patch: $patchbase" | \
		grep -s "Obsoletes:.*$patchbase.*Packages:" > /dev/null 2>&1
	then
		print_obsolete_msg
		myshowrev $PKGDB | grep -v "Patch: $patchbase" | \
			grep "Obsoletes:.*$patchbase.*Packages:"
		exit 6
	fi
	cd $currentdir
}

# Description:
# 		CURRENTLY DISABLED
# Parameters:
#		none
#
function check_if_server
{
	echo > /dev/null
      # if grep -s "SPOOLED_ROOT" $SOFTINFO/$prodver > /dev/null 2>&1
      # then
      #		is_server="yes"
      # fi
}

# Description:
#	Determine if the patch contains any symbolic links. If so, die with
#	an error and a message to the user. I assume the patch will be tested
#	at least once in-house before getting to a non-sun user, so an
#	external user should NEVER see a symbolic link message.
# Parameters:
#	None
# Globals Set:
#	None.
# Globals Used:
#	$patchdir
#
function check_for_symbolic_link
{
	rm -f /tmp/symlink.$$ > /dev/null 2>&1
	olddir=$(pwd)
	cd $patchdir
	for ii in * X
	do
		if [ "$ii" = X ]
		then
			break
		fi
		if [ ! -d "$ii" ]
		then
			continue
		fi
		#
		# Comment out ignoring symbolic links for packages with no current
		# instance. New packages will not be added using installpatch.
		#
		# grep -s "VERSION=.*PATCH=" $1/$2/$ii/pkginfo
		# if [ $? != 0 ]; then
		# 	continue
		# fi
		symlinks=
		symlinks=`sed -n '/^[^ 	]*[ 	]*s[ 	]/p' $1/$2/$ii/pkgmap`
		if [ "$symlinks" != "" ]; then
			echo "Symbolic link in package $ii" >> /tmp/symlink.$$
		fi
	done
	if [ -s /tmp/symlink.$$ ]
	then
		echo
		cat /tmp/symlink.$$
		echo
		echo "Symbolic links can't be part of a patch."
		echo "Installpatch is terminating."
		rm -f /tmp/*.$$
		exit 13
	fi
	cd $olddir
}

# Description:
#	Find package instance of originally-installed package. Extract the
#	PKGID, ARCH, and VERSION by scanning the pkginfo files of each patch
#	package. Check to see if the packages that are being patched were 
#	actually installed on the system in the first place.
# Parameters:
#	$1	- package database directory
#	$2	- patch directory
# Globals Set:
#	pkglist
#	is_a_root_pkg
# Globals Use:
#	pkglist

function check_pkgs_installed
{
	i=
	j=
	pkginst=
	finalpkglist=
	minver=
	Pkgpatchver=
	Pkgarch=
	Pkgabbrev=
	Pkgver=
	Pkgtype=
	OrigPkgver=
	#
	# Search the installed pkginfo files for matches with the list 
	# of packages to be patched.  The package names are listed in 
	# global pkglist.  These names correspond to the package database 
	# subdirectory names.
	#

	for i in $pkglist
	do
		#
		# Get the package abbreviation from the pkginfo file.
		#
		Pkgabbrev=$(sed -n 's/^[ 	]*PKG[ 	]*=[ 	]*\([^ 	]*\)[	 ]*$/\1/p' $i/pkginfo)
		#
		# Get the package architecture from the pkginfo file.
		#
		Pkgarch=$(sed -n 's/^[ 	]*ARCH[ 	]*=[ 	]*\([^ 	]*\)[	 ]*$/\1/p' $i/pkginfo)
		#
		# Get the package version number.
		#
		Pkgver=$(sed -n \
		   -e 's/^[ 	]*VERSION[ 	]*=[ 	]*\([^ 	]*\)\.[0-9][0-9]*[ 	]*$/\1/p' \
		   -e 's/^[ 	]*VERSION[ 	]*=[ 	]*\([^ 	]*\),PATCH=.*$/\1/p' $i/pkginfo )
		minver=$(expr $Pkgver : '\(.*\)\.0$')
		while [ "$minver" != "" ]
		do
		        Pkgver=$minver
		        minver=$(expr $Pkgver : '\(.*\)\.0$')
		done
		Pkgpatchver=$(sed -n 's/^[ 	]*VERSION[ 	]*=[ 	]*\([^ 	]*\)[^ 	]*$/\1/p' $i/pkginfo)
		Pkgtype=$(sed -n 's/^[ 	]*SUNW_PKGTYPE[ 	]*=[ 	]*\([^ 	]*\)[	 ]*$/\1/p' $i/pkginfo)
		if [ "$Pkgtype" = "root" ]
		then
			is_a_root_pkg=yes
		fi
		#
		# Determine if this is a new package to be installed.  If so,
		# add the package to the final package list and force it to 
		# be pkgadd`ed even if there is no current instance.
		#
		# This capability is commented out.  New packages are not to 
		# be installed by installpatch.
		#
		# newpkg="yes"
		# if grep -s "VERSION=.*PATCH=" $2/$i/pkginfo > /dev/null 2>&1
		# then
		# 	newpkg="no"
		# fi
		# if [ "$newpkg" = "yes" ]; then
		# 	finalpkglist="$finalpkglist $i,NO_CURRENT_INSTANCE"
		# 	continue;
		# fi
		# echo ""
		for j in $1/$Pkgabbrev X
		do
			if [ "$j" = "X" ]
			then
				break
			fi
			if [ ! -d "$j" ] && [ ! -d "$j.*" ]
			then
				echo "Package not patched:" >> $LOGFILE
				echo "PKG=$Pkgabbrev" >> $LOGFILE
				echo "Original package not installed" >> $LOGFILE
			fi
		done
		for j in $1/$Pkgabbrev $1/$Pkgabbrev.* X
		do
			if [ "$j" = "X" ]
			then
				break
			fi
			if [ ! -d $j ]
			then
				continue;
			fi
			OrigPkgver=$(sed -n 's/^VERSION=\(.*\)$/\1/p' $j/pkginfo)
			minver=$(expr $OrigPkgver : '\(.*\)\.0$')
			while [ "$minver" != "" ]
			do
				OrigPkgver=$minver
				minver=$(expr $OrigPkgver : '\(.*\)\.0$')
			done
			if grep -s "^PKG=$Pkgabbrev$" $j/pkginfo >/dev/null 2>&1 \
				&& grep -s "^ARCH=$Pkgarch$" $j/pkginfo >/dev/null 2>&1 \
				&& [ "$OrigPkgver" = "$Pkgver" ] ;
			then
				pkginst=$(basename $j)
				finalpkglist="$finalpkglist $i,$pkginst"
				break;
			else
				echo "Package not patched:" >> $LOGFILE
				echo "PKG=$Pkgabbrev" >> $LOGFILE
				echo "ARCH=$Pkgarch" >> $LOGFILE
				echo "VERSION=$OrigPkgver" >> $LOGFILE
				tmp=""
				tmp=$(grep "^ARCH=$Pkgarch$" $j/pkginfo 2>/dev/null)
				if [ "$tmp" = "" ]
				then
					echo "Architecture mismatch" >> $LOGFILE
				fi
				if  [ "$OrigPkgver" != "$Pkgver" ]
				then
					echo "Version mismatch" >> $LOGFILE
				fi
				echo "" >> $LOGFILE
			fi
		done
		#
		#  If j is X, matching package instance was never found.  If
		#  in force mode, add package to list anyway.
		# if [ "$j" = "X"]; then
		#	if [ "$force" = "yes" ] ; then
		#        	finalpkglist="$finalpkglist $i,NO_CURRENT_INSTANCE"
		#	fi
		# fi
		# if [ "$is_server" = "yes" ]; then
		#        if [ "$Pkgarch" = "sparc" ]; then
		#                Pkgarch="sparc.all"
		#        fi
		#        org_template=$Pkgabbrev"_"$Pkgver"_"$Pkgarch
		#        if grep "SPOOLED_ROOT=$Pkgarch:/export/root/templates/$prodver/$org_template" $SOFTINFO/$prodver >/dev/null 2>&1
		#        then
		#                spoolsize=`/usr/bin/du -ks $i | sed 's/ .*//'`
		#                rootlist="$rootlist $i,$Pkgabbrev"_"$Pkgpatchver"_"$Pkgarch",$spoolsize
		#        fi
		# fi
	done
	pkglist=$finalpkglist
}

# Description:
#	If validation is being done, and pkgchk reported ERRORs, bail out.
#	If no validation is being done, keep a list of files that failed
#	validation. If this patch needs to be backed out, don't do an installf
#	on these files. Any files that failed validation before the patch was
#	applied should still fail validation after the patch is backed out.
#	This will be the .validation.errors file in the patch directory.
# Parameters:
#	$1	- validation status [ "yes" or "no" ]
# Globals Used:
#	PKGCOFILE
#	VALERRFILE

function check_validation
{
	if [ "$1" = "yes" -a -s $PKGCOFILE ]
	then
		if grep -s ERROR $PKGCOFILE >/dev/null 2>&1
		then
			echo "The following validation error was found: \n"
			cat $PKGCOFILE
			echo
			echo "See the README file for instructions regarding"
			echo "patch validation errors."
			echo "Installpatch is terminating."
			rm -f /tmp/*.$$
			exit 10
		fi
			
	fi
	if [ -s $VALWARNFILE ]
	then
		cp $VALWARNFILE $VALERRFILE
	fi
}

# Description:
# 	Create a spooling area in the sadm/patch/<patchID> tree for files
# 	which are being replaced by the patch. Store the validation error
# 	file with it.
# Parameters:
#	$1	- patch database directory
#	$2	- patch number
# Globals Used:
#	VALERRFILE

function create_archive_area
{
	if [ ! -d $1/$2/save ]
	then
		echo "Creating patch archive area..."
		mkdir -p -m 750 $1/$2/save
		chown -h -f -R root $1/$2
		chgrp -h -f -R sys $1/$2
	fi
	if [ -s $VALERRFILE ]
	then
		cp $VALERRFILE $1/$2/.validation.errors
	fi
}

# Description:
#	Scan the patch package maps for a list of affected files.
# Parameters:
#	$1	- package database directory
#	$2	- package relocation argument
# Globals Used:
#	PKGCOFILE
#	PATCHFILES
#	pkglist
#
function gen_install_filelist
{
	pkgfiles=/tmp/pkgfiles.$$
	resfiles=/tmp/resolvedfiles.$$
	macrofiles=/tmp/pkgmacros.$$
	pkginst=
	pkginfofile=
	patchpkg=
	basedir=
	i=
	rm -f $PATCHFILES
	echo "Generating list of files to be patched..."
	for i in $pkglist
	do
		patchpkg=${i%%,*}
		pkginst=${i##*,}

		if [ "$pkginst" = "NO_CURRENT_INSTANCE" ]
		then
			#
			# Install a package to be patched if it isn't 
			# currently installed????
			#
			pkginfofile="$patchpkg/pkginfo"
			pkgmapfile="$patchpkg/pkgmap"
		else
			pkginfofile="$1/$pkginst/pkginfo"
			pkgmapfile="$1/$pkginst/pkgmap"
		fi
		#
		# parse out the base directory from the pkginfo file.  First, 
		# remove the BASEDIR keyword together with the = and any 
		# whitespace.  Then remove any / at the end of the line. 
		# Finally, replace any /a partitions with /
		#
		basedir=$(grep '^BASEDIR' $pkginfofile | \
		    	sed -e 's@.*=\ *@@' -e 's@/$@@' -e 's@/a$@/@')
		#
		# Parse out the pkgmap files to get the file names.  First, get
		# rid of all checksum info. Then get rid of all info file entries.
		# Replace all BASEDIR values with emptiness (BASEDIR will be
		# prepended).  Delete all entries that are the BASEDIR without a
		# file (directory entries).  Get the file name. If it's a symbolic
		# link, keep the link, don't follow it to the file.
		#
		sed -e '/^:/d' \
		    -e '/^[^ ][^ ]* i/d' \
		    -e 's, \$BASEDIR/, ,' \
		    -e '/ \$BASEDIR /d' \
		    -e 's/^[^ ]* . \([^ ]*\) \([^ ]*\).*$/\2 \1/' \
		    -e 's/=.*//' $patchpkg/pkgmap > $pkgfiles
		#
		# Resolve any macros in the list of files before determining if
		# the file is relocatable.
		#
		if [ -s $pkgfiles ]
		then
			# resolve any macros in the list of files
			(
			rm -f $macrofiles $resfiles
			cat $pkginfofile |
			while read i
			do
				echo $(echo $i| sed -e 's/^\(.*\)=.*/\1/')=\"$(echo $i|sed -e 's/^\(.*\=\)//')\" | grep -v '^PATH' >> $macrofiles
			done
			. $macrofiles
			cat $pkgfiles |
			while read i
			do
				eval /usr/bin/echo $i >> $resfiles
			done
			)
			#
			# Prepend the basedir to the file name if the file is
			# relocatable, then add it to the pkgfile list. 
			#
			mv $resfiles $pkgfiles
			cat $pkgfiles | parse_sizes $patchpkg

			sed "s,^\([^/]\),$basedir/&," $pkgfiles > $resfiles
			#
			# If there are some files to patch in the package, see if 
			# they have validation errors. Ignore any validation errors 
			# for files having class action scripts. The remaining 
			# validation errors will be put in a validation error file.
			#
			if [ -s $resfiles ]
			then
				cat $resfiles |
				while read j
				do
					jfile=$(echo $j | sed 's/^\([^ ]*\).*/\1/')
					class=$(echo $j | sed 's/^[^ ]* \(.*\)/\1/')
					badfile=
					badfile=$(pkgchk $2 -p $jfile\
						$patchpkg 2>&1 | \
						grep "^ERROR:" | \
						sed -n 's/^ERROR:[ 	]*//p')
					if [ "$badfile" != "" ]
					then
						if [ "$class" != "" -a "$class" != "preserve" -a ! -f $patchdir/$patchpkg/install/i.$class ]
						then
							pkgchk $2 -p $jfile\
							$patchpkg >> $PKGCOFILE 2>&1
						fi
						echo $jfile >> $VALWARNFILE
					fi
				done
			fi
		 	sed 's/^\([^ ]*\).*/\1/' $resfiles >> $PATCHFILES
		fi
	done
}

# 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:
#	$1	- patch 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
	while read Filename junk
	do
		grep " $Filename " $1/pkgmap  |
		while read part ftype f3 f4 f5 f6 f7 f8 Junk
		do
			case $ftype in
				f|e|v)
					pathname=$f4
					size=$f8
					case $pathname in
						usr\/openwin\/*|\/usr\/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)
					pathname=$f4
					case $pathname in
						usr\/openwin\/*|\/usr\/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
	done
}

# Description:
#	Generate a list of files which are "to be patched." Determine their
#	total size in bytes to figure out the space requirements of backing
#	them up.
# Parameters:
#	none
# Globals Used:
#	PATCHFILES
#	EXISTFILES
#	KBYTESFILE
#
function gen_patch_filelist
{
	typeset -i tmp_total=0
	typeset -i kbytes_total=0
	typeset -i kb=0
	size=

	if [ -s $PATCHFILES ]
	then
		cat $PATCHFILES |
		while read j
		do
			if ls -d $ROOTDIR$j >/dev/null 2>&1
			then
				echo "."$j >> $EXISTFILES
				size=$(wc -c $ROOTDIR$j)
				size=$(echo $size | sed 's/\ .*//')
				if [ "$size" != "" ]
				then
					tmp_total=tmp_total+$size
				fi
				if (( tmp_total >= 1024 ))
				then
					kb=tmp_total/1024
					tmp_total=tmp_total-kb*1024
					kbytes_total=kbytes_total+kb
				fi
			fi
		done;
		if (( tmp_total > 0 ))
		then
			kbytes_total=kbytes_total+1
		fi
		Kbytes_Required=kbytes_total
	else
		rm -f $EXISTFILES
	fi
}

# Description:
# 	Assemble a list of the package package IDs contained in the patch
#	(at least one diretory with a pkginfo file must exist due to checks
#	in activate_patch)
# Parameters:
#	none
# Globals Set:
#	pkglist
#
function gen_patchpkg_list
{
	pkg=
	for i in */pkginfo X
	do
		if [ "$i" = "X" ]
		then
			break
		fi

		pkg=${i%%/*}
		pkglist="$pkglist $pkg"
	done
}

# Description:
#	Get the product version <name>_<version> of local Solaris installation
# Parameters:
#	$1	- softinfo directory path
# Globals Set:
#	prodver
#
function get_os_version 
{
	if [ ! -f ${1}/INST_RELEASE ]
	then
		echo "$0 is unable to find the INST_RELEASE file.  This file"
		echo "must be present for $0 to function correctly."
		exit 11
	fi
	Product=
	Instver=
	Product=$(sed -n 's/^OS=\(.*\)/\1/p' $1/INST_RELEASE)
	Instver=$(sed -n 's/^VERSION=\(.*\)/\1/p' $1/INST_RELEASE)
	prodver=$Product"_"$Instver
}

# Description:
# 	Actually install patch packages which apply to the system
# Parameters:
#	$1	- patch database directory
#	$2	- patch number
#	$3	- patch directory
#	$4	- package add relocation argument
#	$5	- package database directory
# Globals Used:
#	ADMINTFILE
#	ADMINFILE
#	pkglist
#
function install_patch_pkgs
{
	typeset -i pkgadderr=0
	typeset -i real_pkgadderr_2=0

	i=
	ij=
	pkginst=
	pkginfofile=
	patchpkg=
	basedir=
	#
	#	Write out the contents of the logfile if there were any
	#	messages. Do this now, because the $1/$2 directory may not
	#	exist before this point.
	#
	if [ -f $LOGFILE ]
	then
		cat $LOGFILE >> $1/$2/log
		rm -f $LOGFILE
	fi

	move_libraries

	echo "Installing patch packages..."
	for ij in $pkglist
	do
		i=${ij%%,*}
		pkginst=${ij##*,}
		if [ "$pkginst" = "NO_CURRENT_INSTANCE" ]
		then
			pkginfofile="$3/$i/pkginfo"
		else
			pkginfofile="$5/$pkginst/pkginfo"
		fi
		basedir=$(grep '^BASEDIR' $pkginfofile | sed -e 's@.*=\ *@@' -e 's@/a/@/@' -e 's@/a$@/@')
		if [ ! -d $1/$2/$i ]
		then
			mkdir -m 750 $1/$2/$i
		fi
		cp $i/pkgmap $1/$2/$i/pkgmap
		cp $i/pkginfo $1/$2/$i/pkginfo
 		cp $ADMINTFILE $ADMINFILE
		echo basedir=$basedir >>$ADMINFILE
	
		echo "\nDoing pkgadd of $i package:"
		pkgadd $4 -S -a $ADMINFILE -n -d $3 $i >$LOGFILE 2>&1
		pkgadderr=$?

		real_pkgadderr_2=0
		if (( pkgadderr == 2 ))
		then
			if grep '^ERROR' $LOGFILE >/dev/null 2>&1
			then
				real_pkgadderr_2=1
			fi
		fi

		cat $LOGFILE >> $1/$2/log
		cat $LOGFILE | grep -v "^$"
		rm -f $LOGFILE
		if (( pkgadderr != 0 && real_pkgadderr_2 != 0 && \
		      pkgadderr != 10 && pkgadderr != 20 ))
		then
			echo "Pkgadd of $i package failed with error code $pkgadderr." |tee -a $1/$2/log
			if [ "$isapplied" = "no" ]
			then
				echo "See /tmp/log.$2 for reason for failure."
				cp $1/$2/log /tmp/log.$2
				echo "Backing out patch:"
				cd $3
				if [ "$ROOTDIR" != "/" ]
				then
					./backoutpatch $PKGDBARG $2
				else
					./backoutpatch $2
				fi
			else
				echo "See $1/$2/log for reason for failure."
				echo "Will not backout patch...patch re-installation."
				echo "Warning: The system may be in an unstable state!"
			fi
			echo "Installpatch is terminating."
			rm -fr /tmp/*.$$
			remove_libraries
			exit 5
		fi
	done
	remove_libraries
}

# Description:
#	Make internal variables available to child processes
#   of installpatch.  This is done by writing them to a
#   file and by exporting them.
# Parameters:
#	none
# Environment Variables Set:
#	none
#
function make_params_available
{
	echo "saveold=$saveold" > $PARAMS_FILE
	echo "validate=$validate" >> $PARAMS_FILE
	echo "patchdir=$patchdir" >> $PARAMS_FILE
	echo "patchnum=$patchnum" >> $PARAMS_FILE
	echo "patchbase=$patchbase" >> $PARAMS_FILE
	echo "patchrev=$patchrev" >> $PARAMS_FILE
	echo "ROOTDIR=$ROOTDIR" >> $PARAMS_FILE
	echo "PATCHDB=$PATCHDB" >> $PARAMS_FILE
	echo "PKGDB=$PKGDB" >> $PARAMS_FILE
	echo "SOFTINFO=$SOFTINFO" >> $PARAMS_FILE
	echo "PKGDBARG=$PKGDBARG" >> $PARAMS_FILE

	export saveold validate patchdir patchnum patchbase patchrev
	export PARAMS_FILE ROOTDIR PATCHDB PKGDB SOFTINFO PKGDBARG
}

# Description:
#	Copy required libraries to TMP_LIB_DIR, set and
#	export LD_PRELOAD.
# Parameters:
#	none
# Environment Variables Set:
#	LD_PRELOAD
#
function move_libraries
{
	typeset -i Rev=0

    Rev=$(echo $Instver | sed -e 's/[0-9]\.//')
    if (( Rev >= 5 ))
    then

        if [ ! -d $TMP_LIB_DIR ]
        then
            mkdir -p -m755 $TMP_LIB_DIR
        fi

        LD_PRELOAD=
        for Lib in libc libdl libelf libintl libw libadm
        do
            cp /usr/lib/${Lib}.so.1 ${TMP_LIB_DIR}/${Lib}.so.1

            chown bin ${TMP_LIB_DIR}/${Lib}.so.1
            chgrp bin ${TMP_LIB_DIR}/${Lib}.so.1
            chmod 755 ${TMP_LIB_DIR}/${Lib}.so.1

            LD_PRELOAD="${LD_PRELOAD} ${TMP_LIB_DIR}/${Lib}.so.1"
        done
        export LD_PRELOAD
    fi
}


# Description:
# 	Parse the arguments and set all affected global variables
# Parameters:
#	Argument list passed into installpatch 
# Globals Set:
#	validate
#	saveold
#	force
#	printpatches
#	patchdir
#	ROOTDIR
#	PATCHDB
#	PKGDB
#	SOFTINFO
#	PKGDBARG
# Globals Used:
#	prodver
#	SOFTINFO
#	PKGDB
#	PATCHDB
#
function parse_args
{
	service_specified="n"
	rootdir_specified="n"
	while [ "$1" != "" ]
	do
		case $1 in
		-u)	validate="no"; shift;;
		-d)	saveold="no"; shift;;
		-p)	printpatches="yes"; shift;;
		-S)	shift
			if [ "$rootdir_specified" = "y" ]
			then
				echo "The -S and -R arguments are mutually exclusive."
				print_usage
				exit 1
			fi
			get_os_version $SOFTINFO
			if [ "$1" != "$prodver" ]
			then
				if [ -d "/export/$1$PKGDB" ]
				then
					ROOTDIR=/export/$1
					PATCHDB=$ROOTDIR$PATCHDB
					PKGDB=$ROOTDIR$PKGDB
					SOFTINFO=$ROOTDIR$SOFTINFO
					PKGDBARG="-R $ROOTDIR"
					service_specified="y"
				else
					echo "The $1 service cannot be found on this system."
					print_usage
					exit 1
				fi
			fi
			shift;;
		-V) echo "@(#) installpatch 5.0 95/09/07"
			exit 0;;
		-R)	shift
			if [ "$service_specified" = "y" ]
			then
				echo "The -S and -R arguments are mutually exclusive."
				print_usage
				exit 1
			fi
			if [ ! -d "$1" ]
			then
				echo "The Package Install Root directory $1 cannot be found on this system."
				print_usage
				exit 1
			else
				ROOTDIR=$1
				PATCHDB=$ROOTDIR$PATCHDB
				PKGDB=$ROOTDIR$PKGDB
				SOFTINFO=$ROOTDIR$SOFTINFO
				PKGDBARG="-R $ROOTDIR"
				rootdir_specified="y"
			fi
			shift;;

		-*)	print_usage; exit 1;;
		 *)	break;;
		esac
	done
	if [ "$printpatches" = "yes" ]
	then
		myshowrev $PKGDB
		exit 0
	fi
	if [ "$1" = "" ]
	then
		echo "No patch directory specified."
		print_usage
		exit 1
	fi
	if [ ! -d $1 ]
	then
		echo "Patch directory '$1' does not exist."
		print_usage
		exit 1
	fi
	#
	#	Derive the full path name from a (possibly) relative path name.
	#
	olddir=$(pwd)
	cd $1
	patchdir=$(pwd)
	cd $olddir
	echo "@(#) installpatch 5.0 95/09/07 SMI"
}

# Description:
#	Scan all pkginfo files for installed packages for a PATCH
#	identifier string and print out the associated package info
# Parameters:
#	$1 	- patch database directory
#
# Description:
#	Print the patch obsolecensce message
# Parameters:
#	$1	- number of patch which obsoleted this patch
#
function print_obsolete_msg
{
	echo "This patch is obsoleted by patch $1 which has already"
	echo "been applied to this system. Patch installation is aborted."
}

# Description:
#	Print the list of patch packages which were applied, and those
#	which were not.
# Parameters:
#	none
# Globals Used:
#	pkglist
#
function print_results
{
	i=
	p=
	echo "\nPatch packages installed:"
	if [ -s "$pkglist" ]
	then
		echo "		none"
	else
		for i in $pkglist
		do
			echo "	${i%%,*}"
		done
	fi
}

# Description:
#	remove the TMP_LIB_DIR directory
# Parameters:
#	none
# Environment Variables Set:
#	LD_PRELOAD
#
function remove_libraries
{
	LD_PRELOAD=
	export LD_PRELOAD
	rm -rf $TMP_LIB_DIR
}

# Description:
# 	Archive files which will be overwritten by the patch application,
#	if the patch actually affects any existing files.
# Parameters:
#	$1	- patch database directory
#	$2	- patch number
#	$3	- patch directory
#	$4	- save old files [ "yes" or "no" ]
# Globals Used:
#	EXISTFILES
#	KBYTESFILE
#
function save_overwritten_files
{
	typeset -i exit_code=0
	typeset -i kbytes_avail=0
	olddir=

	kbytes_required=
	bytes=
	if [ ! -s $EXISTFILES ]
	then
		touch $1/$2/.nofilestosave
	elif [ "$4" = "yes" ]
	then
		echo "Saving a copy of existing files to be patched..."
		# Is there enough space? Use sed to extract the fourth field of
		# df output (can't use awk because it may not be installed).
		kbytes_avail=$(df -k $1 | tail -1 | \
		  sed 's/^[^ 	]*[ 	]*[^ 	]*[ 	]*[^ 	]*[ 	]*\([^ 	]*\).*/\1/')

		if (( Kbytes_Required > kbytes_avail ))
		then
			echo "Insufficient space in $1 to save old files."
			echo "Space required in kilobytes:  $kbytes_required"
			echo "Space available in kilobytes:  $kbytes_avail"
			if [ "$isapplied" = no ]
			then
				cd $3
				if [ "$ROOTDIR" != "/" ]
				then
					./backoutpatch $PKGDBARG $2
				else
					./backoutpatch $2
				fi
				rm -fr $1/$2
			fi
			rm -f /tmp/*.$$
			exit 4
		fi
		cd $ROOTDIR
		if [ "$isapplied" = "no" ]
		then
			cpio -oL -O $1/$2/save/archive.cpio <$EXISTFILES
			exit_code=$?
		else
			if [ ! -d $TMP_ARCHIVE ]
			then
				mkdir $TMP_ARCHIVE
			fi

			cd $TMP_ARCHIVE
			if [ -f $1/$2/save/archive.cpio.Z ]
			then
				zcat $1/$2/save/archive.cpio.Z | cpio -idum
			else
				cpio -idum -I $1/$2/save/archive.cpio
			fi
			find . -print > $TMP_FILELIST

			cd $ROOTDIR
			cpio -oL -O /tmp/archive.cpio < $EXISTFILES >/dev/null 2>&1
			exit_code=$?

			cd $TMP_ARCHIVE
			cpio -oAL -O /tmp/archive.cpio < $TMP_FILELIST >/dev/null 2>&1
			exit_code=exit_code+$?

			cd $ROOTDIR
			rm -rf $TMP_ARCHIVE/* $TMP_FILELIST
			rmdir $TMP_ARCHIVE
		fi
		if (( exit_code != 0 ))
		then
			echo "Save of old files failed. \c"
			echo "See README file for instructions."
			if [ "$isapplied" = "no" ]
			then
				cd $3
				if [ "$ROOTDIR" != "/" ]
				then
					./backoutpatch $PKGDBARG $2
				else
					./backoutpatch $2
				fi
				rm -fr $1/$2
			fi
			echo "Installpatch is terminating."
			rm -f /tmp/*.$$
			exit 4
		fi
		if [ -x /usr/bin/compress ]
		then
			if [ "$isapplied" = "no" ]
			then
				compress $1/$2/save/archive.cpio
			else
				compress /tmp/archive.cpio
			fi
			if [ $? = 0 ]
			then
				echo "	File compression used"
			else
				echo "	No file compression used."
			fi
		else
			echo "	No file compression used."
		fi
		if [ "$isapplied" = "yes" ]
		then
			cp /tmp/archive.cpio* $1/$2/save
		fi
		chmod 600 $1/$2/save/archive.cpio*
		touch $1/$2/.oldfilessaved
	fi
	cd $3
}

# Description:
#	Finish up the patch
# Parameters:
#	$1	- patch database directory
#	$2	- patch number
#
function set_patch_status
{
	mv -f /tmp/ACTION.$patchnum $1/$2 >/dev/null 2>&1
	cp -p README.$2 backoutpatch $1/$2 >/dev/null 2>&1
	cp -p prebackout postbackout $1/$2 > /dev/null 2>&1
	rm -f $MYSHOWREV_FILE
	rm -f /tmp/*.$$
	rm -f /tmp/archive.cpio*
}

# Description:
# 	Spool a copy of the patch into the client root template area
#			NOT IMPLEMENTED AT THIS TIME
# Parameters:
#	none
#
function spool_client_patch
{
	echo > /dev/null
      #	if [ "$is_server" = "yes" ]
      #	then
      #		echo "Spooling patch packages to root templates for future clients"
      #		echo "[NOTE: existing clients will need patches applied individually]"
      #		for i in $rootlist; do
      #			patchpkg=`expr $i : '\([^,]*\),.*'`
      #			spoolloc=`expr $i : '[^,]*,\([^,]*\),.*'`
      #			spoolsize=`expr $i : '[^,]*,[^,]*,\(.*\)'`
      #			arch=`expr $spoolloc : '[^_]*_[^_]*_\(.*\)'`
      #			ver=`expr $spoolloc : '[^_]*_\([^_]*\)_.*'`
      #			abbrev=`expr $spoolloc : '\([^_]*\)_.*'`
      #			echo $patchpkg $spoolloc $spoolsize $arch $ver $abbrev
      #	
      #			if [ ! -d /export/root/templates/$prodver/$spoolloc ]; then
      #				mkdir -m 750 -p /export/root/templates/$prodver/$spoolloc
      #			fi
      #			find $patchpkg -print | cpio -pd /export/root/templates/$prodver/$spoolloc
      #			if [ $? != 0 ]; then
      #				echo "Error while adding patch to root template."
      #				echo "See README file for instructions."
      #				rm -fr /export/root/templates/$prodver/$spoolloc
      #				cd $patchdir
      #				./backoutpatch -s $patchnum
      #				echo "Installpatch is terminating."
      #				rm -fr $PATCHDB/$patchnum
      #				rm -f /tmp/*.$$
      #				exit 11
      #			fi
      #
      #			echo SPOOLED_ROOT=$arch":"/export/root/templates/$prodver/$spoolloc >> $SOFTINFO/$prodver
      #			echo ROOT=$arch:$abbrev,$spoolsize,$ver >> $SOFTINFO/$prodver
      #			echo /SPOOLED_ROOT=$arch":".export.root.templates.$prodver.$spoolloc/d >> $PATCHDB/$patchnum/softinfo_sed
      #			echo /ROOT=$arch:$abbrev,$spoolsize,$ver/d >> $PATCHDB/$patchnum/softinfo_sed
      #			echo "/export/root/templates/$prodver/$spoolloc" >> $PATCHDB/$patchnum/spooled_dirs
      #		done
      #	fi
}

# Description:
# Parameters:
#	$1	- patch database directory
#	$2	- patch number
#	$3	- patch directory
#
function trap_backoutsaved
{
	echo "Interrupt signal detected."
	if [ "$isapplied" = "no" ]
	then
		echo "Backing out patch:"
		cd $3
		if [ "$ROOTDIR" != "/" ]
		then
			./backoutpatch $PKGDBARG $2
		else
			./backoutpatch $2
		fi
		rm -fr $1/$2
	else
		cp /tmp/archive.cpio* $1/$2/save
		rm -f /tmp/archive.cpio*
		echo "Installpatch Interrupted." >> $1/$2/log
	fi
	echo "Installpatch is terminating."
	rm -f /tmp/*.$$
	rm -f $MYSHOWREV_FILE
	remove_libraries
	exit 12
}

# Description:
# Parameters:
#	$1	- patch directory
#	$2	- patch number
#
function trap_backout
{
	echo "Interrupt signal detected."
	echo "Backing out Patch:"
	cd $1
	if [ "$ROOTDIR" != "/" ]
	then
		./backoutpatch $PKGDBARG $2
	else
		./backoutpatch $2
	fi
	if [ "$isapplied" = "yes" ]
	then
		rm -f /tmp/archive.cpio*
	fi
	echo "Installpatch is terminating."
	rm -f /tmp/*.$$
	rm -f $MYSHOWREV_FILE
	remove_libraries
	exit 12
}

# Description:
# Parameters:
# 	$1	- patch database directory
#	$2	- patch number
#
function trap_notinstalled
{
	echo "Interrupt signal detected. Patch not installed."
	echo "Installpatch is terminating."
	rm -f /tmp/*.$$
	rm -f $MYSHOWREV_FILE
	if [ "$isapplied" = "no" ]
	then
		rm -fr $1/$2
	else
		echo "Install Interrupted." >> $1/$2/log
	fi
	exit 12
}

# Description:
#	 Make sure effective UID is '0'
# Parameters:
#	none
#
function validate_uid
{
	typeset -i uid
	uid=$(id | sed 's/uid=\([0-9]*\)(.*/\1/')
	if (( uid != 0 ))
	then
		echo "You must be root to execute this script."
		exit 3
	fi
}

# Description:
#	Assume that any system on which the SUNWcsu package is NOT
#	installed is a client. It is a safe bet that this criterion
#	will remain valid through Solaris 2.3. Later releases may require
#	that this test be changed. Make sure pkgadd is executable too.
# Parameters:
#	none
# Globals Set:
#	client
#
function verify_client
{
	pkginfo -q SUNWcsu
	if [ $? != 0 ]
	then
		client=yes
		sum /usr/sbin/pkgadd > /dev/null 2>&1
		if [ $? != 0 ]
		then
			echo "The /usr/sbin/pkgadd command is not executable."
			echo "See the README file for instructions for making this"
			echo "command executable."
			exit 9
		fi
	fi
}


############################################
#		Main Routine		   #
############################################

#
# -	Get the product version <name>_<version> of local Solaris
#	installation (sets the prodver global variable)
# -	Parse the argument list and set globals accordingly
# -	Make sure the user is running as 'root'
#
echo
Cmd=$0
parse_args $*
validate_uid
get_os_version "$SOFTINFO"

#
# Change to the patch directory and set globals according to the patchID
# found in the pkginfo files of the patch packages
#
rm -f $MYSHOWREV_FILE
activate_patch "$patchdir"

#
# -	Check to see if patch has already been applied, and exit if already
# 	applied successfully
# -	Check whether patch has been obsoleted by a prior patch
# -	Determine if system being patch is a client, and make sure pkgadd
#	is usable servers must export to clients for read-only root access)
# -	Determine if system being patched is a server (CURRENTLY DISABLED)
#
# -	Make sure the patch doesn`t contain any symbolic links. If it does,
#	print out an error. Sustaining engineering will need to re-engineer
#	the patch to replace a symbolic link in one of the pre or post install
#	scripts.
#
check_if_applied "$PATCHDB" "$patchnum"

#
#   prev  | curr | .nofilestosave | Is it OK to |  How to verify previous
#   save  | save |    exist?      |  continue?  |    save/no_save state
#  -------+------+----------------+-------------+-----------------------------
#         |      |      yes       |             | a. empty save directory
#  1. yes | yes  |----------------|  continue   +-----------------------------
#         |      |       no       |             | b. NON empty save directory
#  -------+------+----------------+-------------+-----------------------------
#         |      |      yes       |  continue   | a. empty save directory
#  2. yes |  no  |----------------+-------------+-----------------------------
#         |      |       no       |  terminate  | b. NON empty save directory
#  -------+------+----------------+-------------+-----------------------------
#  3.  no |  no  |       no       |  continue   |    empty save directory
#  -------+------+----------------+-------------+-----------------------------
#  4.  no | yes  |       no       |  terminate  |    empty save directory
#  -------+------+----------------+-------------+-----------------------------
#
if [ "$isapplied" = "yes" ]
then
    if [ "$saveold" = "no" ]
    then
        if [ ! -f "${PATCHDB}/${patchnum}/.nofilestosave" -a \
            \( -f "${PATCHDB}/${patchnum}/save/archive.cpio" -o \
               -f "${PATCHDB}/${patchnum}/save/archive.cpio.Z" \) ]
        then
            # condition #2b - terminate
            echo "A previous installation of patch $patchnum was invoked"
            echo "that saved files that were to be patched.  Since files"
            echo "were saved, you must run this instance of installpatch"
            echo "apply this patch WITHOUT the -d option."
            exit 17
        elif [ -f "${PATCHDB}/${patchnum}/.nofilestosave"     -a \
              ! -f "${PATCHDB}/${patchnum}/save/archive.cpio" -a \
              ! -f "${PATCHDB}/${patchnum}/save/archive.cpio.Z"   ]
        then
            # condition #2a - rm .nofilestosave
            rm ${PATCHDB}/${patchnum}/.nofilestosave
        fi
    else
        if [ ! -f "${PATCHDB}/${patchnum}/.nofilestosave"    -a \
             ! -f "${PATCHDB}/${patchnum}/save/archive.cpio" -a \
             ! -f "${PATCHDB}/${patchnum}/save/archive.cpio.Z"   ]
        then
            # condition #4 - terminate
            echo "A previous installation of patch $patchnum was invoked"
            echo "with the -d option.  (i.e. Do not save files that would"
            echo "be patched)  Therefore, this invocation of installpatch"
            echo "must also be run with the -d option."
            exit 17
        fi
    fi
fi

check_if_obsolete "$PATCHDB"

verify_client

check_if_server

check_for_symbolic_link "$patchdir"

trap 'trap_notinstalled "$PATCHDB" "$patchnum"' 1 2 3 15

make_params_available

#
#	If there is a prepatch file in the $patchdir directory,
#	execute it. If the return code is not 0, exit installpatch
#	with an error.
#	Don't re-execute a prepatch script if this is a re-installation
#

if [ "$isapplied" = "no" ]; then
	execute_prepatch "$patchdir"
fi

# -	Generate list of patch packages to be installed
# -	Find out those packages which are already installed on
#	the system
# -	Check to see if there is any work to do
# -	Generate list of files to be installed
# -	Check for validation errors
# -	Generate a list of files to be patched that already exist
# -	Create patch archive and save area

gen_patchpkg_list

#	If this is a re-installation of the patch, remove the already installed
#	packages from the package list. If all packages in the patch have already
#	been applied, then exit.
#

if [ "$isapplied" = "yes" ]; then
	gen_uninstalled_pkgs $PKGDB $PATCHDB $patchnum
fi

check_pkgs_installed "$PKGDB" "$patchdir"

check_for_action "$client" "$is_a_root_pkg"

echo "s=0\\c" > $Root_Equation_File
echo "s=0\\c" > $Var_Equation_File
echo "s=0\\c" > $Opt_Equation_File
echo "s=0\\c" > $Usr_Equation_File
echo "s=0\\c" > $Openwin_Equation_File

gen_install_filelist "$PKGDB" "$PKGDBARG"

check_validation "$validate"

gen_patch_filelist

compute_fs_space_requirements 
check_fs_space

create_archive_area "$PATCHDB" "$patchnum"

trap 'trap_backoutsaved "$PATCHDB" "$patchnum" "$patchdir"' 1 2 3 15

# -	Save current versions of files to be patched
# -	On servers, spool the patch into /export/root/templates for
#	future clients (CURRENTLY DISABLED)
# -	Build admin file for later use by pkgadd

save_overwritten_files "$PATCHDB" "$patchnum" "$patchdir" "$saveold"

spool_client_patch

build_admin_file

trap 'trap_backout "$patchdir" "$patchnum"' 1 2 3 15

# -	Install the patch packages
# -	Print results of install
# -	Save ACTION file if exists, README file and backoutpatch
#	script

install_patch_pkgs "$PATCHDB" "$patchnum" "$patchdir" "$PKGDBARG" "$PKGDB"

execute_postpatch "$PATCHDB" "$patchnum" "$patchdir"

print_results

set_patch_status "$PATCHDB" "$patchnum"

echo "\nPatch installation completed."
echo "See $PATCHDB/$patchnum/log for more details."

exit 0
