#!/bin/ksh -hp

# ident	"@(#)patchUpdate.sh	1.7	06/08/09 SMI"

# Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.

## This script is for updating a Zones enabled S10 GA or a S10U1 system
## to a S10U2 system. It is also used to update a zones enabled S10 GA
## system to S10U1.
## This script is only invoked by the upgrade program on a S10U1 and S10U2
## distribution. It will only be invoked if a S10 GA or a S10U1 system has
## non-global Zones installed.

umask 022

SetGlobals () {
	# Needed Commands
	CAT=/usr/bin/cat
	CP=/usr/bin/cp
	CD=/usr/bin/cd
	LN=/usr/bin/ln
	GREP=/usr/bin/grep
	CHGRP=/usr/bin/chgrp
	CHMOD=/usr/bin/chmod
	DF=/usr/bin/df
	ECHO=/usr/bin/echo
	EGREP=/usr/bin/egrep
	HEAD=/usr/bin/head
	ID=/usr/bin/id
	MD=/usr/bin/mkdir
	MV=/usr/bin/mv
	NAWK=/usr/bin/nawk
	PATCHADD=/usr/sbin/patchadd
	PATCHRM=/usr/sbin/patchrm
	PKGADD=/usr/sbin/pkgadd
	PKGINSTALL=/usr/sadm/install/bin/pkginstall
	PKGRM=/usr/sbin/pkgrm
	SED=/usr/bin/sed
	SORT=/usr/bin/sort
	TAIL=/usr/bin/tail
	DATE=/usr/bin/date
	DIRNAME=/usr/bin/dirname
	BASENAME=/usr/bin/basename
	TOUCH=/usr/bin/touch
	CUT=/usr/bin/cut
	PKGINFO=/usr/bin/pkginfo
	MOUNT=/usr/sbin/mount
	ZONEADM=/usr/sbin/zoneadm
	FGREP=/usr/bin/fgrep
	TIMESTAMP=$($DATE +"%Y_%m_%d")
	
	
	# Files & Dirs	
	IMAGE="/cdrom/Solaris_10"
	PATCHDIR="$IMAGE/UpgradePatches"
	WORKDIR="/tmp/$($BASENAME $PROGNAME).$$"
	PKGDIR="$IMAGE/Product"
	LOGDIR="$ROOT/var/sadm/system/logs"
	FAILEDPKGS="$ROOT/var/sadm/system/data/upgrade_failed_pkgadds"
	RESTART="$ROOT/var/sadm/system/admin/upgrade_restart"
	PATCHPKGLOGDIR="$LOGDIR/patch_pkg_logs"
	LOG="$LOGDIR/upgrade_log"
	PATCHLOG="$PATCHPKGLOGDIR/patch_log"
	PKGLOG="$PATCHPKGLOGDIR/pkg_log"
	ANALYSISLOG="$LOGDIR/analysis_log"
	PATCH_PROGRESS_LOG="$LOGDIR/patch_progress"
	ORIGPATCHES="$PATCHPKGLOGDIR/orig_patches"
	ADMINFILE=/tmp/admin.dflt.$$
	ADMIN_DEPEND_FILE=/tmp/admin.depend.dflt.$$
	UPGRADE_PROGRESS=/tmp/upg_prog	
}

Usage ()
{
cat << EOT

Usage:
	patchUpdate -z <pathToFileContainingListOfNonglobalZones> [-r] [-a] [-v]
		-p <PPID> -n <NewPkgsToInstall> -R <altRoot>

Options:
	a)	Analyze system

	n)	A file containing the pkgs to install

	p)	Process ID of parent

	r)	If installation was halted pass -r to resume the installation.

	R)	Alternate root

	v)	Debug the script
	
	z)	File of non-global Zones that are to be installed.

EOT
}

# Description:
#	Generate correct pkg and patch lists when the upgrade is restarted.
#   
# Parameters:
#   None
#
# Returns:
#	0 - Success
#	1 - Failure
#	
# SideEffects:
#	Sets global PATCHES and PKGS variables.
#

CreateUpgradeLog ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer ctr=0

	typeset -r logdir="$ROOT/var/sadm/system/logs"
	typeset -r log="upgrade_log"
	typeset logTS="upgrade_log_$TIMESTAMP"
	typeset tmplogTS=$logTS
	
	(
		cd $logdir || return 1
		
		if [[ ! -f $logTS && ! -h $log ]]; then
			$TOUCH $logTS || return 1
		elif [[ -h $log ]]; then
			$RM $log || return 1
			while [[ -f "$tmplogTS" ]]; do
				ctr=ctr+1
				tmplogTS=""$logTS"_$ctr"
			done
			if (( ctr == 0 )); then
				logTS="$logTS"
			else
				logTS=""$logTS"_$ctr"
			fi
			$TOUCH $logTS || return 1
		fi
	
		$LN -s $logTS $log || return 1
	)
	
	return 0
}

# Description:
#	Quit the upgrade and cleanup temporary files.
#   
# Parameters:
#   $1 - code to exit script with. 
#
# Returns:
#	0 - Success
#	!0 - Failure
#	
# SideEffects:
#	Installs upgrade related files if upgrade is successful.
#

Quit () 
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer exitCode=$1
	
	$RM -fr $WORKDIR

	if (( exitCode == 0 )); then
		PrintLog "$(gettext "Upgrade was successful.")"
	else
		PrintLog "$(gettext "Upgrade failed.")"
	fi

	if (( exitCode == 0 || exitCode == 2 )); then
		[[ -d "$PATCHPKGLOGDIR" ]] && $RM -fr $PATCHPKGLOGDIR
		$RM -f $PATCH_PROGRESS_LOG
		[[ "$ANALYZE" == "false" ]] && $RM -f $ANALYSISLOG
	fi

	$RM -f $ADMINFILE $ADMIN_DEPEND_FILE $UPGRADE_PROGRESS $RESTART

	exit $exitCode
}

# Description:
#	Generate list of patches to install to both the global and non-global zones.
#   
# Parameters:
#   None
#
# Returns:
#	0 - Success
#	1 - Failure
#	
# SideEffects:
#	none.
#

