#!/bin/ksh -h
# @(#) installpatch 5.3 95/10/10 SMI
#
# 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
#		20	A direct instance patch was required but not found
#		21	The required patches have not been installed on the manager
#		22	A progressive instance patch was required but not found
#		23	A restricted patch is already applied to the package
#		24	An incompatible patch is applied
#		25	A required patch is not applied
#

# Set the path for use with these scripts.
PATH=/usr/sadm/bin:/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.$$
LOGFILE=/tmp/pkgaddlog.$$
TMP_ARCHIVE=/tmp/TmpArchive.$$
TMP_FILELIST=/tmp/FileList.$$
TMP_LIB_DIR=/tmp/TmpLibDir.$$
INSTPATCHES_FILE=/tmp/MyShowrevFile.$$
PARAMS_FILE=/tmp/ParamsFile.$$
RESPONSE_FILE=/tmp/response.$$

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
typeset -i ReqArrCount=0
typeset -i ReqdPatchCnt=0
typeset -i Something_Installed=0
typeset -i interactive=0

# response file keywords
PATCH_UNCONDITIONAL="false"
PATCH_PROGRESSIVE="true"
PATCH_NO_UNDO="false"
PATCH_BUILD_DIR="none"
PATCH_UNDO_ARCHIVE="none"

client=no
is_a_root_pkg=no
is_an_instpatches="no"
patchdir=
PatchNum=
PatchBase=
PatchVers=
pkglist=
printpatches="no"
ThisPatchFnd="no"
ObsoletedBy="none"
ReqdOSPatch="none"
rootlist=
saveold="yes"
validate="yes"
isapplied="no"
libs_are_moved="no"

Obsoletes=""
Incompat=""
Requires=""
ObsoletePast=""
UninstReqs=""
InstIncompat=""
Product=""
MgrProduct=""

OpenwinFS=
OptFS=
UsrFS=
VarFS=

ROOTDIR="/"
PATCHDB="/var/sadm/patch"
PKGDB="/var/sadm/pkg"
NEW_SOFTINFO="/var/sadm/system/admin/INST_RELEASE"
OLD_SOFTINFO="/var/sadm/softinfo/INST_RELEASE"
MGRSOFTINFO="none"
TRGSOFTINFO="none"
PKGDBARG=""
PatchIdFormat='^[A-Z]*[0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9]$'

# List of required commands
REQD_CMDS="/usr/sbin/removef /usr/sbin/installf /usr/sbin/pkgadd /usr/bin/grep /usr/bin/find /usr/bin/pkgparam /usr/bin/pkginfo"

# 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
}

