#!/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 [-r] [-a] [-v] -p -n -R 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= 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