GenerateListOfPatches ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	typeset -r caller=$1
	typeset -r noPatches="None."
	integer retCode=0

	if [[ -f $PATCHLOG ]]; then
		$RM -f $PATCHLOG
	else
		if ! CreateFile $PATCHLOG; then
			return 1
		fi
	fi
	
	# Get the list of patches for all zones.

	$PATCHADD -a -q -R $ROOT -M $PATCHDIR > $PATCHLOG 2>&1	\
			|| retCode=$?

	if [[ "$caller" == "InstallPatches" ]]; then
		if (( retCode != 0 && retCode != 8 )); then
			$CAT $PATCHLOG >> $LOG
			PatchAnalysisMsgs $retCode
			return $retCode
		fi		
	fi
		
	patches=$($GREP "Approved patches:" $PATCHLOG \
		| $HEAD -n 1 | $CUT -d: -f2)
	
	patches=$($ECHO $patches)
	
	if [[ -z "$patches" ]]; then
		PrintLog "$(gettext "No patches are applicable for installation.")"
		$CAT $PATCHLOG >> $LOG
		return 1
	fi
	
	[[ "$patches" != "$noPatches" ]] && $ECHO $patches || $ECHO ""
	
	return 0
}

# Description:
#	Generate list of packages to install.
#
# Parameters:
#   None
#
# Returns:
#	0 - Success
#	1 - Failure
#
# SideEffects:
#	None.
#

GenerateListOfPackages ()
{
	[[ "$DEBUG" == "true" ]] && set -x
		
	[[ ! -f "$PKGLIST" ]] && return 1
		
	$ECHO $($CAT $PKGLIST)
	
	return 0
}

# Description:
#	Install patches to both global and non-global zones. If a patch fails to
#	install, backout the previously installed patches and remove the
#	installed packages.
#   
# Parameters:
#   None
#
# Returns:
#	0  - Success
#	!0 - Failure
#	
# SideEffects:
#	backs out patches and packages if a patch fails to install.
#

InstallPatches ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer retCode=0

	PATCHES=$(GenerateListOfPatches "InstallPatches")
	if [[ $? != 0 ]]; then
		PrintLog "$(gettext "Generating list of patches failed.")"
		return 1
	fi	

	for zone in $ZONES; do
		[[ "$zone" == "global" ]] && continue
		$ZONEADM -R $ROOT -z $zone mount
	done

	for patch in $PATCHES; do			
		LogProgress local_patchadd $patch
		if ! $PATCHADD -q -e -R $ROOT $PATCHDIR/$patch \
				>> $LOG 2>&1; then
			PrintLog "$(gettext "Patchadd failed installing $patch.")"
			
			if IsResume; then
				# Remove all patches that may have been installed the
				# first time.
				installedPatchesFile=$ORIGPATCHES
			fi
			
			BackoutPatches $patch $installedPatchesFile	|| retCode=$?

			if (( retCode == 0 )); then
				if ! RemovePackages "$PKGS" "backoutpkgs"; then
					PrintLog "$(gettext "Unable to remove packages.")"
					retCode=1
				else
					PrintLog "$(gettext "Your system has been restored successfully.")"
					retCode=2
				fi
			else
				PrintLog "$(gettext "Unable to remove patches.")"
			fi
			break
		else
			installedPatchesFile=$(LogPatchComplete $patch)
		fi
	done

	for zone in $ZONES; do
		[[ "$zone" == "global" ]] && continue
		$ZONEADM -R $ROOT -z $zone unmount
	done

	return $retCode
}

# Description:
#	Backout all patches that were installed by this script. This is only
# 	called if a patch fails to install.
#   
# Parameters:
#   $1 - The patch that failed to install
#   $2 - The list of the patches successfully installed.
#
# Returns:
#	0  - Success
#	!0 - Failure
#	
# SideEffects:
#	Sets global COMP_ACTIONS variable.
#

BackoutPatches () 
{
	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r currentPatch=$1
	typeset listOfinstalledPatches=$2
	integer retCode=0

	[[ -z "$currentPatch" ]] && return 1
	
	if [[ -f $listOfinstalledPatches ]]; then
		listOfinstalledPatches=$($CAT $listOfinstalledPatches)
		for patch in $listOfinstalledPatches; do
			revOrder="$patch $revOrder"
		done
		listOfinstalledPatches=$($ECHO $revOrder)
	fi

	for zone in $ZONES; do
		[[ "$zone" == "global" ]] && continue
		$ZONEADM -R $ROOT -z $zone mount
	done
	
	for patch in $listOfinstalledPatches $currentPatch; do				
		installedPatch=$($PATCHADD -R $ROOT -p \
			| $GREP "Patch: $patch")
			
		if [[ -n "$installedPatch" && -d "$ROOT/var/sadm/patch/$patch" ]]; then
			LogProgress remove_patch $patch
			$PATCHRM -q -R $ROOT $patch >> $LOG 2>&1 || \
				retCode=$?
			if (( retCode != 0 )); then
				PrintLog "$(gettext "Patchrm of $patch in Zone $zone failed to backout")"
				break
			fi
		fi
	done

	for zone in $ZONES; do
		[[ "$zone" == "global" ]] && continue
		$ZONEADM -R $ROOT -z $zone unmount
	done


	return $retCode
}

# Description:
#	Update file read by pfinstall to show status of the upgrade.
#   
# Parameters:
#   $1 - The last command completed
#   $2 - The action performed, (patchadd, pkgadd, analysis)
#   $3 - Name of the Zone.
#
# Returns:
#	None
#	
# SideEffects:
#	Sets global LASTCMD, LAST_ACTION, TOTAL_ACTIONS,
#	COMPLETED_ACTIONS, LAST_ZONE variables.
#

LogProgress () {

	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r cmd=$1
	typeset -r action=$2
	
	if [[ "$cmd" != "remove_patch" && "$cmd" != "backoutpkgs" ]]; then
		COMP_ACTIONS=$(($COMP_ACTIONS+1))
	elif (( COMP_ACTIONS > 0 )); then
		COMP_ACTIONS=$(($COMP_ACTIONS-1))
	fi

	if [ "$cmd" = "none" ]; then
		doSig=no
	else
		doSig=yes
	fi

	# Make sure progress bar shows 100% complete at the end of a
	# successful upgrade.

	if [[ "$cmd" == "modify_files" ]]; then
		TOTAL_ACTIONS=$COMP_ACTIONS
	elif (( COMP_ACTIONS > TOTAL_ACTIONS )); then
		TOTAL_ACTIONS=$(($TOTAL_ACTIONS+1))
	fi

	if [ "$doSig" = "yes" ]; then
		$ECHO $cmd $action $TOTAL_ACTIONS $COMP_ACTIONS > $UPGRADE_PROGRESS
	fi
	
	if [[ ! -f "$RESTART" ]]; then
		if ! CreateFile $RESTART; then
			return 1
		fi
	fi
	

	$ECHO "LASTCMD=$cmd\nTIMESTAMP=$TIMESTAMP" > $RESTART
	$ECHO "LAST_ACTION=$action" >> $RESTART
	$ECHO "TOTAL_ACTIONS=$TOTAL_ACTIONS" >> $RESTART
	$ECHO "COMPLETED_ACTIONS=$COMP_ACTIONS" >> $RESTART
		
	if [ "$doSig" = "yes" -a -n "$PPID" ]; then
		kill -USR1 $PPID
	fi
}