# Quit installpatch and clean up any remaining temporary files.
function patch_quit {	# exit code
	if [[ $1 -ne 0 ]]
	then
		echo "Installpatch is terminating."
	fi

	rm -f $INSTPATCHES_FILE
	rm -f /tmp/*.$$
	rm -f /tmp/*.$$.1
	rm -f /tmp/archive.cpio*

	if [[ $libs_are_moved = "yes" ]]
	then
		remove_libraries
	fi
	exit $1
}

#
# Description:
#	Return the base code of the provided patch. The base code
#	returned will include the version prefix token (usu "-").
#
# Parameters Used:
#	$1	- patch number
#
function get_base_code {
	ret_value=${1:%[0-9]}
	last_value=$1

	while [[ $ret_value != $last_value ]]
	do
		last_value=$ret_value
		ret_value=${last_value%[0-9]}
	done

	cur_base_code=${ret_value%?}
}

#
# Description:
#	Return the version number of the provided patch.
#
# Parameters Used:
#	$1	- patch number
#	$2	- base code
#
function get_vers_no {
	cur_vers_no=${1:#$2?}
}

#
# 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."
			patch_quit 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 "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 "See $1/$2/log for more details."
			fi
			patch_quit 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 used
#	PatchNum
#	force
#
# Globals Set:
#	ObsoletedBy
#	ThisPatchFnd
#	PatchedPkgs
#	ReqdOSPatchFnd	The patch that this OS release requires was found
#
# Revision History
#	1995-08-01	Added PATCH_OBSOLETES and expanded the tests for
#			direct instance patches since all necessary
#			is reviewed at this time, this function also
#			tests for obsolescence, dependencies and
#			incompatibilities.
#
function eval_inst_patches
{
	typeset -i TestCount=0
	typeset -i ArrayCount=0
	typeset -i PatchFound=0
	typeset -i req_count=0
	typeset -i sr_count=0

	set -A PkgArrElem
	set -A PatchArrElem
	set -A ObsArrElem
	set -A ShowRevPkg
	set -A ShowRevPatch
	set -A ShowRevObs

	if [[ "$is_an_instpatches" = "yes" ]]
	then
		cat $INSTPATCHES_FILE
		return
	fi

	olddir=$(pwd)
	#
	# First get the old-style patches and obsoletions
	#
	if [[ -d $1 && -d $PATCHDB ]]
	then
		cd $1
		patches=""

		# This gets old and new style patches
		patches=$(grep -l SUNW_PATCHID ./*/pkginfo 2>/dev/null | \
		    xargs sed -n 's/^SUNW_PATCHID=//p' | sort -u)

		if [[ "$patches" != "" ]]
		then
			for apatch in $patches
			do
				outstr="Patch: $apatch Obsoletes: "

				# Scan all the installed packages for this
				# patch number and return the effected
				# package instances
				patchvers=$(grep -l "SUNW_PATCHID=$apatch" \
				    ./*/pkginfo 2>/dev/null | \
				    sed 's,^./\(.*\)/pkginfo$,\1,' )

				# If there's a PATCH_INFO entry then this
				# is really a direct instance patch
				for package in $patchvers
				do
					break;
				done

				$(grep -b "PATCH_INFO_$apatch" $package/pkginfo 1>/dev/null 2>&1)
				if [[ $? -eq 0 ]]
				then
					continue
				fi

				PatchFound=1

				# Get the obsoletes list
				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

				# The current patch is a direct instance patch
				if [[ $apatch = "$PatchNum" ]]
				then
					ThisPatchFnd="yes"
				fi

				if [[ "$printpatches" = "yes" ]]
				then
					echo $outstr
				else
					echo $outstr | tee -a $INSTPATCHES_FILE
					is_an_instpatches="yes"
				fi
			done
		fi
	fi

	#
	# Now get the direct instance patches
	#
	# DIPatches is a non-repeating list of all patches applied
	# to the system.
	#
	pkginfo -R $ROOTDIR | nawk ' { print $2; } ' | while read pkg
	do
		DIPatches=$(pkgparam -R $ROOTDIR $pkg PATCHLIST)
		for patch in $DIPatches
		do
			get_base_code $patch
			patch_base=$cur_base_code

			get_vers_no $patch $patch_base
			patch_vers=$cur_vers_no

			PatchFound=1;

			# Get the obsoletes from each installed package
			obsoletes=$(pkgparam -R $ROOTDIR $pkg PATCH_INFO_$patch | nawk ' { print substr($0, match($0, "Obsoletes:")+11) } ')
			if [[ -n "$obsoletes" ]]
			then
				for obs in $obsoletes;
				do
					PatchArrElem[$ArrayCount]=$patch;
					ObsArrElem[$ArrayCount]=$obs;
					PkgArrElem[$ArrayCount]=$pkg;
					ArrayCount=ArrayCount+1;
				done
			else
				PatchArrElem[$ArrayCount]=$patch;
				ObsArrElem[$ArrayCount]="";
				PkgArrElem[$ArrayCount]=$pkg;
				ArrayCount=ArrayCount+1;
			fi

			# Check for already installed
			if [[ "$patch" = "$PatchNum" ]]
			then
				ThisPatchFnd="yes"
			fi

			if [[ $printpatches != "yes" ]]
			then
				# Check for incompatible patches
				for incompat in $Incompat
				do
					get_base_code $incompat

					if [[ "$patch_base" = "$cur_base_code" ]]
					then
						get_vers_no $incompat $cur_base_code
						if [[ $patch_vers -ge cur_vers_no ]]
						then
							InstIncompat=$patch
						fi
					fi
				done

				# Check for required patches
				if [[ $ReqArrCount -gt 0 && $validate = "yes" ]]
				then
					req_count=0;

					for required in $Requires; do
						get_base_code $required

						if [[ "$patch_base" = "$cur_base_code" ]]
						then
							get_vers_no $required $cur_base_code

							if [[ $patch_vers -ge $cur_vers_no ]]
							then
								ReqArrElem[$req_count]="yes"
							fi
						fi
						req_count=req_count+1
					done
				fi

				for obs_entry in $obsoletes
				do
					get_base_code $obs_entry

					if [[ "$cur_base_code" = "$PatchBase" ]]
					then
						get_vers_no $obs_entry $cur_base_code

$PatchNum
						if [[ $cur_vers_no -ge $PatchVers ]]
						then
							ObsoletedBy=$patch
						else
							ObsoletePast=$PatchBase
							ObsoletedBy=$patch

						fi
					fi
				done
			fi
		done
	done

	req_count=0;
	for req in $Requires; do
		if [[ ${ReqArrElem[$req_count]} = "no" ]]
		then
			ReqdPatchCnt=ReqdPatchCnt+1
			UninstReqs="$UninstReqs $req"
		fi
		req_count=req_count+1;
	done

	if [[ $validate = "no" ]]
	then
		ReqdPatchCnt=0
	fi

	while [[ $TestCount -lt $ArrayCount ]]
	do
		typeset -i TempCount=TestCount+1

		# Scan all entries matching the current one
		PatchArrEntry=${PatchArrElem[$TestCount]}	# Current one
		ObsArrEntry=${ObsArrElem[$TestCount]}
		PkgArrEntry=${PkgArrElem[$TestCount]}

		if [[ "$PatchArrEntry" = "used" ]]
		then
			TestCount=TestCount+1
			continue
		fi

		while [[ $TempCount -lt $ArrayCount ]]
		do
			typeset -i dont_use;
			#
			# If this is another line describing this patch
			#
			if [[ ${PatchArrElem[$TempCount]} = $PatchArrEntry ]]
			then
				dont_use=0;

				PatchArrElem[$TempCount]="used"
				for pkg in $PkgArrEntry
				do
					if [[ $pkg = ${PkgArrElem[$TempCount]} ]]
					then
						dont_use=1;
						break;
					fi
				done

				if [[ $dont_use = 0 ]]
				then
					PkgArrEntry="$PkgArrEntry ${PkgArrElem[$TempCount]}"
				fi

				dont_use=0;

				for obs in $ObsArrEntry
				do
					if [[ $obs = ${ObsArrElem[$TempCount]} ]]
					then
						dont_use=1;
						break;
					fi
				done

				if [[ $dont_use = 0 ]]
				then
					ObsArrEntry="$ObsArrEntry ${ObsArrElem[$TempCount]}"
				fi
			fi
			TempCount=TempCount+1
		done

		if [[ $PatchArrEntry = "$PatchNum" ]]; then
			export PatchedPkgs="$PkgArrEntry"
		fi

		# Now make it comma separated lists
		PkgArrEntry=$(echo $PkgArrEntry | sed s/\ /,\ /g)
		ObsArrEntry=$(echo $ObsArrEntry | sed s/\ /,\ /g)

		outstr="Patch: $PatchArrEntry Obsoletes: $ObsArrEntry Packages: $PkgArrEntry"
		if [[ "$printpatches" = "yes" ]]
		then
			echo $outstr
		else
			echo $outstr | tee -a $INSTPATCHES_FILE
			is_an_instpatches="yes"
		fi

		TestCount=TestCount+1
	done
	if [[ $PatchFound = 0 && $printpatches = "yes" ]]
	then
		print " No patches 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
#	PatchVers
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.
		#
		tmp=$(grep PATCHID $i)
		PatchNum=$(pkgparam -f $i ${tmp:%=*})
		break;
	done

	if [[ "$PatchNum" = "" ]]
	then
		echo "$1 packages are not proper patch packages."
		echo 'See "Instructions for applying the patch" in the README file.'
		patch_quit 7
	else
		#
		# Get the patch base code (the number up to the version prefix) 
		# and the patch revision number (the number after the version prefix).
		#
		get_base_code $PatchNum
		PatchBase=$cur_base_code
		get_vers_no $PatchNum $cur_base_code
		PatchVers=$cur_vers_no
	fi
}

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

function build_admin_file
{
	if [[ "$PatchMethod" = "direct" && -f /var/sadm/install/admin/patch ]]
	then
		ADMINTFILE="patch"
	else
		cat > $ADMINTFILE << EOF
mail=
instance=unique
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=quit
setuid=nocheck
conflict=nocheck
action=nocheck
EOF
	fi
}

# Description:
#	create a response file if it is necessary
# Parameters:
#	$1	patch type
#	$2	patch method
function build_response_file
{
	if [[ "$1" != "piPatch" ]]
	then
		if [[ "$2" = "progressive" ]]
		then
			cat > $RESPONSE_FILE << EOF
PATCH_PROGRESSIVE=$PATCH_PROGRESSIVE
EOF
		else
			cat > $RESPONSE_FILE << EOF
PATCH_PROGRESSIVE=$PATCH_PROGRESSIVE
PATCH_UNCONDITIONAL=$PATCH_UNCONDITIONAL
PATCH_NO_UNDO=$PATCH_NO_UNDO
EOF

			if [[ $PATCH_BUILD_DIR != "none" ]]
			then
				echo "PATCH_BUILD_DIR=$PATCH_BUILD_DIR" >> $RESPONSE_FILE
			fi

			if [[ $PATCH_UNDO_ARCHIVE != "none" ]]
			then
				echo "PATCH_UNDO_ARCHIVE=$PATCH_UNDO_ARCHIVE" >> $RESPONSE_FILE
			fi
		fi
	fi
}

# 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" = "" && "$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 || $2 = yes ]]
		then
 			echo "None of the packages to patch are installed on this system."
			patch_quit 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."
			patch_quit 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 [[ "$PatchMethod" = "direct" ]]
	then
		if [[ "$ThisPatchFnd" = "yes" ]]
		then
			isapplied="yes"
		else
			rm -fr $1/$2
		fi
	else
		if eval_inst_patches $PKGDB | grep -s "^Patch:[ 	]*$2" > /dev/null 2>&1
		then
			isapplied="yes"
		else
			rm -fr $1/$2
		fi
	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=0
	typeset -i Openwin_Available=0
	typeset -i Root_Available=0
	typeset -i Usr_Available=0
	typeset -i Var_Available=0
	typeset -i exit_status=0

	echo "Verifying sufficient filesystem capacity (exhaustive method) ..."

	#
	# Bear in mind that df -b gives the total kbytes available to
	# the super-user. That means we have to 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

	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
		patch_quit $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
#	$2	- patch database directory
#	$3	- patch number
# Globals Used:
#	pkglist
# Globals Set:
#	pkglist
function gen_uninstalled_pkgs
{
	pkg=
	for i in $pkglist
	do
		if eval_inst_patches $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."
		patch_quit 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
#	PatchVers
# Globals set:
#	isapplied

function check_if_obsolete
{
	if [[ "$PatchMethod" = "direct" ]]
	then
		if [[ "$ObsoletedBy" = "none" ]]
		then
			return
		else
			print_obsolete_msg "$ObsoletedBy" "none"
			patch_quit 6
		fi
	else
		#
		# Search for patches that specifically obsolete the current 
		# patch.  Ignore if the PatchBase of the obsoletor is the same 
		# as the obsoletee.
		#
		if eval_inst_patches $PKGDB | grep -v "Patch: $PatchBase" | \
			grep -s "Obsoletes:.*$PatchBase.*Packages:" > /dev/null 2>&1
		then
			print_obsolete_msg
			eval_inst_patches $PKGDB | grep -v "Patch: $PatchBase" | \
				grep "Obsoletes:.*$PatchBase.*Packages:"
			patch_quit 6
		fi
	fi

	currentdir=$(pwd)
	#
	# Now 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 2>/dev/null | \
	         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" = "$PatchVers" ]]
			then
				isapplied="yes"
				continue
			elif [[ "$ii" -gt "$PatchVers" ]]
			then
				print_obsolete_msg "$PatchBase-$ii"
				patch_quit 6
			fi
		done
	fi

	cd $currentdir
}

# 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."
		patch_quit 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 Used:
#	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	# for each package in the patch
	do
		#
		# Get the package abbreviation, architecture, version
		# and target filesaystem.
		#
		Pkginst=$(basename $i)
		Pkgabbrev=$(pkgparam -f $i/pkginfo PKG)
		Pkgarch=$(pkgparam -f $i/pkginfo ARCH)
		Pkgpatchver=$(pkgparam -f $i/pkginfo VERSION)
		Pkgtype=$(pkgparam -f $i/pkginfo SUNW_PKGTYPE)

		if [[ "$Pkgtype" = "root" ]]
		then
			is_a_root_pkg=yes
		elif [[ "$Pkgtype" = "" ]]
		then
			PkgType="opt"
		fi

		#
		# First the easy test, see if there's a package by
		# that name installed.
		#
		if [ ! -d "$1/$Pkgabbrev" ] && [ ! -d "$1/$Pkgabbrev.*" ]
		then
			echo "Package not patched:" >> $LOGFILE
			echo "PKG=$Pkgabbrev" >> $LOGFILE
			echo "Original package not installed" >> $LOGFILE

			continue
		fi

		#
		# At this point, there's a package of that name
		# installed. So now we have to look for the right
		# architecture and version. This is pretty easy for a
		# direct instance patch. For the progressive instance
		# patch, there's a lot of munging around with the various
		# installed versions.
		#
		if [[ "$PatchMethod" = "direct" ]]
		then
			if [[ $ROOTDIR = "/" ]]
			then
				pkginst=$(pkginfo -a $Pkgarch -v $Pkgpatchver $Pkgabbrev.\* 2>/dev/null | nawk ' { print $2 } ')
			else
				pkginst=$(pkginfo -R $ROOTDIR -a $Pkgarch -v $Pkgpatchver $Pkgabbrev.\* 2>/dev/null | nawk ' { print $2 } ')
			fi

			if [[ $? -eq 0 ]]
			then
				finalpkglist="$finalpkglist $i,$pkginst"
			else
				echo "Package not patched:" >> $LOGFILE
				echo "PKG=$Pkginst" >> $LOGFILE
				echo "Original package not installed" >> $LOGFILE

				continue
			fi
		else
			#
			# 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

			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
		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" && -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."
			patch_quit 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=`expr $i : '\(.*\),.*'`
		pkginst=`expr $i : '.*,\(.*\)'`

		if [[ $pkginst = "" ]]
		then
			continue
		fi

		pkginfofile="$1/$pkginst/pkginfo"
		pkgmapfile="$1/$pkginst/pkgmap"

		# Get the BASEDIR
		basedir=$(pkgparam -f $pkginfofile BASEDIR)

		#
		# 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
			(	# different shell
			rm -f $macrofiles $resfiles

			# Extract every macro that may be meaningful
			# and throw quotes around all of the values
			# assigned.
			nawk -F= '
				$1 ~ /PATCHLIST/	{ next; }
				$1 ~ /OBSOLETES/	{ next; }
				$1 ~ /ACTIVE_PATCH/	{ next; }
				$1 ~ /PATCH_INFO/	{ next; }
				$1 ~ /UPDATE/	{ next; }
				$1 ~ /SCRIPTS_DIR/	{ next; }
				$1 ~ /PATCH_NO_UNDO/	{ next; }
				$1 ~ /INSTDATE/	{ next; }
				$1 ~ /PKGINST/	{ next; }
				$1 ~ /OAMBASE/	{ next; }
				$1 ~ /PATH/	{ next; }
				{
					printf("%s=\"%s\"\n", $1, $2);
				} ' $pkginfofile > $macrofiles

			. $macrofiles
			cat $pkgfiles |
			while read i
			do
				eval /usr/bin/echo $i >> $resfiles
			done
			)	# back to original shell

			#
			# 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
#
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=$(/usr/bin/ls -Ldl $ROOTDIR$j)
				size=$(echo $size | nawk ' { print $5 } ')
#				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 patch package IDs contained in the patch
#	(at least one directory 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=`expr $i : '\(.*\)/pkginfo'`
		pkglist="$pkglist $pkg"
	done
}

# 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:
#	prodver
#
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
		MgrProduct="Solaris"
		MgrOSVers=$(uname -r | sed -n -e 's/5\./2\./p' -e 's/4\./1\./p')
		Mgrprodver=$MgrProduct"_"$MgrOSVers
	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
		echo "installpatch is unable to find the INST_RELEASE file for the target"
		echo "filesystem.  This file must be present for installpatch to function correctly."
		patch_quit 11
	else
		Product=$(sed -n 's/^OS=\(.*\)/\1/p' $1)
		TrgOSVers=$(sed -n 's/^VERSION=\(.*\)/\1/p' $1)
		prodver=$Product"_"$TrgOSVers
	fi
}

# 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=`expr $ij : '\(.*\),.*'`
		pkginst=`expr $ij : '.*,\(.*\)'`

		pkginfofile="$5/$pkginst/pkginfo"

		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:"

		if [[ $PatchType = "caPatch" ]]
		then
			cp $RESPONSE_FILE $RESPONSE_FILE.1
			pkgadd $4 -S -a $ADMINFILE -r $RESPONSE_FILE.1 -n -d $3 $i >$LOGFILE 2>&1
		else
			pkgadd $4 -S -a $ADMINFILE -n -d $3 $i >$LOGFILE 2>&1
		fi
		pkgadderr=$?

		if [[ $PatchType = "caPatch" ]]
		then
			rm -f $RESPONSE_FILE.1
		fi

		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
			patch_quit 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=$PatchVers" >> $PARAMS_FILE
	echo "ROOTDIR=$ROOTDIR" >> $PARAMS_FILE
	echo "PATCHDB=$PATCHDB" >> $PARAMS_FILE
	echo "PKGDB=$PKGDB" >> $PARAMS_FILE
	echo "PKGDBARG=$PKGDBARG" >> $PARAMS_FILE
	echo "PATCHMETHOD=PatchMethod" >> $PARAMS_FILE
	echo "UNINST_REQS=\"$UninstReqs\"" >> $PARAMS_FILE
	echo "PATCH_UNDO_ARCHIVE=$PATCH_UNDO_ARCHIVE" >> $PARAMS_FILE

	export saveold validate patchdir PatchNum PatchBase PatchVers
	export PARAMS_FILE ROOTDIR PATCHDB PKGDB PKGDBARG
	export PATCH_BUILD_DIR
}

# 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 $TrgOSVers | 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
		libs_are_moved="yes"
	fi
}


# Description:
#	Find the appropriate softinfo files for the manager and the target.
# Parameters:
#	$1	ROOT of target filesystem
# Globals set:
#	TRGSOFTINFO
#	MGRSOFTINFO
# Globals used:
#	OLD_SOFTINFO
#	NEW_SOFTINFO
function find_softinfos
{
	if [[ -f $NEW_SOFTINFO ]]
	then
		MGRSOFTINFO=$NEW_SOFTINFO
	elif [[ -f $OLD_SOFTINFO ]]
	then
		MGRSOFTINFO=$OLD_SOFTINFO
	fi

	if [[ "$1" = "/" || "$1" = "" ]]
	then
		TRGSOFTINFO=MGRSOFTINFO
	elif [[ -f $1$NEW_SOFTINFO ]]
	then
		TRGSOFTINFO=$1$NEW_SOFTINFO
	elif [[ -f $1$OLD_SOFTINFO ]]
	then
		TRGSOFTINFO=$1$OLD_SOFTINFO
	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
#	PKGDBARG
# Globals Used:
#	Mgrprodver
#	MGRSOFTINO
#	TRGSOFTINFO
#	PKGDB
#	PATCHDB
#
function parse_args
{
	service_specified="n"
	rootdir_specified="n"
	while [ "$1" != "" ]
	do
		case $1 in
		-i)	interactive=1; shift;;
		-u)	validate="no"; PATCH_UNCONDITIONAL="true"; shift;;
		-d)	saveold="no"; PATCH_NO_UNDO="true"; shift;;
		-B)	shift
			if [[ -d $1 ]]
			then
				PATCH_UNDO_ARCHIVE=$1
			else
				echo "Specified backout directory cannot be found."
			fi
			shift;;
		-p)	printpatches="yes"; shift;;
		-S)	shift
			if [ "$rootdir_specified" = "y" ]
			then
				echo "The -S and -R arguments are mutually exclusive."
				print_usage
				patch_quit 1
			fi

			find_softinfos /export/$1

			get_OS_version "$TRGSOFTINFO" "$MGRSOFTINFO" "$1"

			if [ "$1" != "$Mgrprodver" ]
			then
				if [ -d "/export/$1$PKGDB" ]
				then
					ROOTDIR=/export/$1
					PATCHDB=$ROOTDIR$PATCHDB
					PKGDB=$ROOTDIR$PKGDB
					PKGDBARG="-R $ROOTDIR"
					service_specified="y"
				else
					echo "The $1 service cannot be found on this system."
					print_usage
					patch_quit 1
				fi
			fi
			shift;;
		-V) echo "@(#) installpatch 5.3 95/10/10"
			patch_quit 0;;
		-R)	shift
			if [ "$service_specified" = "y" ]
			then
				echo "The -S and -R arguments are mutually exclusive."
				print_usage
				patch_quit 1
			fi
			if [ ! -d "$1" ]
			then
				echo "The Package Install Root directory $1 cannot be found on this system."
				print_usage
				patch_quit 1
			else
				ROOTDIR=$1
				PATCHDB=$ROOTDIR$PATCHDB
				PKGDB=$ROOTDIR$PKGDB
				PKGDBARG="-R $ROOTDIR"
				rootdir_specified="y"
			fi
			shift;;

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

# 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."
}

# 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 [[ -z "$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
	libs_are_moved="no"
}

# 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
#
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
			patch_quit 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
			patch_quit 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
{
	if [[ ! -d $1/$2 ]]
	then
		mkdir -m 750 -p $1/$2
	fi
	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
}

# 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
	patch_quit 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
	patch_quit 12
}

# Description:
# Parameters:
# 	$1	- patch database directory
#	$2	- patch number
#
function trap_notinstalled
{
	echo "Interrupt signal detected. Patch not installed."
	rm -f /tmp/*.$$
	rm -f $INSTPATCHES_FILE
	if [[ "$isapplied" = "no" ]]
	then
		rm -fr $1/$2
	else
		echo "Install Interrupted." >> $1/$2/log
	fi
	patch_quit 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."
		patch_quit 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."
			patch_quit 9
		fi
	fi
}

# Description:
#	Get key parameters relating to this patch
# Parameters:
#	none
# Globals Set:
#	Obsoletes	those patches that this one obsoletes
#	Incompat	those patches with which this one is incompatible
#	Requires	those patches that this one requires
#	ReqArrElem[]	an ordered mapping of "yes" or "no" attributes
#			associated with each entry in Requires. If it
#			is "yes", that package has been found on the
#			system. If "no", it has not been found.
#	ReqArrCount	The count of elements in the above array
#
function collect_data
{
	cd $patchdir
	for pkg in *
	do
		if [[ -f $pkg/pkginfo ]]
		then
			# Collect the data from a pkginfo file
			tmp=$(grep OBSOLETES $pkg/pkginfo)
			if [[ $tmp = "" ]]
			then
				Obsoletes=""
			else
				Obsoletes=$(pkgparam -f $pkg/pkginfo ${tmp:%=*})
			fi

			tmp=$(grep INCOMPAT $pkg/pkginfo)
			if [[ $tmp = "" ]]
			then
				Incompat=""
			else
				Incompat=$(pkgparam -f $pkg/pkginfo ${tmp:%=*})
			fi

			tmp=$(grep REQUIRES $pkg/pkginfo)
			if [[ $tmp = "" ]]
			then
				Requires=""
			else
				Requires=$(pkgparam -f $pkg/pkginfo ${tmp:%=*})
			fi
			for req in $Requires
			do
				ReqArrElem[$ReqArrCount]="no"
				ReqArrCount=ReqArrCount+1;
			done
			break
		fi
	done
}

# Description:
#	Apply a direct instance patch
# Parameters:
#	none
# Globals Set:
#	none
#
function apply_diPatch {	
	typeset -i exit_code=0
	ReqArrCount=0
	curdir=$(pwd)

	# strip the installed instance out of the package list
	for pkg in $pkglist
	do
		newpkglist="$newpkglist ${pkg:%,*}"
	done

	pkglist=$newpkglist

	echo "Installing patch packages..."

	# actually install the packages
	#
	for pkg in $pkglist
	do
		if [[ -f $pkg/pkginfo ]]	# If this is a package
		then
			cp $RESPONSE_FILE $RESPONSE_FILE.1
			pkgadd -S -n -a $ADMINTFILE -r $RESPONSE_FILE.1 \
			    -R $ROOTDIR -d . $pkg 1>$LOGFILE 2>&1
			exit_code=$?

			# If it's a suspend (exit code 4), then the
			# message type is the appropriate installpatch
			# exit code and the appropriate message follows.
			# A suspend means, nothing has been installed.
			if (( exit_code == 4 ))	# suspend
			then
				Message=$(egrep PaTcH_MsG $LOGFILE | sed s/PaTcH_MsG\ //)
				if [[ $Message = "" ]]
				then
					exit_code == 5
				else
					Msg_Type=$(echo $Message | nawk ' { print $1 } ')
					Message=$(echo $Message | sed s/$Msg_Type\ //)

					echo "$Message" >> $LOGFILE
					puttext "$Message"
					patch_quit $Msg_Type
				fi
			fi

			if (( exit_code == 5 ))	# administration
			then
				cat $LOGFILE
				./backoutpatch -R $ROOTDIR $PatchNum
				Something_Installed=0
				break
			elif (( exit_code != 0 ))
			then
				egrep ERROR $LOGFILE
				./backoutpatch -R $ROOTDIR $PatchNum
				Something_Installed=0
				patch_quit 5
			else
				Something_Installed=1
			fi
		fi
		rm -f $RESPONSE_FILE.1
	done
	
	if (( Something_Installed == 1 ))
	then
		cd $ROOTDIR
		cd var/sadm/pkg

		InstPkgs=$(pkginfo -R $ROOTDIR | nawk '
		    { printf ("%s ", $2) }
		END { printf("\n") } ')

		#
		# With that done successfully, obsolete explicitly
		# listed prior patches.
		#
		for patch in $Obsoletes
		do
			get_base_code $patch
			patch_base=$cur_base_code

			get_vers_no $patch $patch_base
			patch_vers=$cur_vers_no

			for pkg in $InstPkgs
			do
				#
				# Locate all applicable obsoleted patches
				# by searching for entries with identical
				# base codes and versions greater than or
				# equal to the one specified.
				#
				cd $pkg/save

				patch_list=$(ls -db $patch_base* 2>/dev/null)

				for cur_patch in $patch_list
				do
					get_vers_no $cur_patch $patch_base
					if [[ $cur_vers_no -gt $patch_vers ]]
					then
						echo "WARNING: Later version of obsolete patch $patch was found. Leaving $cur_patch as is."
						continue;
					fi

					if [[ -f $cur_patch/undo ]]
					then
						mv $patch/undo $cur_patch/obsolete
						echo $PatchNum >> $cur_patch/obsoleted_by
					elif [[ -f $cur_patch/undo.Z ]]
					then
						mv $cur_patch/undo.Z $cur_patch/obsolete.Z
						echo $PatchNum >> $cur_patch/obsoleted_by
					elif  [[ -f $cur_patch/obsolete || -f $cur_patch/obsolete.Z ]]
					then
						grep $PatchNum $cur_patch/obsoleted_by >/dev/null
						if [[ $? -ne 0 ]]
						then
							echo $PatchNum >> $cur_patch/obsoleted_by
						fi
					fi
				done

				cd $ROOTDIR
				cd var/sadm/pkg
			done
		done
		echo "Patch number $PatchNum has been installed."
	else
		echo "Installation of patch number $PatchNum has been suspended."
	fi
	
	rm -f $RESPONSE_FILE
	cd $curdir
}

# Description:
#	Determine which patch is required for this OS release to work.
# Parameters:
#	$1	Solaris release of the managing host
#	$2	The patch method to use
# Globals Set:
#	ReqdOSPatch	patch number that this OS requires
#	ReqdOSPatchBase	base number of the above patch
#	ReqdOSPatchVers	version number of the above patch
#
function ident_reqd_patch {
	if [[ "$PatchMethod" = "direct" ]]
	then
		case $1 in
			"2.0")
				echo "ERROR: Solaris 2.0 is not capable of installing patches"
				echo "to a 2.5 or later client.";
				patch_quit 21;;
			"2.1")
				echo "ERROR: Solaris 2.1 is not capable of installing patches"
				echo "to a 2.5 or later client.";
				patch_quit 21;;
			"2.2")
				ReqdOSPatch="101122-07";;
			"2.3")
				ReqdOSPatch="101331-06";;
			"2.4")
				MgrPlatform=$(uname -p);
				case $MgrPlatform in
					"sparc")
						ReqdOSPatch="102039-04";;
					"i386")
						ReqdOSPatch="102041-04";;
				esac;;
		esac

		if [[ "$ReqdOSPatch" != "none" ]]
		then
			get_base_code $ReqdOSPatch
			ReqdOSPatchBase=$cur_base_code;

			get_vers_no $ReqdOSPatch $cur_base_code
			ReqdOSPatchVers=$cur_vers_no;

			cd /var/sadm/patch
			for apatch in *
			do
				get_base_code $apatch
				if [[ "$ReqdOSPatchBase" = "$cur_base_code" ]]
				then
					get_vers_no $apatch $cur_base_code
					if [[ "$ReqdOSPatchVers" -le "$cur_vers_no" ]]
					then
						ReqdOSPatchFnd="true"
					fi
				fi
			done
		fi
	fi

	cd $patchdir
}

# Description:
#	Evaluate the patch provided and return the patch type.
# Parameters:
#	$1 - patch directory
# Globals Set:
#	PatchType	one of:
#				diPatch		direct instance patch
#				caPatch		cross architecture patch
#				piPatch		progressive instance patch
#
function eval_patch {
	if [[ -f ${1}/.diPatch ]]
	then
		if [[ -d ${1}/old_style_patch ]]
		then
			PatchType="caPatch"
		else
			PatchType="diPatch"
		fi
	else
		PatchType="piPatch"
	fi
}

# Description:
#       Evaluate the patch methodology to be used based upon the
#	Solaris version of the manager and target hosts.
# Parameters:
#	$1	Managing host OS version
#	$2	Target host OS version
#	$3	patch type
# Globals Set:
#	PatchMethod	one of:
#				direct		direct instance method
#				progressive	progressive instance method
#	PATCH_PROGRESSIVE
#
function eval_OS_version {
	if [[ "$1" < "2.5" ]]
	then
		if [[ "$2" < "2.5" ]]
		then
			PatchMethod="progressive"
			PATCH_PROGRESSIVE="true"
		else
			if [[ $3 = "diPatch" || $3 = "caPatch" ]]
			then
				PatchMethod="direct"
				PATCH_PROGRESSIVE="false"
			else
				PatchMethod="progressive"
				PATCH_PROGRESSIVE="true"
			fi
		fi
	else
		if [[ "$2" < "2.5" ]]
		then
			if [[ $PatchType = "diPatch" ]]
			then
				echo "ERROR: This direct instance patch cannot be installed onto a"
				echo "   Solaris $TrgOSVers host."
				patch_quit 21
			else
				PatchMethod="progressive"
				PATCH_PROGRESSIVE="true"
			fi
		else
			if [[ $3 = "diPatch" || $3 = "caPatch" ]]
			then
				PatchMethod="direct"
				PATCH_PROGRESSIVE="false"
			else
				PatchMethod="progressive"
				PATCH_PROGRESSIVE="true"
			fi
		fi
	fi
}

# Description:
#       Evaluate the applied patch to be sure we aren't going to hose up 
#	any existing backout data.
#
#	If this is a progressive instance patch, here's how it is evaluated:
# prev  | curr | .nofilestosave |   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. ! empty save directory
#-------+------+----------------+-----------+--------------------------
#       |      |      yes       | continue  | a. empty save directory
# 2 yes |  no  |----------------+-----------+--------------------------
#       |      |       no       | terminate | b. ! empty save directory
#-------+------+----------------+-----------+--------------------------
# 3  no |  no  |       no       | continue  |    empty save directory
#-------+------+----------------+-----------+--------------------------
# 4  no | yes  |       no       | terminate |    empty save directory
#-------+------+----------------+-----------+--------------------------
#
#	And the direct instance patch is evaluated as follows:
#  prev  | curr |   OK to   |  How to verify previous
#  save  | save | continue? |    save/no_save state
# -------+------+-----------+------------------------------------------
# A.  no | yes  | terminate | ! -d /var/sadm/pkg/<pkg>/save/<patch_id>
# -------+------+-----------+------------------------------------------
# B.  no |  no  | continue  | ! -f /var/sadm/pkg/<pkg>/save/<patch_id>
# -------+------+-----------+------------------------------------------
# C. yes |  --  | continue  |   -f /var/sadm/pkg/<pkg>/save/<patch_id>
# -------+------+-----------+------------------------------------------
#
# Parameters:
#	$1	patch database directory
#	$2	package database directory
#	$3	the patch method
#	$4	the saveold parameter value
#	$5	the patch number
# Globals Set:
#
function eval_applied_patch { 
	if [[ "$3" = "progressive" ]]
	then
		if [[ "${4}" = "no" ]]
		then
			if [ ! -f "${1}/${5}/.nofilestosave" -a \
			    \( -f "${1}/${5}/save/archive.cpio" -o \
			    -f "${1}/${5}/save/archive.cpio.Z" \) ]
	       		then
				# condition #2b - terminate
				echo "A previous installation of patch ${5} was invoked which saved files that were"
				echo "to be patched.  Since files have already saved, you must apply this patch"
				echo "WITHOUT the -d option."
				patch_quit 17
			elif [ -f "${1}/${5}/.nofilestosave" -a \
			    ! -f "${1}/${5}/save/archive.cpio" -a \
			    ! -f "${1}/${5}/save/archive.cpio.Z"   ]
			then
				# condition #2a - rm .nofilestosave
				rm ${1}/${5}/.nofilestosave
			fi
		else	# ${4} != "yes"
			if [ ! -f "${1}/${5}/.nofilestosave"    -a \
			    ! -f "${1}/${5}/save/archive.cpio" -a \
			    ! -f "${1}/${5}/save/archive.cpio.Z"   ]
			then
				# condition #4 - terminate
				echo "A previous installation of patch ${5} was invoked with the -d option."
				echo "(i.e. Do not save files that would be patched)  Therefore, this invocation of"
				echo "installpatch must also be run with the -d option."
				patch_quit 17
			fi
		fi
	else	# $3 != "progressive"
		find ${2}/. -name "${5}" -print >/dev/null 2>&1
		prev_save=$?
		if [[ $prev_save != 0 && "${4}" = "yes" ]]
		then
			# condition A.
			echo "A previous installation of patch ${5} 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."
			patch_quit 17
		fi
	fi
}

function eval_utilities {
	for command in $REQD_CMDS; do
		if [[ ! -f $command ]]
		then
			echo "ERROR: Cannot find $command which is required for proper execution of installpatch."
			patch_quit 1
		fi
	done		
}

############################################
#		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'
#

Cmd=$0
parse_args $*
validate_uid	# the caller must be "root"

# determine the OS versions involved
find_softinfos $ROOTDIR
get_OS_version $TRGSOFTINFO $MGRSOFTINFO $ROOTDIR

eval_utilities		# make sure the required utilities are available

eval_patch $patchdir	# determine what type of patch it is

# Establish the patching options based on the versions involved
eval_OS_version $MgrOSVers $TrgOSVers $PatchType

# Construct the required response file
build_response_file $PatchType $PatchMethod

# Clear the list of installed patches, if it's there
rm -f $INSTPATCHES_FILE

#
# Change to the patch directory and set globals according to the patchID
# found in the pkginfo files of the patch packages.
#
collect_data

activate_patch "$patchdir"

if [[ "$PatchMethod" = "progressive" ]]
then
	echo $PatchNum | grep $PatchIdFormat >/dev/null
	if [[ $? -ne 0 ]]
	then
		echo "Invalid patch id format: $PatchNum"
		exit 8
	fi
	if [[ "$PatchType" = "diPatch" ]]
	then
		echo "ERROR:  A progressive instance patch is required but patch number $PatchNum is direct"
		echo "instance only and can only be installed onto a host running Solaris 2.5 or later."
		patch_quit 22
	elif [[ "$PatchType" = "caPatch" ]]
	then
		cd old_style_patch
		patchdir=$(pwd)
	fi
fi

ident_reqd_patch $MgrOSVers	# determine if this host needs a patch

eval_inst_patches $PKGDB 1>/dev/null 2>&1	# scan installed patches & analyze

if [[ "$InstIncompat" != "" ]]
then
	echo "ERROR: This patch is incompatible with $InstIncompat which is already applied to the system."
	patch_quit 24
fi

if (( ReqdPatchCnt == 1 ))
then
	echo "ERROR: Patch requires patch number$UninstReqs which is not applied to the system."
	patch_quit 25
elif (( ReqdPatchCnt > 1 ))
then
	echo "ERROR: Patch requires the following patches which are not applied to the system:"
	echo "    $UninstReqs"
	patch_quit 25	
fi

if [[ "$ObsoletePast" != "" ]]
then
	echo "WARNING: This patch appears to have been produced after the base code $ObsoletePast was obsoleted. This patch will be treated as though it were also obsoleted."
fi

if [[ "$ReqdOSPatch" != "none" && "$ReqdOSPatchFnd" != "true" ]]
then
	echo "ERROR: This Solaris $MgrOSVers server requires the following patch before it can"
	echo "apply a patch to a Solaris $TrgOSVers client."
	echo
	echo "    patch base number : $ReqdOSPatchBase"
	echo "    patch version number : $ReqdOSPatchVers or higher"
	patch_quit 21
fi

check_if_applied "$PATCHDB" "$PatchNum"

if [[ "$isapplied" = "yes" ]]
then
	eval_applied_patch $PATCHDB $PKGDB $PatchMethod $saveold $PatchNum 
fi

check_if_obsolete "$PATCHDB"	# see if this package is already obsolete

# For the old-style patch, there were sometimes problems running on a
# client and also symbolic links were not allowed. We check for those next.
if [[ "$PatchMethod" = "progressive" ]]
then
	verify_client
	check_for_symbolic_link "$patchdir"
fi

trap 'trap_notinstalled "$PATCHDB" "$PatchNum"' 1 2 3 15	# set the trap

make_params_available	# export parameters for use by patch scripts

build_admin_file

# If there is a prepatch file in the $patchdir directory,
# execute it. If the return code is not 0, exit installpatch
# with an error. Lord knows what this does. We just have to hope
# it's benign.
#
if [[ "$isapplied" = "no" ]]	# if this isn't a reinstallation
then
	execute_prepatch "$patchdir"	# do it
fi

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"

gen_install_filelist "$PKGDB" "$PKGDBARG"

#
# OK, which patch method are we using?
#
if [[ "$PatchMethod" = "direct" ]]
then
	#
	# This is how we install a direct instance patch
	#
	gen_patch_filelist

	compute_fs_space_requirements 
	check_fs_space
	cd $patchdir
	apply_diPatch
else	
	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"

	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"

fi

execute_postpatch "$PATCHDB" "$PatchNum" "$patchdir"

print_results

if [[ "$PatchMethod" = "progressive" ]]
then
	echo "\nPatch installation completed."
	echo "See $PATCHDB/$PatchNum/log for more details."
fi

set_patch_status "$PATCHDB" "$PatchNum"

patch_quit 0