# Description:
#	Generate correct pkg and patch lists when the upgrade is restarted.
#   
# Parameters:
#   None
#
# Returns:
#	0 - Success
#	1 - Failure
#	
# SideEffects:
#	Sets global PATCHES and PKGS variables.
#

GenerateNewPatchPkgLists ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer totalActions=0
	integer compActions=0
	integer ctr=0
	typeset lastCmd=""
	typeset tmp=""

	[[ ! -f "$RESTART" ]] && return 1

	lastCmd=$($GREP LASTCMD $RESTART | $CUT -d= -f2)
	compActions=$($GREP COMPLETED_ACTIONS $RESTART | $CUT -d= -f2)
	totalActions=$($GREP TOTAL_ACTIONS $RESTART | $CUT -d= -f2)
	lastAction=$($GREP LAST_ACTION $RESTART | $CUT -d= -f2)

	if (( compActions < totalActions )); then
		case $lastCmd in
			zone_analysis)
				RedoAnalysis
				;;
			patch_space)
				RedoAnalysis
				;;
			pkg_space)
				RedoAnalysis
				;;
			patch_dependency)
				RedoAnalysis
				;;
			pkg_dependency)
				RedoAnalysis
				;;
			patchadd)
				SetDoPatchInstallation
				;;
			pkgadd)		
				lineNum=$($GREP -n $lastAction $PKGLIST | $CUT -d: -f1)

				for pkg in $PKGS; do
					ctr=ctr+1
					if (( ctr >= lineNum )); then
						tmp="$tmp $pkgs"
					fi
				done

				PKGS=$($ECHO $tmp)
				
				SetDoPkgInstallation
				;;
			patchrm) ;;
			*)
				SetDoPkgInstallation
				SetDoPatchInstallation
				;;
		esac
		
		TOTAL_ACTIONS=$(($TOTAL_ACTIONS - $compActions))
		COMP_ACTIONS=0
	fi

	return 0
}

# Description:
#	Remove previously installed packages from global and all non-global
#	Zones if one fails to install.
# Parameters:
#	$1 - List of packages to remove.
#
# Returns:
#	0  - Success
#	!0 - Failure
#	
# SideEffects:
#	None.
#

RemovePackages ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	typeset -r pkgs=$1
	typeset -r cmdType=$2
	integer retCode=0
	
	for pkg in $pkgs; do
		LogProgress $cmdType $pkg
		if IsPkgAlreadyInstalled $pkg global; then
			$PKGRM -n -a $ADMINFILE -R $ROOT $pkg >> $LOG 2>&1
			retCode=$?
			if (( retCode != 0 )); then
				PrintLog "$(gettext "pkgrm failed. return code = $retCode")"
			fi
		fi
	done
	
	return $retCode
}

# Description:
#	Create a list of pkgs to remove and install if the package exists. 
# Parameters:
#
# Returns:
#	None
#	
# SideEffects:
#	Updates UPDATEDPKGS global variable.
#

UpdatePkgsIfPkgExists ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	typeset conditionalPkgs=""

	for pkg in $UPDATE_IF_EXIST; do
		if IsPkgAlreadyInstalled $pkg "global"; then
			conditionalPkgs="$conditionalPkgs $pkg"
		fi
	done

	if [[ -n "$conditionalPkgs" ]]; then
		UPDATEDPKGS="$UPDATEDPKGS $conditionalPkgs"
	fi
}		

# Description:
#	Install all packages to global and non-global Zones.
#
# Parameters:
#	None.
#
# Returns:
#	0  - Success
#	!0 - Failure
#	
# SideEffects:
#	Calls RemovePackages() to remove package if installation fails.
#

InstallUpdatedPackages ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	integer retCode=0		

	RemovePackages "$UPDATEDPKGS" "pkgrm" || retCode=$?
	if (( retCode != 0 )); then
		PrintLog "$(gettext "Unable to remove packages")"
		return 2
	fi
	
	MVDPKGS=$PKGS

	PKGS="$UPDATEDPKGS"

	InstallNewPackages	|| retCode=$?
	if (( retCode != 0 )); then
		PrintLog "$(gettext "Installation of packages failed.")"
		return 2
	fi

	PKGS=$MVDPKGS
	
	return $retCode
}

# Description:
#	Install all packages to global and non-global Zones.
#
# Parameters:
#	None.
#
# Returns:
#	0  - Success
#	!0 - Failure
#	
# SideEffects:
#	Calls RemovePackages() to removes package if installation fails.
#

InstallNewPackages ()
{
	[[ "$DEBUG" == "true" ]] && set -x
		
	typeset installedPkgs=""
	integer retCode=0
	integer retCodeRemove=0
	
	for zone in $ZONES; do
		[[ "$zone" == "global" ]] && continue
		$ZONEADM -R $ROOT -z $zone mount
	done


	for pkg in $PKGS; do
		LogProgress local_pkgadd $pkg

		if [[ ! -f $PKGDIR/$pkg/pkginfo ]]; then
			continue
		fi

		# A pkg should not be installed in most cases. If it is we don't
		# want to install over it in case it was installed by a patch
		# that could contain later bits.
		
		if IsPkgAlreadyInstalled $pkg global; then
			PrintLog "$(gettext "\nPackage $pkg is already installed.")"
			continue
		fi
		PrintLog "$(gettext "\nDoing pkgadd of $pkg.")"
		
		$PKGADD -R $ROOT -S -M -d "$PKGDIR" -n -a $ADMINFILE $pkg \
				>> $LOG 2>&1	|| retCode=$?
		
		if (( retCode == 0 )); then
			installedPkgs="$pkg $installedPkgs"
		else
			PrintLog "$(gettext "pkgadd failed. return code = $retCode")"
			PrintLog "$(gettext "Attempting to remove the following packages:")"
			PrintLog "$(gettext "$pkg $installedPkgs")"

			$ECHO "$pkg failed to install." >> $FAILEDPKGS

			RemovePackages "$pkg $installedPkgs" "backoutpkgs" || \
				retCodeRemove=$?
			if (( retCodeRemove != 0 )); then
				PrintLog "$(gettext "Unable to remove packages")"
				retCode=2
			fi
			
			break
		fi
	done

	for zone in $ZONES; do
		[[ "$zone" == "global" ]] && continue
		$ZONEADM -R $ROOT -z $zone unmount
	done
	
	(( retCodeRemove == 0 )) && return $retCode || return $retCodeRemove
}

# Description:
#	See if the to be installed package is already installed.
#
# Parameters:
#	$1 - The package to be isntalled.
#	$2 - The Zone to check
#
# Returns:
#	0 - Success
#	1 - Failure
#	
# SideEffects:
#	None.
#

IsPkgAlreadyInstalled () 
{
	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r pkg=$1
	typeset -r zone=$2
	typeset -r pkgPath="/var/sadm/pkg"
	typeset pkginst=""
	
	# Need to detect pkg existence in a zone without using
	# the pkg{param|info|chk} commands. since they are not Zone aware.
	
	if [[ "$zone" == "global" ]]; then
		typeset -r pkgAbbrev=$(pkgparam -f $ROOT/$pkgPath/$pkg/pkginfo \
				PKG 2>/dev/null)
		typeset -r pkgArch=$(pkgparam -f $ROOT/$pkgPath/$pkg/pkginfo \
				ARCH 2>/dev/null)
		typeset -r pkgVer=$(pkgparam -f $ROOT/$pkgPath/$pkg/pkginfo \
				VERSION 2>/dev/null)
	
		typeset -r pkginst=$($PKGINFO -R "$ROOT" -a "$pkgArch" \
			-v "$pkgVer" $pkg.\* 2>/dev/null | $NAWK '{print $2}')
	else
		typeset zonepath=$($ZONEADM list -p | $GREP $zone | $CUT -d: -f4)
		
		if [[ -n "$zonepath" ]]; then
			zonepath=$zonepath/root
		
			typeset -r pkgAbbrev=$(pkgparam -f \
				$ROOT/$zonepath/$pkgPath/$pkg/pkginfo PKG 2>/dev/null)
			typeset -r pkgArch=$(pkgparam -f \
				$ROOT/$zonepath/$pkgPath/$pkg/pkginfo ARCH 2>/dev/null)
			typeset -r pkgVer=$(pkgparam -f \
				$ROOT/$zonepath/$pkgPath/$pkg/pkginfo VERSION 2>/dev/null)
		
			typeset -r pkginst=$($EGREP -e PKG=$pkgAbbrev \
					-e ARCH=$pkgArch -e VERSION=$pkgVer \
					$ROOT/$zonepath/$pkgPath/$pkg/pkginfo \
					2>/dev/null)
		fi
	fi
	
	[[ -n "$pkginst" ]] && return 0 || return 1
}

# Description:
#	Print message to log file based on return code from patchadd.
#
# Parameters:
#	$1 - return code from patchadd
#
# Returns:
#	None.
#	
# SideEffects:
#	None.
#

PatchAnalysisMsgs ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r retCode=$1

	# Pdo dryrun return codes:
	# FAILURE=0
	# SUCCESS=1
	# REQUIRE_FAILURE=2
	# INCOMPATIBLE_FAILURE=3
	# OBSOLETE_FAILURE=4
	# PATCHADD_FAILURE=5
	# PATCHRM_FAILURE=6
	# REVISION__FAILURE=7
	# REQD_PKGS_FAILURE=8
	
	case $retCode in
		1) 	PrintLog "$(gettext "\nPatchadd failed to complete the dependency checks.\n")" ;;
		2) 	PrintLog "$(gettext "\nA required patch is not installed on this system.\n")" ;;
		3) 	PrintLog "$(gettext "\nAn incompatible patch exists on this system.\n")" ;;
		4) 	PrintLog "$(gettext "\nAn obsolete patch is installed on this system.\n")" ;;
		5) 	PrintLog "$(gettext "\nPatchadd failed to inatall a patch.\n")" ;;
		6) 	PrintLog "$(gettext "\nPatchrm failed to backout a patch.\n")" ;;
		7) 	PrintLog "$(gettext "\nA later revision of a patch is already installed.\n")" ;;
		8) 	PrintLog "$(gettext "\nA required package is not installed on this system.\n")" ;;
		*)  PrintLog "$(gettext "\nInstallation cannot continue.\n")" ;;
	esac
	
	PrintLog "$(gettext "Installation cannot continue.\n")"
}

# Description:
#	Main control for analyzing system,
#
# Parameters:
#	None.
#
# Returns:
#	0  - Success
#	!0 - Failure
#	
# SideEffects:
#	None.
#

AnalyzeSystem ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	# return code meanings
	# 3  upgrade failed, not enough File System space available 
	# 4  upgrade failed, patch dependency failed. 
	# 5  upgrade failed, pkg dependency failed. 

	if IsAnalysisComplete; then
		return 0
	fi

	TOTAL_ACTIONS=$(CalculateTotalAnalysisActions)
	
	if ! FSSpaceAnalysis; then
		return 3
	fi

	if ! PkgAnalysis; then
		return 5
	fi
	
	if ! AnalysisComplete; then
		return 2
	fi
	
	return 0
}

# Description:
#	See if enough space is available for installing the patches and packages
#   in any given File System.
# Parameters:
#	None.
#
# Returns:
#	0 - Success
#	1 - Failure
#	
# SideEffects:
#	None.
#

FSSpaceAnalysis ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer pspoolfudgefactor=200
	integer blockSize=512
	integer KB=1024
	integer MB=1048576
	integer reqSpace=750000		# 750 MB
	integer totalPkgSize=0
	integer ctr=0
	integer pkgSize=0
	integer retCode=0
	integer wholeRootNum=1
	integer fudgeFactor=200000	# 200 MB
	integer totalPkgSize=0
	integer patchPkgSize=0
	integer allPatchPkgs=0
	integer sizeNeededPerZone=0
	typeset -r interim_space="/tmp/interim_space"
	typeset -r zonesIndex="$ROOT/etc/zones/index"
	typeset -r productDir=PKGDIR
	typeset -r FSspaceFile=$WORKDIR/FSspaceFile
	
	# For patches we estimate how big the backout package is

	for pkg in $PKGS; do
		LogProgress pkg_space $pkg
		pkgSize=$($HEAD -n 1 $PKGDIR/$pkg/pkgmap | \
			$NAWK '{print $3}')
		totalPkgSize=$(($pkgSize + $totalPkgSize))
	done

	pspoolfudgefactor=$(($pspoolfudgefactor * $NUMPKGS))
	totalPkgSize=$(($totalPkgSize * $blockSize + $pspoolfudgefactor))

	# Get the sizes for all the patch pkgs in the global zone only
	# We estimate the size of the bacout packages based on the size
	# of the patch pkgs.
	
	for patch in $PATCHES; do
		LogProgress patch_space $patch
		patchPkgSize=0
		for patchPkg in $PATCHDIR/$patch/*/pkgmap; do
			pkgSize=$($HEAD -n 1 $patchPkg | \
				$NAWK '{print $3}')
			patchPkgSize=$(($pkgSize + $patchPkgSize))
		done
		allPatchPkgs=$(($patchPkgSize + $allPatchPkgs))
	done

	allPatchPkgs=$(($allPatchPkgs * $blockSize))

	sizeNeededPerZone=$(($allPatchPkgs + $totalPkgSize + $fudgeFactor))

	# Find any whole root zones. 

	rootFS=$($DF -kb $ROOT | $TAIL -1 | $NAWK '{print $6}')
	fileSystems=$($DF -k | grep "^/dev/dsk/c" | awk '{print $6}')

	if ! CreateFile "$FSspaceFile"; then
		PrintLog "$(gettext "Cannot create $FSspaceFile.")"
	fi

	for zone in $ZONES; do
		[[ "$zone" == "global" ]] && continue

		LogProgress zone_analysis $zone

		# Need to manually get the zones from the $ROOT/etc/index file
		# since zoneadm and zonecfg are not $ROOT aware.

		zonepath=$($GREP "^$zone:" $zonesIndex | $CUT -d: -f3)
		zonepath=$ROOT/$zonepath

		if [[ -z "$zonepath" ]]; then
			continue
		fi

		# See if anything exists under $zonepath
		# At the point this code runs no FS's should be LOFS mounted so
		# we can check to see key directories are empty and surmize
		# that a sparse zone exists if the directories are empty.

		if [[ -d "$zonepath/root/usr/sbin" && \
				 -f "$zonepath/root/lib/ld.so.1" ]]; then
		
			# See if zone is mounted on its own FS.
			zoneFS=$($DF -kb $zonepath | $TAIL -1 | $NAWK '{print $6}')
			
			ctr=0			
			
			for fs in $fileSystems; do
				if [[ "$zoneFS" == "$rootFS" ]]; then
					wholeRootNum=wholeRootNum+1
					break
				fi

				if [[ "$zoneFS" == "$fs" ]]; then
					if [[ "${zoneSizeNeeded[$ctr]}" == "" ]]; then
						zoneSizeNeeded[$ctr]=$sizeNeededPerZone
					else
						zoneSizeNeeded[$ctr]=$((${zoneSizeNeeded[$ctr]} + \
							$sizeNeededPerZone))
					fi
					if ! IsEnoughSpaceAvail "${zoneSizeNeeded[$ctr]}" \
							"$zoneFS" "$FSspaceFile"; then
						retCode=2
					break
					fi
				fi
				ctr=ctr+1
			done
		fi
	done

	if [[ $retCode == 2 && -s "$FSspaceFile" ]]; then

		PrintLog "$($CAT $FSspaceFile)"
		$ECHO "$((${zoneSizeNeeded[$ctr]} / $MB))" > $interim_space

	else	

		# rootSizeNeeded = size of all patch pkgs + 
		#                  * # whole root zones   +
		#                  install factor
	
		LogProgress zone_analysis global
	
		rootSizeNeeded=$(($sizeNeededPerZone * $wholeRootNum ))
		
		if ! IsEnoughSpaceAvail "$rootSizeNeeded" "$rootFS" \
				"$FSspaceFile"; then
			
			$ECHO "$(($rootSizeNeeded / $MB))" > $interim_space
			
			retCode=2
		fi
	
		if [[ $retCode == 2 && -s "$FSspaceFile" ]]; then
			PrintLog "$($CAT $FSspaceFile)"
		fi
	fi
	
	return $retCode
}

# Description:
#	See if enough space is available for installing the patches and packages
#   in any given File System.
# Parameters:
#	$1 - size needed to install the patches and packages
#	$2 - File System to check
#
# Returns:
#	0 - Success
#	1 - Failure
#	
# SideEffects:
#	None.
#

IsEnoughSpaceAvail ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer sizeNeeded=$1
	typeset -r fileSys=$2
	typeset -r FSspaceFile=$3
	
	integer sizeAvail=0
	integer KB=1024
	integer MB=1048576

	# Calculate the size of the File System passed in.
	
	sizeAvail=$($DF -kb $fileSys | $TAIL -1 | $NAWK '{print $4}')
	
	sizeAvail=$(($sizeAvail * $KB))
	
	if (( sizeNeeded > sizeAvail )); then
		if (( sizeAvail < KB )); then
			$ECHO "$(/usr/bin/gettext "Upgrade has less then $KB KB of free space available in $fileSys.")" > $FSspaceFile
		elif (( sizeAvail < MB )); then
			$ECHO "$(/usr/bin/gettext "Upgrade has less then $MB MB of free space available in $fileSys.")" > $FSspaceFile
		fi
		sizeAvail=$(($sizeAvail / $MB))
		sizeNeeded=$(($sizeNeeded / $MB))
		$ECHO "$(gettext "Upgrade needs at least $sizeNeeded MBs of free space in $fileSys.\n")" > $FSspaceFile
		$ECHO "$(gettext "$fileSys currently has $sizeAvail MBs available.")" >> $FSspaceFile
		return 1
	fi
		
	return 0
}

# Description:
#	Detect if any patches that are part of the patch list have installation
#   issues. i.e. required patches not installed....
# Parameters:
#	None.
#
# Returns:
#	0  - Success
#	!0  - Failure
#	
# SideEffects:
#	None.
#

PatchAnalysis ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	integer retCode=0
	
	LogProgress patch_dependency global
				
	$PATCHADD -a -q -R $ROOT -M \
		$PATCHDIR > $PATCHLOG 2>&1	|| retCode=$?
	if (( retCode != 0 )); then
		$CAT $PATCHLOG >> $LOG
		PatchAnalysisMsgs $retCode
	fi		
	
	return $retCode
}

# Description:
#	Detect if any packages have dependencies not met
#
# Parameters:
#	None.
#
# Returns:
#	0  - Success
#	!0 - Failure
#	
# SideEffects:
#	None.
#

PkgAnalysis ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	integer retCode=0

	for pkg in $PKGS; do
		LogProgress pkg_dependency $pkg
		
		if [[ ! -f $PKGDIR/$pkg/pkginfo ]]; then
			continue
		fi		

		if IsPkgAlreadyInstalled $pkg "global"; then
			PrintLog "$(gettext "\nPackage $pkg is already installed.")"
			continue
		fi
		if ! $PKGINSTALL -O "preinstallcheck" -R $ROOT \
			-n -a $ADMIN_DEPEND_FILE -S -M -t "$PKGDIR"  \
			$pkg > $PKGLOG 2>&1; then
			PrintLog "$(gettext "Pkgadd failed checking dependencies for $pkg.\n")"
			$CAT $PKGLOG >> $LOG
			retCode=1
		fi
		
		if ! ParsePkgLog $PKGLOG $pkg; then
			PrintLog "$(gettext "Package $pkg failed to pass dependency checking.\n")"
			retCode=1
		fi
	done

	return $retCode
}

# Description:
#	Update the files needed to make the system appear that it is upgraded.
#
# Parameters:
#	None.
#
# Returns:
#	0 - Success
#	1 - Failure
#	
# SideEffects:
#	None.
#

UpdateSystemFiles () 
{
	[[ "$DEBUG" == "true" ]] && set -x

	# Files needing modification after successfull installation.
	typeset -r cluster="/var/sadm/system/admin/cluster"
	typeset -r instRelease="/var/sadm/system/admin/inst_release"
	typeset -r clustertoc="/var/sadm/system/admin/.clustertoc"
	typeset -r platform="/var/sadm/system/admin/.platform"
	typeset -r servicesDir="/var/sadm/system/admin/services"
	typeset -r locales_installed="/var/sadm/system/data/locales_installed"
	typeset -r dataDir="/var/sadm/system/data"
	typeset -r reconfigure="/reconfigure"
	typeset -r installDataDir="/var/sadm/install_data"
	typeset -r adminDir="/var/sadm/system/admin"
	typeset -r softinfo="/var/sadm/softinfo"
	
	typeset -r uname_p=$(uname -p)
	typeset -r uname_i=$(uname -i)

	typeset -r oldClusterTOC="$installDataDir/.clustertoc"
	typeset -r clusterTOC="$adminDir/.clustertoc"
	typeset -r cdClusterTOC=$IMAGE/Product/locale/C/.clustertoc
	
	integer retCode=0

	LogProgress modify_files $zone

	globalZonePath="$ROOT"
	
	[[ ! -d "$globalZonePath/$servicesDir" ]] && \
		$MD -p -m 755 $globalZonePath/$servicesDir

	[[ ! -d "$globalZonePath/$dataDir" ]] && \
		$MD -p -m 755 $globalZonePath/$dataDir

	[[ ! -d "$globalZonePath/$installDataDir" ]] && \
		$MD -p -m 755 $globalZonePath/$installDataDir
	
	$RM -f $globalZonePath$oldClusterTOC
	$RM -f $globalZonePath$clusterTOC
	
	if CreateDir $globalZonePath/$adminDir; then
		$CP $cdClusterTOC $globalZonePath/$clusterTOC || retCode=1
	else
		retCode=1
	fi

	$RM -f $globalZonePath/var/sadm/install_data/CLUSTER
			
	if [ ! -h $globalZonePath/$instRelease ]; then
		$RM -f $globalZonePath/$instRelease
	fi
	$ECHO "OS=Solaris" > $globalZonePath/$instRelease
	$ECHO "VERSION=10" >> $globalZonePath/$instRelease
	$ECHO "REV=0" >> $globalZonePath/$instRelease
	$CHMOD 644 $globalZonePath/$instRelease
	
	$RM -f $globalZonePath/$servicesDir/Solaris_*
	$RM -f $globalZonePath/$softinfo/Solaris_*
	$ECHO "OS=Solaris" > $globalZonePath/$servicesDir/Solaris_10
	$ECHO "VERSION=10" >> $globalZonePath/$servicesDir/Solaris_10
	$ECHO "REV=0" >> $globalZonePath/$servicesDir/Solaris_10
	$CHMOD 644 $globalZonePath/$servicesDir/Solaris_10

	$RM -f $globalZonePath/$platform
	$TOUCH $globalZonePath/$platform
	$CHMOD 644 $globalZonePath/$platform

	if [ ! -h $globalZonePath/$locales_installed ] ; then
		$RM -f $globalZonePath/$locales_installed
	fi
	$ECHO "GEOS=" > $globalZonePath/$locales_installed
	$ECHO "LOCALES=" >> $globalZonePath/$locales_installed
	$CHMOD 644 $globalZonePath/$locales_installed

	cat >> $globalZonePath/$platform << EOF
PLATFORM_GROUP="$uname_i"
INST_ARCH="$uname_p"
PLATFORM_NAME="$uname_i"
PLATFORM_ID="$uname_i"
IN_PLATFORM_GROUP="$uname_i"
EOF

	$TOUCH $globalZonePath/$reconfigure	|| retCode=1
	
	return $retCode
}

# Description:
#	Generate the package, patch and Zone lists.
#
# Parameters:
#	$1 - size needed to install the patches and packages
#	$2 - File System to check
#
# Returns:
#	0 - Success
#	1 - Failure
#	
# SideEffects:
#	Sets globals: ZONES, NUMZONES, PKGS, NUMPKGS, 
#				PATCHES, NUMPATCHES, TOTAL_ACTIONS.
#

GeneratePatchPkgZoneLists ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	ZONES="global $($CAT $NONGLOBALZONELIST)"
	NUMZONES=$(GenerateNumZones)

	PKGS=$(GenerateListOfPackages)
	if [[ $? != 0 ]]; then
		PrintLog "$(gettext "Generating list of packages failed.")"
		return 1
	fi
	
	NUMPKGS=$(GenerateNumPackages)

	UpdatePkgsIfPkgExists

	NUMUPDATEDPKGS=$(GenerateUpdatedPackages)

	NUMREMOVEONLYPKGS=$(GenerateRemoveOnlyPackages)

	PATCHES=$(GenerateListOfPatches "GeneratePatchPkgZoneLists")
	if [[ $? != 0 ]]; then
		PrintLog "$(gettext "Generating list of patches failed.")"
		return 1
	fi	
	
	NUMPATCHES=$(GenerateNumPatches)
	if [[ $? != 0 ]]; then
		PrintLog "$(gettext "Generating number of patches failed.")"
		return 1
	fi

	[[ "$ANALYZE" != "true" ]] && TOTAL_ACTIONS=$(CalculateTotalActions)

	return 0
}

# Description:
#	Parse the pkg log file to see if any dependent pkgs are not installed.
#
# Parameters:
#	$1 - file to parse
#	$2 - The pkg to search for in the file
#
# Returns:
#	0  - Success
#	!0 - Failure
#	
# SideEffects:
#	None.
#

ParsePkgLog ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r file=$1
	typeset -r pkg=$2
	integer retCode=0
	integer ctr=0
	
	# Search for:
	#	prerequisite-installed=<pkg>
	
	if [[ ! -f $file ]]; then
		return 1
	fi
	
	typeset -r preReqPkg=$($GREP "prerequisite-installed" $file | \
			$CUT -d'=' -f2)
	
	if [[ -n "$preReqPkg" ]]; then
		for pPkg in $preReqPkg; do
			# If a prerequisite pkg is in the pkglist then we can 
			# ignore the error since it will be installed.
			if ! $GREP $pPkg $PKGLIST > /dev/null 2>&1; then
				if (( ctr == 0 )); then
					PrintLog "$(gettext "\nThe following packages are required to be installed by $pkg:\n")"
					ctr=1
				fi
				PrintLog "$pPkg \n"
				retCode=1
			fi
		done
	fi

	return $retCode
}

CalculateTotalAnalysisActions ()
{
	[[ "$DEBUG" == "true" ]] && set -x
				
	# pkgs are checked for size needed and dependencies
	# patches are checked for size needed
	# Files system space is calculated per Zone
	
	$ECHO "$(($NUMPKGS * 2 + $NUMPATCHES + $NUMZONES))"
}

CreateAdminFiles () {
	[[ "$DEBUG" == "true" ]] && set -x

cat > $ADMINFILE << EOA
mail=
instance=overwrite
partial=nocheck
runlevel=nocheck
idepend=nocheck
rdepend=nocheck
space=nocheck
setuid=nocheck
conflict=nocheck
action=nocheck
basedir=default
EOA

cat > $ADMIN_DEPEND_FILE << EOF
mail=
instance=overwrite
partial=nocheck
runlevel=nocheck
idepend=quit
rdepend=quit
space=quit
setuid=nocheck
conflict=quit
action=nocheck
basedir=default
EOF

}

AnalysisComplete ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	if ! CreateFile $ANALYSISLOG; then
		return 1
	fi
	
	$ECHO "ANALYSIS_COMPLETE" >> $ANALYSISLOG
	
	return 0
}

GenerateNumPackages ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer numPkgs=0
	
	for pkg in $PKGS; do
		numPkgs=numPkgs+1
	done
	
	$ECHO $numPkgs
}

GenerateUpdatedPackages ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer numPkgs=0
	
	for pkg in $UPDATEDPKGS; do
		numPkgs=numPkgs+1
	done
	
	$ECHO $numPkgs
}

GenerateRemoveOnlyPackages ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer numPkgs=0
	
	for pkg in $REMOVEONLYPKGS; do
		numPkgs=numPkgs+1
	done
	
	$ECHO $numPkgs
}

GenerateNumZones ()
{
	[[ "$DEBUG" == "true" ]] && set -x

	integer numZones=0
	
	for zone in $ZONES; do
		numZones=numZones+1
	done
	
	$ECHO $numZones
}

GenerateNumPatches ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	integer numPatches=0
	
	for patch in $PATCHES; do
		numPatches=numPatches+1
	done
		
	$ECHO $numPatches 
}

RedoAnalysis () 
{
	$RM -f $ANALYSISLOG
	ANALYZE="true"
}

IsAnalysisComplete ()
{	
	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r analysisComplete=$($GREP "ANALYSIS_COMPLETE" \
			$ANALYSISLOG > /dev/null 2>&1)
	
	if [[ -n "$analysisComplete" ]]; then
		return 0
	else
		$RM -f $ANALYSISLOG
		return 1
	fi
}

CalculateTotalActions ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	# pkgs are installed one at a time
	# patches are installed one at a time
	# one action for modifying the system files on the global zone.
	

	$ECHO "$(($NUMPKGS + $NUMPATCHES + $NUMUPDATEDPKGS + \
		$NUMUPDATEDPKGS + $NUMREMOVEONLYPKGS + 1))"
}

ParseArgs ()
{
	while getopts 'am:n:p:qrR:vz:' char ; do
		case $char in
			a)	ANALYZE="true"
				;;
			n)	PKGLIST="$OPTARG"
				;;
			p)	PPID="$OPTARG"
				typeset -r tmp=`expr $OPTARG : '\([0-9][0-9]*\)'`
				if [ "$tmp" != "$OPTARG" ]; then
					$ECHO "invalid PID: $OPTARG"
					exit 1
				fi
				PPID=$tmp
				;;
			r)	SetResume
				;;
			R)	ROOT="$OPTARG"
				;;
			v)	DEBUG="true"
				;;
			z)	if [[ ! -f "$OPTARG" ]]; then
					PrintLog "$(gettext "File $OPTARG does not exist."))"
					return 1
				fi
				NONGLOBALZONELIST="$OPTARG"
				;;
			\?)	PrintLog "$(gettext "Unknown option $OPTARG")"
				return 1
				;;
			:)	PrintLog "$(gettext "Option -$OPTARG requires a value!")"
				return 1
				;;
		esac
	done

	shift OPTIND-1

	[[ -z "$ROOT" ]] && \
		PrintLog "$(gettext "-R must be defined")" && \
		Quit 2
	
	[[ -z "$PKGLIST" ]] && \
		PrintLog "$(gettext "A list to the packages to install must be defined")" && \
		Quit 2
	
	[[ -z "$PPID" ]] && \
		PrintLog "$(gettext "The parent process ID must be defined")" && \
		Quit 2
		
	return 0
}

AbortInstallation ()
{
	PrintLog "$(gettext "Upgrade is terminating due to users request")"
	Quit 2
}

PrintLog ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
		$ECHO "$@" >>$LOG	

	return 0
}

CreateFile () {

	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r file=$1

	if [[ ! -f $file ]]; then
		typeset -r dir=$($DIRNAME $file)
		if [[ ! -d $dir ]]; then
			$MD -p $dir || return 1
		fi
		$TOUCH $file || return 1
	fi
	
	return 0
}

CreateDir () {

	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r dir=$1

	if [[ ! -d $dir ]]; then
		$MD -p $dir || return 1
	fi
	
	return 0
}

LogPatchComplete () {

	[[ "$DEBUG" == "true" ]] && set -x

	typeset -r patch=$1

	if ! CreateFile "$PATCH_PROGRESS_LOG"; then
		return 1
	fi
	
	$ECHO $patch >> $PATCH_PROGRESS_LOG
	
	$ECHO $PATCH_PROGRESS_LOG
	
	return 0
}

SetResume ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	RESTARTINSTALLATION=1
}

IsResume ()
{		
	[[ "$DEBUG" == "true" ]] && set -x

	if [[ -f "$RESTART" && "$RESTARTINSTALLATION" == "1" ]]; then
		return 0
	else
		return 1
	fi
	
}

SaveOriginalPatchlists ()
{
	[[ "$DEBUG" == "true" ]] && set -x


	$RM -f $ORIGPATCHES
	
	if ! CreateFile $ORIGPATCHES; then
		PrintLog "$(gettext "Cannot create file $ORIGPATCHES.")"
		return 1
	fi
	
	for patch in $PATCHES; do
		$ECHO "$patch" >> $ORIGPATCHES
	done
				
	return 0
}

SetDoPatchInstallation ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	INSTALLPATCHES=1
}

SetDoPkgInstallation ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	INSTALLPKGS=1
}

DoPatchInstallation ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	(( INSTALLPATCHES == 1 )) && return 0 || return 1
}

DoPkgInstallation ()
{
	[[ "$DEBUG" == "true" ]] && set -x
	
	(( INSTALLPKGS == 1 )) && return 0 || return 1

}


##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Main control
##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# Exit status  Meaning          
# 
# 0    upgrade was successful     
# 1    upgrade failed and system needs to be restored from backup
# 2    upgrade failed and action is required by sysadmin before
#            attempting the upgrade again 
# 3    upgrade failed, not enough File System space available. No
#            changes have been made to the system.
# 4    upgrade failed, patch dependency failed. No
#           changes have been made to the system.
# 5    upgrade failed, pkg dependency failed. No
#           changes have been made to the system.

typeset PROGNAME=$0
typeset ROOT=""
typeset ANALYZE="false"
typeset RM=/usr/bin/rm

# Packages to remove and install

typeset UPDATEDPKGS="SUNWasman SUNWgldoc SUNWglh SUNWglrt SUNWj3cfg \
		SUNWj3dev SUNWj3dmo SUNWj3dvx SUNWj3jmp SUNWj3man SUNWj3rt \
		SUNWj3rtx SUNWlur SUNWluu  SUNWsolnm"

# Packages to remove

typeset REMOVEONLYPKGS="SUNWgldp SUNWglrtu SUNWglsr SUNWglsrz SUNWgldpx \
		SUNWglrtx SUNWglsrx SUNWjaxp SUNWxrgrt SUNWxrpcrt SUNWxsrt"

# Packages to remove and install only if they exist

typeset UPDATE_IF_EXIST="SUNWsom SUNWsogm SUNWsoam SUNWsoagm SUNWmlibe \
	    SUNWmlib SUNWmlibl SUNWmlibh SUNWmlibk SUNWfwdc SUNWfwdcu \
	    SUNWiqum SUNWiqjx"

integer INSTALLPKGS=0
integer COMP_ACTIONS=0
integer INSTALLPATCHES=0
integer RESTARTINSTALLATION=0
integer NUMPKGS=0
integer NUMUPDATEDPKGS=0
integer NUMREMOVEONLYPKGS=0
integer NUMZONES=0
integer pRetCode=0

trap 'AbortInstallation' INT

ParseArgs $@ || {
	Usage
	Quit 2
}

SetGlobals

if ! CreateDir $WORKDIR; then
	PrintLog "$(gettext "Unable to create $WORKDIR")"
	Quit 2
fi

CreateAdminFiles

if ! CreateUpgradeLog; then
	PrintLog "$(gettext "Unable to create $LOG")"
	Quit 2
fi

if ! GeneratePatchPkgZoneLists; then
	PrintLog "$(gettext "Generating patch/package/zone lists failed.\n")"
	Quit 2
fi

if IsResume; then
	if ! GenerateNewPatchPkgLists; then
		PrintLog "$(gettext "Unable to restart the upgrade.")"
		Quit 2
	fi
	PrintLog "$(gettext "Restarting upgrade:\n")"
else
	SetDoPkgInstallation
	SetDoPatchInstallation
	
	[[ "$ANALYZE" == "true" ]] &&
		PrintLog "$(gettext "Starting System Analysis:\n")" ||
		PrintLog "$(gettext "Starting upgrade:\n")"
	
	if ! SaveOriginalPatchlists; then
		Quit 2
	fi
fi

if [[ "$ANALYZE" == "true" ]]; then
	AnalyzeSystem	|| pRetCode=$?
	if (( pRetCode != 0 )); then
		PrintLog "$(gettext "System Analysis failed.")"
		Quit $pRetCode
	fi
 	Quit 0
fi

# Patch installation is done first to avoid previously freshbitted 
# patch conflicts that occur due to incremental patching. 

if DoPatchInstallation; then
	InstallPatches	|| pRetCode=$?
	if (( pRetCode != 0 )); then
		Quit $pRetCode
	fi
fi

if DoPkgInstallation; then
	InstallUpdatedPackages	|| pRetCode=$?
	if (( pRetCode != 0 )); then
		PrintLog "$(gettext "Updating of packages failed.")"
		Quit $pRetCode
	fi
	InstallNewPackages	|| pRetCode=$?
	if (( pRetCode != 0 )); then
		PrintLog "$(gettext "Installation of packages failed.")"
		Quit $pRetCode
	fi
fi

RemovePackages "$REMOVEONLYPKGS" "pkgrm"  || pRetCode=$?
if (( pRetCode != 0 )); then
	PrintLog "$(gettext "Unable to remove packages")"
	Quit $pRetCode
fi

if (( pRetCode == 0 )); then
	if ! UpdateSystemFiles; then
		PrintLog "$(gettext "Unable to modify system files.")"
		Quit 1
	fi
fi

Quit $pRetCode
