#!/bin/ksh # # @(#) install_mu 8.5 ( 08/10/01 ) SMI # # Copyright (c) 1994-2001 Sun Microsystems, Inc. All Rights Reserved. Sun # considers its source code as an unpublished, proprietary trade secret, and # it is available only under strict license provisions. This copyright # notice is placed here only to protect Sun in the event the source is # deemed a published work. Dissassembly, decompilation, or other means of # reducing the object code to human readable form is prohibited by the # license agreement under which this code is provided to the user or company # in possession of this copy. # # RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the Government # is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the # Rights in Technical Data and Computer Software clause at DFARS 52.227-7013 # and in similar clauses in the FAR and NASA FAR Supplement. # install_mu for 2.8. Installs a set of patches quickly. # # Exit Codes: # 0 No error # 1 User interrupt # 2 Cannot find INST_RELEASE file # 3 Required utility not found # 4 -B and -d options can't be used together # 5 -p (patch directory) option is not a directory # 6 -B (backout directory) option is not a directory # 7 Permissions on -B (backoutdir) option too restrictive # 8 -R (root directory) option is not a directory # 9 /export/ is not a valid service area # 10 Unrecognized option # 11 Can't write to log file # 12 Client SUNWcar (core architecture root) package missing # 13 Unsupported architecture (not sparc or i386) # 14 Cannot find .order file in directory specified by -p # 15 Searched for but could not find .order file # 16 Script run by non-root user # 17 OS version of target system is not 2.8 # 18 Tools directory missing # 19 Tools directory does not contain executable # patchadd_mu and/or patchrm_mu # 20 Service area is not Solaris_2.8 # 21 -S and -R options are mutually exclusive # 22 Not enough disk space to apply patch set # 23 Not enough disk space to save backout data # 24 pkgadd dryrun failure # 25 -f and -D options are mutually exclusive # 26 Service area doesn't have a valid /var/sadm/pkg directory # 27 Cannot find state file. # 28 patch 108987-08 (sparc) or 108988-08 (i386) not found # The current build of MU MU_BUILD="08c" # The current version of the state file STATE_VERSION="1.0" # File containing release and MU information RELEASE_FILE="/etc/release" # The template file from which /etc/release is built RELEASE_INFO_TEMPLATE="/var/sadm/install_data/.solaris_relinfo" # One of these lines gets appended to release file after install_mu runs MU_RELEASE_LINE='Solaris 8 Maintenance Update 7 applied' # install_mu Version Number VERSION="8."$MU_BUILD # Supported OS MU_OS="8" # If we don't completely read the list of patches then # we assume the user interrupted the program. UserInterrupted="yes" # Version Name MU_VERSION_NAME="Solaris_8MU7" # # Force the path to first look in certain directories # PATH=/usr/bin:/usr/sbin:/sbin:/bin:$PATH export PATH # array of patchadd error codes set -A PatchAddError PatchAddError[1]="Usage error" PatchAddError[2]="Attempt to apply a patch that's already been applied" PatchAddError[3]="Effective UID is not root" PatchAddError[4]="Attempt to save original files failed" PatchAddError[5]="pkgadd failed" PatchAddError[6]="Patch is obsoleted" PatchAddError[7]="Invalid package directory" PatchAddError[8]="Attempting to patch a package that is not installed" PatchAddError[9]="Cannot access /usr/sbin/pkgadd (client problem)" PatchAddError[10]="Package validation errors" PatchAddError[11]="Error adding patch to root template" PatchAddError[12]="Patch script terminated due to signal" PatchAddError[13]="Symbolic link included in patch" PatchAddError[14]="NOT USED" PatchAddError[15]="The prepatch script had a return code other than 0." PatchAddError[16]="The postpatch script had a return code other than 0." PatchAddError[17]="Mismatch of the -d option between a previous patch install and the current one." PatchAddError[18]="Not enough space in the file systems that are targets of the patch." PatchAddError[19]="$SOFTINFO/INST_RELEASE file not found" PatchAddError[20]="A direct instance patch was required but not found" PatchAddError[21]="The required patches have not been installed on the manager" PatchAddError[22]="A progressive instance patch was required but not found" PatchAddError[23]="A restricted patch is already applied to the package" PatchAddError[24]="An incompatible patch is applied" PatchAddError[25]="A required patch is not applied" PatchAddError[26]="The user specified backout data can't be found" PatchAddError[27]="The relative directory supplied can't be found" PatchAddError[28]="A pkginfo file is corrupt or missing" PatchAddError[29]="Bad patch ID format" PatchAddError[30]="Dryrun failure(s)" PatchAddError[31]="Path given for -C option is invalid" PatchAddError[32]="Must be running Solaris 2.6 or greater" PatchAddError[33]="Bad formatted patch file or patch file not found" PatchAddError[34]="Incorrect patch spool directory" PatchAddError[35]="Later revision already installed" PatchAddError[36]="Cannot create safe temporary directory" PatchAddError[37]="Illegal backout directory specified" ARCH="" LPROC="" NEW_SOFTINFO="/var/sadm/system/admin/INST_RELEASE" MGRSOFTINFO="none" TRGSOFTINFO="none" Mgrprodver="" prodver="" Product="" MgrOSVers="" TrgOSVers="" TMP_LIST="/tmp/plist.$$" # Used in command line processing xsaveold=" " saveold="yes" PKGDBARG=" " BACKOUTARG="" PATCHDIR="" uPATCHDIR="" TOP_DIR="" SKIP_DRYRUN="false" DRYRUN_ONLY="false" # Holds list of patches successfully and not-successfully installed succeeded_list="" failed_list="" ## TOOLSDIR="" ADMINFILE=/tmp/admin.$$ PKGADDLOGFILE=/tmp/pkgaddlog.$$ DRYRUN_TMP_DIR=/tmp/dryrun.$$ DRYRUN_JUNK_DIR=/tmp/dryrunjunk.$$ PKGLISTFILE=/tmp/pkglist.$$ DRYRUN_REPORT=/tmp/dryrun_report.$$ MS_NAME="Maintenance Update" RELEASE= LOGFILE= logname= ROOTDIR="/" SERVICE_AREA="" # Determine the plaform in use and pass results to CDROMLOC MU_PLATFORM=$(uname -p) if [ $MU_PLATFORM = "sparc" ]; then CDROMLOC="/cdrom/s8_maintenance_update_7_sparc/Solaris_8_MU7" else CDROMLOC="/cdrom/s8_maintenance_update_7_x86/Solaris_8_MU7" fi # Needed utilities AWK=/usr/bin/awk BASENAME=/usr/bin/basename CAT=/usr/bin/cat CHGRP=/usr/bin/chgrp CHMOD=/usr/bin/chmod CHOWN=/usr/bin/chown CP=/usr/bin/cp DATECMD=/usr/bin/date DF=/usr/bin/df ECHO=/usr/bin/echo EGREP=/usr/bin/egrep EXPR=/usr/bin/expr FIND=/usr/bin/find GETTEXT=/usr/bin/gettext GREP=/usr/bin/grep HEAD=/usr/bin/head LN=/usr/bin/ln LS=/usr/bin/ls MKDIR=/usr/bin/mkdir MV=/usr/bin/mv NAWK=/usr/bin/nawk PATCHADD=/usr/sbin/patchadd PKGADD=/usr/sbin/pkgadd PR=/usr/bin/pr PWDCMD=/usr/bin/pwd RM=/usr/bin/rm SED=/usr/bin/sed TAIL=/usr/bin/tail VALPATH=/usr/sadm/bin/valpath WC=/usr/bin/wc XARGS=/usr/bin/xargs REQD_CMDS="$AWK $BASENAME $CAT $CHGRP $CHMOD $CHOWN $CP $DATECMD $DF $ECHO $EGREP $EXPR $FIND $GETTEXT $GREP \ $HEAD $LN $LS $MKDIR $MV $NAWK $PATCHADD $PKGADD $PR $PWDCMD $RM $SED $TAIL $VALPATH $WC $XARGS" disable_do_busy="no" backoutdir_specified="no" INSTALL_MU_PID=0 umask 022 CLEAN_UP_FILES="$ADMINFILE $PKGADDLOGFILE $PKGLISTFILE $DRYRUN_REPORT $TMP_LIST" CLEAN_UP_DIRS="$DRYRUN_TMP_DIR $DRYRUN_JUNK_DIR" COMPACT_SLASHES="$SED s,//*,/,g" #################### handle_trap #################### # Description: # Handle keyboard interrupt. # Parameters: # None. # Globals set: # None. # Globals used: # LOGFILE function handle_trap { $GETTEXT "\ninstall_mu: signal detected ($1).\n" | tee -a ${LOGFILE} $GETTEXT "install_mu is terminating.\n" | tee -a ${LOGFILE} do_file_cleanup exit 1 } #################### activate_do_busy #################### # Description: # Start the do_busy dots # Parameters: # None. # Globals set: # None. # Globals used: # disable_do_busy, INSTALL_MU_PID function activate_do_busy { if [ "$disable_do_busy" = "no" ]; then if [ $INSTALL_MU_PID -eq 0 ]; then (while :; do $ECHO ".\c" sleep 5 done)& INSTALL_MU_PID=$! fi fi } #################### stop_do_busy #################### # Description: # Stop the do_busy dots # Parameters: # None. # Globals set: # None. # Globals used: # disable_do_busy, INSTALL_MU_PID function stop_do_busy { if [ "$disable_do_busy" = "no" ]; then if [ $INSTALL_MU_PID -ne 0 ]; then kill $INSTALL_MU_PID >/dev/null 2>&1 kill $INSTALL_MU_PID >/dev/null 2>&1 fi fi INSTALL_MU_PID=0 $ECHO | tee -a ${LOGFILE} } #################### handle_mu_applied #################### # Description # Put in .mu_applied file, or update it. # Parameters # None. # Globals set: # None. # Globals used: # ROOTDIR, CHMOD, CHOWN, CHGRP, CAT, NAWK, MU_VERSION_NAME function handle_mu_applied { MU_APPLIED="$ROOTDIR/var/sadm/patch/.mu_applied" if [ ! -f $MU_APPLIED ]; then $ECHO "VERSION=$MU_VERSION_NAME" >$MU_APPLIED $CHMOD 644 $MU_APPLIED $CHOWN root $MU_APPLIED $CHGRP bin $MU_APPLIED else mu_version_found=no $CAT $MU_APPLIED | $NAWK -F= ' { print $2 } ' | while read mu_version_name do if [ "$mu_version_name" = "$MU_VERSION_NAME" ]; then mu_version_found=yes break; fi done if [ "$mu_version_found" = "no" ]; then $ECHO "VERSION=$MU_VERSION_NAME" >>$MU_APPLIED fi fi } #################### get_OS_version #################### # Description: # Get the product version _ of local Solaris installation # Parameters: # $1 target host softinfo directory path # $2 managing host softinfo directory path # $3 root of the target host # Globals Set: # Product, MgrOSVers, TrgOSVers, prodver, Mgrprodver # Globals used: # SED function get_OS_version { if [ "$2" != "none" ]; then MgrProduct=`$SED -n 's/^OS=\(.*\)/\1/p' $2` MgrOSVers=`$SED -n 's/^VERSION=\(.*\)/\1/p' $2` Mgrprodver=$MgrProduct"_"$MgrOSVers else if [ "${LOGFILE}" != "" ]; then $GETTEXT "install_mu is unable to find the INST_RELEASE file for the" | tee -a ${LOGFILE} $GETTEXT "target filesystem.\nThis file must be present for install_mu" | tee -a ${LOGFILE} $GETTEXT "to function correctly.\n" | tee -a ${LOGFILE} else $GETTEXT "install_mu is unable to find the INST_RELEASE file for the" $GETTEXT "target filesystem.\nThis file must be present for install_mu" $GETTEXT "to function correctly.\n" fi do_file_cleanup exit 2 fi if [ $3 = "/" ]; # If there's not a client then Product=$MgrProduct TrgOSVers=$MgrOSVers prodver=$Mgrprodver # OK, there is a client elif [ "$1" = "none" ]; # but no softinfo file then if [ "${LOGFILE}" != "" ]; then $GETTEXT "install_mu is unable to find the INST_RELEASE file for the" | tee -a ${LOGFILE} $GETTEXT "target filesystem.\nThis file must be present for install_mu" | tee -a ${LOGFILE} $GETTEXT "to function correctly.\n" | tee -a ${LOGFILE} else $GETTEXT "install_mu is unable to find the INST_RELEASE file for the" $GETTEXT "target filesystem.\nThis file must be present for install_mu" $GETTEXT "to function correctly.\n" fi do_file_cleanup exit 2 else Product=`$SED -n 's/^OS=\(.*\)/\1/p' $1` TrgOSVers=`$SED -n 's/^VERSION=\(.*\)/\1/p' $1` prodver=$Product"_"$TrgOSVers fi } #################### display_patches_succeeded_failed #################### # Description: # Display lists of the patches that installed successfully and of those that didn't. # Parameters: # None. # Globals set: # None. # Globals used: # succeeded_list, failed_list, LOGFILE, PR, PatchAddError function display_patches_succeeded_failed { # List the patches that succeeded and failed installation (if any). # set -A PatchAddError PatchAddError[1]="Usage error" PatchAddError[2]="Attempt to apply a patch that's already been applied" PatchAddError[3]="Effective UID is not root" PatchAddError[4]="Attempt to save original files failed" PatchAddError[5]="pkgadd failed" PatchAddError[6]="Patch is obsoleted" PatchAddError[7]="Invalid package directory" PatchAddError[8]="Attempting to patch a package that is not installed" PatchAddError[9]="Cannot access /usr/sbin/pkgadd (client problem)" PatchAddError[10]="Package validation errors" PatchAddError[11]="Error adding patch to root template" PatchAddError[12]="Patch script terminated due to signal" PatchAddError[13]="Symbolic link included in patch" PatchAddError[14]="NOT USED" PatchAddError[15]="The prepatch script had a return code other than 0." PatchAddError[16]="The postpatch script had a return code other than 0." PatchAddError[17]="Mismatch of the -d option between a previous patch install and the current one." PatchAddError[18]="Not enough space in the file systems that are targets of the patch." PatchAddError[19]="$SOFTINFO/INST_RELEASE file not found" PatchAddError[20]="A direct instance patch was required but not found" PatchAddError[21]="The required patches have not been installed on the manager" PatchAddError[22]="A progressive instance patch was required but not found" PatchAddError[23]="A restricted patch is already applied to the package" PatchAddError[24]="An incompatible patch is applied" PatchAddError[25]="A required patch is not applied" PatchAddError[26]="The user specified backout data can't be found" PatchAddError[27]="The relative directory supplied can't be found" PatchAddError[28]="A pkginfo file is corrupt or missing" PatchAddError[29]="Bad patch ID format" PatchAddError[30]="Dryrun failure(s)" PatchAddError[31]="Path given for -C option is invalid" PatchAddError[32]="Must be running Solaris 2.6 or greater" PatchAddError[33]="Bad formatted patch file or patch file not found" PatchAddError[34]="The appropriate kernel jumbo patch needs to be installed" PatchAddError[35]="Later revision already installed" PatchAddError[36]="Cannot create safe temporary directory" PatchAddError[37]="Illegal backout directory specified" $GETTEXT "The following patches installed successfully:\n" | tee -a ${LOGFILE} if [ " ${succeeded_list}" != " " ]; then $ECHO ${succeeded_list} | tr ' ' '\012' | $PR -5 -t -o 1 | tee -a ${LOGFILE} else $GETTEXT " None.\n" | tee -a ${LOGFILE} fi $GETTEXT "\n" | tee -a ${LOGFILE} $GETTEXT "The following patches were not installed:\n" | tee -a ${LOGFILE} if [ " ${failed_list}" != " " ]; then $ECHO ${failed_list} | tr ':' '\012' | while read patch_id failure_code; do $ECHO " $patch_id ${PatchAddError[$failure_code]}" | tee -a ${LOGFILE} done else $GETTEXT " None.\n" | tee -a ${LOGFILE} fi $GETTEXT "\n" | tee -a ${LOGFILE} } #################### find_softinfos #################### # Description: # Find the appropriate softinfo files for the manager and the target. # Parameters: # $1 ROOT of target filesystem # Globals set: # TRGSOFTINFO # MGRSOFTINFO # Globals used: # NEW_SOFTINFO function find_softinfos { if [ -f $NEW_SOFTINFO ]; then MGRSOFTINFO=$NEW_SOFTINFO fi if [ "$1" = "/" ]; then TRGSOFTINFO=$MGRSOFTINFO elif [ "$1" = "" ]; then TRGSOFTINFO=$MGRSOFTINFO elif [ -f $1$NEW_SOFTINFO ]; then TRGSOFTINFO=$1$NEW_SOFTINFO fi } #################### do_file_cleanup #################### # Description: # Stop do_busy dots, remove temp files and dirs, show patches installed # and not installed, rename and relink log file. # Parameters: # None. # Globals set: # None. # Globals used: # disable_do_busy, CLEAN_UP_FILES, CLEAN_UP_DIRS, RM function do_file_cleanup { stop_do_busy if [ "$CLEAN_UP_DIRS" != "" ]; then $ECHO $CLEAN_UP_DIRS | $XARGS $RM -rf $ECHO "" >/dev/null fi if [ "$CLEAN_UP_FILES" != "" ]; then $ECHO $CLEAN_UP_FILES | $XARGS $RM -f $ECHO "" >/dev/null fi if [ "$DRYRUN_ONLY" = "false" ]; then display_patches_succeeded_failed fi move_log_files if [ "$DRYRUN_ONLY" = "false" ]; then update_release_file fi } #################### move_log_files #################### # Description: # Rename LOGFILE to have a date and time stamp on it. # Link LOGFILE to this newly renamed file. # Parameters: # None. # Globals Used: # LOGFILE, MU_VERSION_NAME, DATECMD, LN, MV, logname, STATE_FILE # Globals Set: # function move_log_files { $GETTEXT "install_mu completed at `$DATECMD` \n" | tee -a ${LOGFILE} if [ "${LOGFILE}" != "" ]; then # ISDATETIME=`$DATECMD +%m%d%y08/10/01M` # Make Year 2000 friendly and sortable ISDATETIME=`$DATECMD +%Y.%m.%d.%H.%M.%S | $AWK '{FS="."}{print $1$2$3$4$5$6}'` $MV ${LOGFILE} ${LOGFILE}.$MU_VERSION_NAME.$ISDATETIME $LN -s ./$logname.$MU_VERSION_NAME.$ISDATETIME ${LOGFILE} fi # If at least one patch applied remind the user to reboot his system. # Then save a list of patches applied plus MU information from /etc/release # into a state file. if [ " ${succeeded_list}" != " " ]; then if [ "$service_specified" = "yes" ]; then root_or_usr="usr" else root_or_usr="root" fi # Look for existing state file for this MU STATE_FILE="" if [ -r $ROOTDIR/var/sadm/install_data/.mu_state.${root_or_usr}.* ]; then STATE_FILE=$($GREP -l $MU_VERSION_NAME $ROOTDIR/var/sadm/install_data/.mu_state.${root_or_usr}.* | \ $TAIL -1 | $AWK '{print $1}') fi if [ "$STATE_FILE" != "" ]; then # Append new patches that applied to existing state file $ECHO ${succeeded_list} | tr -s " " "\n" > $TMP_LIST while read p; do $GREP "$p" $STATE_FILE > /dev/null if [ $? -ne 0 ]; then # This patch not in state file. Add it. echo "$p" >> $STATE_FILE fi done < $TMP_LIST $GETTEXT "\nUpdated state file `$ECHO $STATE_FILE | $COMPACT_SLASHES`.\n" | tee -a ${LOGFILE} else # Create and populate a new state file STATE_FILE="$ROOTDIR/var/sadm/install_data/.mu_state.${root_or_usr}.$ISDATETIME" # Dropped the MU_INFO in the if condition as there is no /export/Solaris_8/etc/release file. # Bug 4458697 if [ "$service_specified" != "yes" ] then MU_INFO=$($GREP "Maintenance" $ROOTDIR/$RELEASE_FILE | $TAIL -1) fi $ECHO "# $MU_INFO" >> $STATE_FILE $ECHO ${succeeded_list} | tr -s " " "\n" >> $STATE_FILE $GETTEXT "\nCreated new state file: `$ECHO $STATE_FILE | $COMPACT_SLASHES`\n" fi $GETTEXT "\n-=- Please REBOOT your system -=-\n\n" fi } #################### eval_utilities #################### # Description: # Make sure all required commands are available. # # Globals Used: # REQD_CMDS, LOGFILE # # Globals Set: # None function eval_utilities { for xcommand in $REQD_CMDS; do if [ ! -x $xcommand ]; then $GETTEXT "ERROR: Cannot find $xcommand which is required for proper" \ | tee -a ${LOGFILE} $GETTEXT "execution of install_mu, or $xcommand is not executable.\n" \ | tee -a ${LOGFILE} do_file_cleanup exit 3 fi done } #################### print_usage #################### # Description: # Print usage message. # Parameters: # None. # Globals set: # None. # Globals used: # None. function print_usage { # The -D option is still available for system test engineers, though the # space checking feature is done via ksh rather than using the data from # pkgadd. The -D and -f options are mutually exclusive. $GETTEXT "\n\ usage: `basename $0` [-p patchdir] [-d | -B backoutdir]\n\ [-f] [-q]\n\n\ [-p patchdir] - specifies directory that contains the patch set\n\ [-d] - don't backup patches (Note: cannot use -B also)\n\ [-B backoutdir] - specifies directory patches will be saved in (Note: cannot\n\ use -d also)\n\ [-f] - force installation of patch set without checking for\n\ sufficient disk space (Note: cannot use -D also)\n\ use -R also)\n\ [-q] - quiet mode, no do_busy dots\n\n" # These options were removed in S8 MU6 Build 06 after the decision to not support # diskless clients in the MUA # usage: `basename $0` [-p patchdir] [-d | -B backoutdir]\n\ # [-f] [-R rootdir | -S servicedir] [-q]\n\n\ # [-p patchdir] - specifies directory that contains the patch set\n\ # [-d] - don't backup patches (Note: cannot use -B also)\n\ # [-B backoutdir] - specifies directory patches will be saved in (Note: cannot\n\ # use -d also)\n\ # [-f] - force installation of patch set without checking for\n\ # sufficient disk space (Note: cannot use -D also)\n\ # use -R also)\n\ # [-R rootdir] - install root directory, prepended to all absolute\n\ # path names of the install root (Note: cannot use -S also)\n\ # [-S servicedir] - specifies alternate service directory (Note: cannot\n\ # use -R also)\n\ # [-q] - quiet mode, no do_busy dots\n\n" } #################### parse_args #################### # Description: # Parse command line arguments. # Parameters: # $* : all options and arguments on command line # Globals set: # xsaveold, saveold, uPATCHDIR, TOP_DIR, disable_do_busy, backoutdir_specified, # BACKOUTARG, ROOTDIR, DRYRUN_ONLY, SKIP_DRYRUN # Globals used: # PWDCMD, MKDIR, LOGFILE, PKGDBARG function parse_args { command="$0 $*" root_specified="no" service_specified="no" backoutdir_specified="no" PATCH_NO_UNDO="false" while [ "$1" != "" ] do case $1 in -d) PATCH_NO_UNDO="true" if [ "$backoutdir_specified" = "yes" ]; then $GETTEXT "The -B and -d arguments are mutually exclusive.\n" print_usage exit 4 fi xsaveold="-d" saveold="no" shift;; -p) shift uPATCHDIR=$1 # the user-supplied patch dir path if [ ! -d $uPATCHDIR ]; then $GETTEXT "The -p parameter must be a directory.\n" $GETTEXT "$uPATCHDIR is not a directory.\n" print_usage exit 5 fi shift;; -q) disable_do_busy="yes"; shift;; -f) SKIP_DRYRUN="true" if [ "$DRYRUN_ONLY" = "true" ]; then print_usage exit 25 fi shift;; # The -D option is still available for system test engineers, though the # space checking feature is done via ksh rather than using the data from # pkgadd. The -D and -f options are mutually exclusive. -D) DRYRUN_ONLY="true" if [ "$SKIP_DRYRUN" = "true" ]; then print_usage exit 25 fi shift;; -B) shift backoutdir_specified="yes" if [ "$PATCH_NO_UNDO" = "true" ]; then $GETTEXT "The -B and -d arguments are mutually exclusive.\n" print_usage exit 4 fi if [ ! -d $1 ]; then $GETTEXT "The -B parameter must be a directory.\n" $GETTEXT "$1 is not a directory.\n" print_usage exit 6 fi bo_cdir=`$PWDCMD` cd $1 BACKOUTDIR=`$PWDCMD` $MKDIR -p $BACKOUTDIR/test/file if [ $? != 0 ]; then $GETTEXT "Permissions on backout directory $BACKOUTDIR not adequate.\n" exit 7 fi $RM -rf $BACKOUTDIR/test cd $bo_cdir shift;; # # -R) shift # if [ "$service_specified" = "yes" ]; then # $GETTEXT "The -S and -R arguments are mutually exclusive.\n" # print_usage # exit 21 # fi # ROOTDIR=$1 # if [ ! -d $ROOTDIR ]; then # $GETTEXT "The -R parameter must be a directory.\n" # $GETTEXT "$ROOTDIR is not a directory.\n" # print_usage # exit 8 # fi # root_specified="yes" # shift;; # # -S) shift # if [ "$root_specified" = "yes" ]; then # $GETTEXT "The -S and -R arguments are mutually exclusive.\n" # print_usage # exit 21 # fi # if [ "$1" != "Solaris_${MU_OS}" ]; then # $GETTEXT "The service area must be Solaris_${MU_OS}.\n" # print_usage # exit 20 # fi # if [ ! -d /export/$1 ]; then # $GETTEXT "The -S parameter must be a directory.\n" # $GETTEXT "/export/$1 is not a directory.\n" # print_usage # exit 9 # fi # find_softinfos /export/$1 # get_OS_version "$TRGSOFTINFO" "$MGRSOFTINFO" "$1" # Need to check for diskless client install here old diskless clients will # share a servers /usr the new clients (SUNWdclnt) can not and the service # area is always based out of /export. # # pkginfo -q SUNWdclnt # dclnt_rc=$? # Check for Server and Client OS's not being the same and this being a Version 1 # style diskless client server. If so prepend /export to the ROOTDIR, if the OS's # are the same then ROOTDIR is set to / further up. # if [[ "$1" != "$Mgrprodver" && $dclnt_rc != 0 ]]; then # if [ -d /export/$1/var/sadm/pkg ]; then # ROOTDIR=/export/$1 # SERVICE_AREA=$1 # else # $GETTEXT "The $1 service cannot be found on this system.\n" # print_usage # exit 26 # fi # Check for a version 2 style diskless client if it is we always want to keep # the /export prepended to ROOTDIR # elif [ $dclnt_rc == 0 ]; then # SERVICE_AREA=$1 # ROOTDIR=/export/$1 # else # $GETTEXT "The $1 service cannot be found on this system.\n" # print_usage # exit 26 # # fi # service_specified="yes" # shift;; -*) $GETTEXT "Invalid option.\n"; print_usage; exit 10;; *) $GETTEXT "Invalid option.\n"; print_usage; exit 10;; esac done logname=`$ECHO ${MS_NAME} | $SED 's/ /_/g'`_log LOGFILE=`$ECHO $ROOTDIR/var/sadm/install_data/${logname} | $COMPACT_SLASHES` if [ -h ${LOGFILE} ] ; then $RM ${LOGFILE} fi $CAT /dev/null >${LOGFILE} if [ $? != 0 ]; then $ECHO "Can't write to Log File: ${LOGFILE}\n" print_usage exit 11 fi $RM ${LOGFILE} # if there are more parameters, usage is wrong. if [ $# -gt 0 ]; then print_usage exit 10 fi if [ "$ROOTDIR" != "" ]; then PKGDBARG="-R $ROOTDIR " fi if [ "$SERVICE_AREA" != "" ]; then PKGDBARG="-S $SERVICE_AREA " fi if [ "$backoutdir_specified" = "yes" ]; then BACKOUTARG="-B $BACKOUTDIR" else BACKOUTDIR=$(print $ROOTDIR/var/sadm/pkg | $COMPACT_SLASHES) fi } #################### set_patchdir #################### # Description: # Figure out where the .order file is in the patch set. # Parameters: # None. # Globals set: # PATCHDIR, ARCH, LPROC, # Globals used: # TOP_DIR, ROOTDIR, LOGFILE, PWDCMD, uPATCHDIR, function set_patchdir { # Set PATCHDIR to location of .order file in patch directory. # Here's the variable setting and search algorithm: # # If TOP_DIR is set then MU_TOP=TOP_DIR # else MU_TOP="MU" # # LPROC is set to sparc or x86 depending on ARCH field of $ROOTDIR/var/sadm/pkg/SUNWcsr/pkginfo # # If uPATCHDIR is set and if uPATCHDIR/.order exists then PATCHDIR=uPATCHDIR # # If uPATCHDIR is set and if # uPATCHDIR/MU_TOP/LPROC/Patches/.order exists then PATCHDIR=./uPATCHDIR/MU_TOP/LPROC/Patches # # If uPATCHDIR is set but we couldn't find a PATCHDIR # in the last two places we looked then error out. # # If ./LPROC/Patches/.order exists then PATCHDIR=./LPROC/Patches # # If ./MU_TOP/LPROC/Patches/.order exists then PATCHDIR=./MU_TOP/LPROC/Patches # # If /cdrom/cdrom0/LPROC/Patches/.order exists then PATCHDIR=/cdrom/cdrom0/MU_TOP/LPROC/Patches # # If PATCHDIR hasn't been set by now, error out. # if [ "$TOP_DIR" = "" ]; then MU_TOP="MU" else MU_TOP=$TOP_DIR fi if [ $root_specified = "yes" ] then pkginfo -R $ROOTDIR -q SUNWcar if [ $? != 0 ]; then $GETTEXT "SUNWcar (core architecture root) package" | tee -a ${LOGFILE} $GETTEXT "does not exist in $ROOTDIR/var/sadm/pkg.\n" | tee -a ${LOGFILE} do_file_cleanup exit 12 fi ARCH=`$GREP "^ARCH=" $ROOTDIR/var/sadm/pkg/SUNWcar/pkginfo | $SED 's/ARCH=//'` LPROC=`$EXPR $ARCH : '\(.*\)\..*'` if [[ "$LPROC" != "sparc" && "$LPROC" != "i386" ]]; then $GETTEXT "install_mu only supports sparc and i386 architectures.\n" | tee -a ${LOGFILE} $GETTEXT "install_mu has detected ARCH=$LPROC.\n" | tee -a ${LOGFILE} do_file_cleanup exit 13 fi else pkginfo -R $ROOTDIR -q SUNWcsu if [ $? != 0 ]; then $GETTEXT "SUNWcar (core architecture usr) package" | tee -a ${LOGFILE} $GETTEXT "does not exist in $ROOTDIR/var/sadm/pkg.\n" | tee -a ${LOGFILE} do_file_cleanup exit 12 fi ARCH=`$GREP "^ARCH=" $ROOTDIR/var/sadm/pkg/SUNWcsu/pkginfo | $SED 's/ARCH=//'` LPROC=$ARCH if [[ "$LPROC" != "sparc" && "$LPROC" != "i386" ]]; then $GETTEXT "install_mu only supports sparc and i386 architectures.\n" | tee -a ${LOGFILE} $GETTEXT "install_mu has detected ARCH=$LPROC.\n" | tee -a ${LOGFILE} do_file_cleanup exit 13 fi fi # save the value of the current working directory xdir=`$PWDCMD` if [[ "$uPATCHDIR" != "" && -f $uPATCHDIR/.order ]]; then # uPATCHDIR is patch directory cd $uPATCHDIR PATCHDIR=`$PWDCMD` cd $xdir return fi if [[ "$uPATCHDIR" != "" && -f $uPATCHDIR/$MU_TOP/$LPROC/Patches/.order ]]; then # uPATCHDIR/MU_TOP/LPROC/Patches is the patch directory cd $uPATCHDIR/$MU_TOP/$LPROC/Patches PATCHDIR=`$PWDCMD` cd $xdir return fi # If we've gotten to here the -p parameter was set but we couldn't find a .order file # in uPATCHDIR or in uPATCHDIR/MU_TOP/LPROC/Patches if [ "$uPATCHDIR" != "" ]; then $GETTEXT "-p parameter does not point to a directory containing a .order file.\n" | tee -a ${LOGFILE} $GETTEXT "Looked in $uPATCHDIR and in $uPATCHDIR/$MU_TOP/$LPROC/Patches.\n" | tee -a ${LOGFILE} print_usage exit 14 fi if [ -f ./$LPROC/Patches/.order ]; then # LPROC/Patches is the patch directory cd $LPROC/Patches PATCHDIR=`$PWDCMD` cd $xdir return fi if [ -f $MU_TOP/$LPROC/Patches/.order ]; then # MU_TOP/LPROC/Patches is the patch directory cd $MU_TOP/$LPROC/Patches PATCHDIR=`$PWDCMD` cd $xdir return fi if [ -f $CDROMLOC/$LPROC/Patches/.order ]; then # /cdrom/cdrom0/LPROC/Patches is the patch directory cd $CDROMLOC/$LPROC/Patches PATCHDIR=`$PWDCMD` cd $xdir return fi # Cannot find patch directory. Give up! cd $xdir $GETTEXT "install_mu cannot locate patch order (.order) file.\n" | tee -a ${LOGFILE} $GETTEXT "Paths searched:\n" | tee -a ${LOGFILE} $GETTEXT " ./$LPROC/Patches, $MU_TOP/$LPROC/Patches, $CDROMLOC/$LPROC/Patches\n" | tee -a ${LOGFILE} if [ "$uPATCHDIR" != "" ]; then $GETTEXT " ./$uPATCHDIR, and ./$uPATCHDIR/$MU_TOP/$LPROC/Patches.\n" | tee -a ${LOGFILE} fi print_usage exit 15 } #################### validate_uid #################### # Description: # Make sure effective UID is '0' # Parameters: # none # Globals set: # none # Globals used: # none function validate_uid { typeset -i uid uid=$(id | $SED 's/uid=\([0-9]*\)(.*/\1/') if (( uid != 0 )) then $GETTEXT "You must be root to execute this script.\n" exit 16 fi } #################### build_admin_file #################### # Description: # Build the admin file for later use by non-interactive pkgadd # Parameters: # none # Globals Used: # ADMINFILE, CAT function build_admin_file { $CAT > $ADMINFILE << EOF mail= instance=overwrite partial=nocheck runlevel=nocheck idepend=nocheck rdepend=nocheck space=quit setuid=nocheck conflict=nocheck action=nocheck EOF } #################### eval_dryrun_failures #################### # Descriprtion: # Report errors from pkgadd dryrun mode # Parameters: # Dryrun directory name # Globals used: # # Globals set: # function eval_dryrun_failures { $GETTEXT "The following errors were reported by pkgadd dryrun...\n\n" exitCheck=$($NAWK -F= ' $2 ~ /!0/ { print $1 } ' $1/dryrun.isum.asc ) for code in $exitCheck do case $code in CHECKINSTALLEXITCODE) $GETTEXT " The checkinstall script failed.\n" ;; REQUESTEXITCODE) $GETTEXT " The request script failed.\n" ;; esac done dryrunFailures=$($NAWK -F= ' $2 ~ /NOT_OK/ { print $1 } ' $1/dryrun.isum.asc ) failed=" Installation failed due to" for param in $dryrunFailures do case $param in SPACE) $GETTEXT "$failed lack of space reported by pkgadd dryrun\n" ;; PARTIAL) $GETTEXT "$failed partial install reported by pkgadd dryrun\n" ;; RUNLEVEL) $GETTEXT "$failed incorrect run level\n" ;; PKGFILES) $GETTEXT "$failed bad pkg reported by pkgadd dryrun\n" ;; DEPEND) $GETTEXT "$failed incorrect depend file\n" ;; CONFLICT) $GETTEXT "$failed conflicts reported by pkgadd dryrun\n" ;; SETUID) $GETTEXT "$failed incorrect uid\n" ;; PKGDIRS) $GETTEXT "$failed package directories not found\n" ;; esac done } #################### parse_sizes #################### # Description: # Used in the file system space calculation. Determine where each # identified file will be placed, and add its size to the correct # running total. # Parameters: # $pkgdir_and_name - patch id and package name # Globals Used: # Openwin_Kbytes_Needed # Usr_Kbytes_Needed # Opt_Kbytes_Needed # Var_Kbytes_Needed # Root_Kbytes_Needed # function parse_sizes { typeset -i size=0 typeset -i installedSize=0 $CAT $pkgdir_and_name/pkgmap | while read part ftype f3 f4 f5 f6 f7 f8 Junk do case $ftype in f|e|v) $($VALPATH -a $f4) ret=$? if [[ "$ret" != 0 ]] then # Prepend the basedir to # the relocatable objects if [[ "$Pkgpatchbase" = "/" ]] then pathname="$Pkgpatchbase$f4" else pathname="$Pkgpatchbase/$f4" fi else pathname=$f4 fi # Calculate the difference from the # installing object with the object # that is already installed. if [[ "$ROOTDIR" != "/" ]] then # Prepend the root path to the installed object tmpPath="$ROOTDIR$pathname" else tmpPath="$pathname" fi if [[ -f "$tmpPath" ]] then installedSize=$($WC -c $tmpPath | $NAWK '{ print $1 }') # Keep a running total of the installed size of the # data to do the backout size computation. Also track # the installed size by pkg to do the computation for # fudge factor needed during the compression of the pkg. # In both calculations convert bytes to kbytes. backout_kbytes_needed=backout_kbytes_needed+installedSize/1024 pkg_size=pkg_size+installedSize/1024 size=$f8 size=size-installedSize if (( size < 0 )) then size=0 fi else size=$f8 fi case $pathname in usr\/openwin\/*|\/usr\/openwin\/*|openwin\/*|\/openwin\/*) Openwin_Kbytes_Needed=Openwin_Kbytes_Needed+size ;; usr\/*|\/usr\/*) Usr_Kbytes_Needed=Usr_Kbytes_Needed+size ;; var\/*|\/var\/*) Var_Kbytes_Needed=Var_Kbytes_Needed+size ;; opt\/*|\/opt\/*) Opt_Kbytes_Needed=Opt_Kbytes_Needed+size ;; *) Root_Kbytes_Needed=Root_Kbytes_Needed+size ;; esac ;; i) size=$f4 Var_Kbytes_Needed=Var_Kbytes_Needed+size ;; d|l|s|p|b|c|x) $($VALPATH -a $f4) ret=$? if [[ "$ret" != 0 ]] then # Prepend the basedir to # the relocatable objects if [[ "$Pkgpatchbase" = "/" ]] then pathname="$Pkgpatchbase$f4" else pathname="$Pkgpatchbase/$f4" fi else pathname=$f4 fi case $pathname in usr\/openwin\/*|\/usr\/openwin\/*|openwin\/*|\/openwin\/*) Openwin_Kbytes_Needed=Openwin_Kbytes_Needed+512 ;; usr\/*|\/usr\/*) Usr_Kbytes_Needed=Usr_Kbytes_Needed+512 ;; var\/*|\/var\/*) Var_Kbytes_Needed=Var_Kbytes_Needed+512 ;; opt\/*|\/opt\/*) Opt_Kbytes_Needed=Opt_Kbytes_Needed+512 ;; *) Root_Kbytes_Needed=Root_Kbytes_Needed+512 ;; esac ;; *) ;; esac done } #################### compute_fs_space_requirements #################### # Description: # Compute the file system space requirements for /, /var, /opt, # /usr, and /usr/openwin to determine if there is enough free space # in which to place the patch. # # Parameters: # # Globals Used: # # Globals Set: # function compute_fs_space_requirements { typeset -i size=0 if [[ "$ROOTDIR" != "/" ]] then VarFS=$($DF -a $ROOTDIR/var 2>/dev/null | $GREP var) OptFS=$($DF -a $ROOTDIR/opt 2>/dev/null | $GREP opt) UsrFS=$($DF -a $ROOTDIR/usr 2>/dev/null | $GREP usr) OpenwinFS=$($DF -a $ROOTDIR/usr/openwin 2>/dev/null | $GREP openwin) else VarFS=$($DF -a /var 2>/dev/null | $GREP var) OptFS=$($DF -a /opt 2>/dev/null | $GREP opt) UsrFS=$($DF -a /usr 2>/dev/null | $GREP usr) OpenwinFS=$($DF -a /usr/openwin 2>/dev/null | $GREP openwin) fi # All sizes gathered to this point are in bytes. Need to divide by a K. if [[ -z "$OpenwinFS" ]] then Usr_Kbytes_Needed=Usr_Kbytes_Needed+Openwin_Kbytes_Needed Openwin_Kbytes_Needed=0 else if (( Openwin_Kbytes_Needed > 0 )) then Openwin_Kbytes_Needed=Openwin_Kbytes_Needed/1024 Total_Kbytes_Needed=Total_Kbytes_Needed+Openwin_Kbytes_Needed fi fi if [[ -z "$UsrFS" ]] then Root_Kbytes_Needed=Root_Kbytes_Needed+Usr_Kbytes_Needed Usr_Kbytes_Needed=0 else if (( Usr_Kbytes_Needed > 0 )) then Usr_Kbytes_Needed=Usr_Kbytes_Needed/1024 Total_Kbytes_Needed=Total_Kbytes_Needed+Usr_Kbytes_Needed fi fi if [[ -z "$OptFS" ]] then Root_Kbytes_Needed=Root_Kbytes_Needed+Opt_Kbytes_Needed Opt_Kbytes_Needed=0 else if (( Opt_Kbytes_Needed > 0 )) then Opt_Kbytes_Needed=Opt_Kbytes_Needed/1024 Total_Kbytes_Needed=Total_Kbytes_Needed+Opt_Kbytes_Needed fi fi if [[ -z "$VarFS" ]] then Root_Kbytes_Needed=Root_Kbytes_Needed+Var_Kbytes_Needed Var_Kbytes_Needed=0 else if (( Var_Kbytes_Needed > 0 )) then Var_Kbytes_Needed=Var_Kbytes_Needed/1024 Total_Kbytes_Needed=Total_Kbytes_Needed+Var_Kbytes_Needed fi fi Root_Kbytes_Needed=Root_Kbytes_Needed/1024 Total_Kbytes_Needed=Total_Kbytes_Needed+Root_Kbytes_Needed } #################### check_fs_space #################### # Description: # Check space needed against space available # # Parameters: # None # # Globals Used: # ROOTDIR # VarFS # OptFS # UsrFS # OpenwinFS # Root_Kbytes_Needed # Var_Kbytes_Needed # Opt_Kbytes_Needed # Usr_Kbytes_Needed # Openwin_Kbytes_Needed # # Globals Set: # None # function check_fs_space { typeset -i Opt_Available=0 typeset -i Openwin_Available=0 typeset -i Root_Available=0 typeset -i Usr_Available=0 typeset -i Var_Available=0 # Bear in mind that df -b gives the total kbytes available to # the super-user. That means we have to be conservative since # there's no pad. Tmp_Available=$($DF -b $ROOTDIR | $SED -e '1d') Root_Available=${Tmp_Available:#* } # The root file system must have at least 1Mb of free # space or there will be problems after rebooting Root_Available=Root_Available-1000 enoughFsSpace="YES" dryrun_space_fail="no" enough_to_install="yes" printf "\nSpace Available/Needed to apply patch set, including swap space\n\n" >> $DRYRUN_REPORT printf "%-44s%10s%10s%8s\n" " Filesystem" "Kb Avail" "Kb Needed" "Enough?" >> $DRYRUN_REPORT if (( Root_Kbytes_Needed > Root_Available )) then enoughFsSpace="NO" enough_to_install="no" dryrun_space_fail="yes" fi printf "%-44s%10d%10d%8s\n" " $ROOTDIR" $Root_Available $Root_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT if [[ -n "$UsrFS" ]] then Tmp_Available=$($DF -b $ROOTDIR/usr | $SED -e '1d') Usr_Available=${Tmp_Available:#* } ReportFS=$(print $UsrFS | nawk '{print $1}') enoughFsSpace="YES" if (( Usr_Kbytes_Needed > Usr_Available )) then enoughFsSpace="NO" enough_to_install="no" dryrun_space_fail="yes" fi printf "%-44s%10d%10d%8s\n" " $ReportFS" $Usr_Available $Usr_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT fi if [[ -n "$OptFS" ]] then Tmp_Available=$($DF -b $ROOTDIR/opt | $SED -e '1d') Opt_Available=${Tmp_Available:#* } ReportFS=$(print $OptFS | nawk '{print $1}') enoughFsSpace="YES" if (( Opt_Kbytes_Needed > Opt_Available )) then enoughFsSpace="NO" enough_to_install="no" dryrun_space_fail="yes" fi printf "%-44s%10d%10d%8s\n" " $ReportFS" $Opt_Available $Opt_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT fi if [[ -n "$VarFS" ]] then Tmp_Available=$($DF -b $ROOTDIR/var | $SED -e '1d') Var_Available=${Tmp_Available:#* } ReportFS=$(print $VarFS | nawk '{print $1}') enoughFsSpace="YES" if (( Var_Kbytes_Needed > Var_Available )) then enoughFsSpace="NO" enough_to_install="no" dryrun_space_fail="yes" fi printf "%-44s%10d%10d%8s\n" " $ReportFS" $Var_Available $Var_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT fi if [[ -n "$OpenwinFS" ]] then Tmp_Available=$($DF -b $ROOTDIR/usr/openwin | $SED -e '1d') Openwin_Available=${Tmp_Available:#* } ReportFS=$(print $OpenwinFS | nawk '{print $1}') enoughFsSpace="YES" if (( Openwin_Kbytes_Needed > Openwin_Available )) then enoughFsSpace="NO" enough_to_install="no" dryrun_space_fail="yes" fi printf "%-44s%10d%10d%8s\n" " $ReportFS" $Openwin_Available $Openwin_Kbytes_Needed $enoughFsSpace >> $DRYRUN_REPORT fi printf "------------------------------------------------------------------------\n" >> $DRYRUN_REPORT printf "%-10s%54s\n\n" " Total:" $Total_Kbytes_Needed >> $DRYRUN_REPORT } #################### check_disk_space #################### # Description: # Check for sufficient disk space to apply patch set, possibly with # backout data. # Parameters: # # Globals used: # # Globals set: # function check_disk_space { typeset -i link_count=1 typeset -i kbytes_avail typeset -i backout_kbytes_needed typeset -i backout_kbytes_avail typeset -i pkg_size_max=0 typeset -i pkg_size=0 typeset -i tmp_space_avail=0 typeset -i rec_tmp_space=0 typeset -i Openwin_Kbytes_Needed=0 typeset -i Usr_Kbytes_Needed=0 typeset -i Opt_Kbytes_Needed=0 typeset -i Var_Kbytes_Needed=0 typeset -i Root_Kbytes_Needed=0 typeset -i Total_Kbytes_Needed=0 # Here's how we compute/guesstimate install and backout space needed: # # Install # Compute pkgadd dryrun for all patch packages that will install. # Use the blocks used ("bused") value to determine how much # extra space will be needed to apply the patch packages. Convert # blocks to KB. # # this value, for each filesystem needing more than 0 blocks, # add a fudge factor (three times the largest number of blocks # needed by any one package as reported in the pkgmap file) and we're done. # # Backout # This is a guess as to how much space is needed in the backout # directory: # # Compute the sum of the space used by all new packages, as # reported in the pkgmap files. Divide this number by two # to convert blocks to KB. To this number, add a fudge # factor, twice the number of blocks needed by any one package as # reported in the pkgmap file, and we're done! # # If we're only doing a dryrun to check disk space inform the user that it is # unnecesary and that he/she can/should abort if [ "$DRYRUN_ONLY" = "true" ]; then $GETTEXT "\nThe dryrun -D argument is no longer necessary to check disk space. The\n" $GETTEXT "disk space check is always done independently of the dryrun process unless\n" $GETTEXT "it is specifically disabled with the -f argument.\n" $GETTEXT "\nIn it's current form, dryrun is only useful to Sun internal test engineers\n" $GETTEXT "performing MU content validation. Since the time required to fully execute\n" $GETTEXT "dryrun could exceed 12 hours on some systems, it is not recommended for general\n" $GETTEXT "use.\n" $GETTEXT "\nYou can abort this program (via Control-C) then rerun install_mu without\n" $GETTEXT "the -D argument. Patchadd will still perform a dryrun during the individual\n" $GETTEXT "patch installation, and will report any problems that may be found.\n" fi $GETTEXT "\nAnalyzing space requirements for patch set..." | tee -a ${LOGFILE} # pkgadd dryrun will have problems with low /tmp disk space tmp_space_avail=$($DF -k /tmp | tail -1 | $AWK '{print $4}') rec_tmp_space=$($LS -l $ROOTDIR/var/sadm/install/contents | $AWK '{print $5}')*3/1024 if (( tmp_space_avail < rec_tmp_space )); then $GETTEXT "\n\nWarning: You have only ${tmp_space_avail}KB available in /tmp.\n" | tee -a ${LOGFILE} $GETTEXT "${rec_tmp_space}KB or more is recommended.\n" | tee -a ${LOGFILE} $GETTEXT "\npkgadd dryrun may report errors because of this.\n\n" | tee -a ${LOGFILE} fi activate_do_busy # Create directory to contain links to all packages in all patches if [ -d $DRYRUN_TMP_DIR ]; then $RM -rf $DRYRUN_TMP_DIR fi $MKDIR $DRYRUN_TMP_DIR $CP /dev/null $PKGLISTFILE my_dir=$(pwd) cd $PATCHDIR # Make sure the directory where space check will leave temp stuff # exists and is empty. if [ ! -d $DRYRUN_JUNK_DIR ]; then $MKDIR $DRYRUN_JUNK_DIR fi $RM -f $DRYRUN_JUNK_DIR/* # Create list of patch pkginfo files # line format of $PKGLISTFILE = // $CAT .order | $GREP -v "^#" | while read a_patch; do $LS $a_patch/*/pkginfo | $SED 's/pkginfo//g' >> $PKGLISTFILE done # Build a set of links to package directories for packages that will actually install # on the target system while read pkgdir_and_name; do # e.g. 105181-03/SUNWcar.c/ pkgname=${pkgdir_and_name#*/} # e.g. SUNWcar.c/ pkgname=${pkgname%/} # e.g. SUNWcar.c pkgabbrev=$(pkgparam -f $pkgdir_and_name/pkginfo PKG) # e.g. SUNWcar pkgdir=${pkgdir_and_name%/*/} # e.g. 105181-03 if [[ -d $ROOTDIR/var/sadm/pkg/$pkgabbrev || -d $ROOTDIR/var/sadm/pkg/$pkgabbrev.* ]]; then Pkgarch=$(pkgparam -f $pkgdir_and_name/pkginfo ARCH) Pkgpatchver=$(pkgparam -f $pkgdir_and_name/pkginfo VERSION) Pkgpatchbase=$(pkgparam -f $pkgdir_and_name/pkginfo BASEDIR) pkginst=$(pkginfo -R $ROOTDIR -a $Pkgarch -v $Pkgpatchver $pkgabbrev.\* 2>/dev/null \ | $HEAD -1 | awk ' { print $2 } ') if [[ -n $pkginst ]]; then # package exists with matching ARCH and VERSION fields # Now check if patch package is already applied. pkgpatchlist=$(pkgparam -f $ROOTDIR/var/sadm/pkg/$pkginst/pkginfo PATCHLIST \ | $GREP "$pkgdir") if [ "$pkgpatchlist" = "" ]; then # Patch is NOT applied. Now we need to add # a link to later check space for this unpatched pkg $LN -s $PATCHDIR/$pkgdir_and_name $DRYRUN_TMP_DIR/$pkgname.$link_count link_count=link_count+1 # Determine the size of the files to be installed and difference in # size compared to the same files already installed. Keep track of # of the comparison by filesystem parse_sizes # Compute pkg_size_max to use for backout fudge factor # pkg_size is computed in the parse_sizes function. if (( pkg_size > pkg_size_max )); then pkg_size_max=pkg_size fi pkg_size=0 fi fi fi done < $PKGLISTFILE cd $my_dir if (( link_count == 1 )); then stop_do_busy $GETTEXT "\nAll patches already applied. No disk space needed.\n" return fi # Check to determine which filesystems need to be reported and if there is # sufficient space in each. compute_fs_space_requirements # Now do dryrun pkgadd for all the patch packages. This will take a while! # The user is going to wish they had paid attention to the previous message! if [ "$DRYRUN_ONLY" = "true" ]; then $PKGADD -S -n -d $DRYRUN_TMP_DIR -R $ROOTDIR -D $DRYRUN_JUNK_DIR -a $ADMINFILE all > /dev/null 2>&1 # Report if pkgadd dryrun fails completely. if [ ! -r $DRYRUN_JUNK_DIR/dryrun.isum.asc -o ! -r $DRYRUN_JUNK_DIR/dryrun.fs.asc ]; then $GETTEXT "\n\nDryrun package check failed.\n" | tee -a ${LOGFILE} do_file_cleanup exit 24 fi if [ ! -r $DRYRUN_JUNK_DIR/dryrun.isum.asc -o ! -r $DRYRUN_JUNK_DIR/dryrun.fs.asc ]; then $GETTEXT "\n\nDryrun package check failed.\n" | tee -a ${LOGFILE} do_file_cleanup exit 24 fi dryrunExit=$($NAWK -F= ' $1 ~ /^EXITCODE$/ { print $2 } ' $DRYRUN_JUNK_DIR/dryrun.isum.asc ) # Check and report dryrun failures. if [[ "$dryrunExit" != "0" ]]; then eval_dryrun_failures $DRYRUN_JUNK_DIR fi fi stop_do_busy # Check the space required against the space available and report it check_fs_space # Add backout space information to the report if [[ "$saveold" = "yes" ]]; then # Compute the final backout space required by dividing by two in consideration # that the compressed, saved data will be smaller. To this backoutsize, add three # times the size of the largest package size (pkg_size_max). This provides the # 'fudge factor' that needs to be included to account for the space needed # to do the compression of the saved data. backout_kbytes_needed=backout_kbytes_needed/2+pkg_size_max*3 # Determine how much space is available in the backout filesystem backout_kbytes_avail=$($DF -k $BACKOUTDIR | tail -1 | $NAWK '{print $4}') backout_filesystem=$($DF -k $BACKOUTDIR | tail -1 | $NAWK '{print $6}') # Determine if the required backout space is greater than the amount # of space available. If it is, report the space descrepancy and # exit install_mu. backout_enough="YES" if (( backout_kbytes_needed > backout_kbytes_avail )); then backout_enough="NO" dryrun_space_fail="yes" fi printf "Total space Available/Needed in $backout_filesystem filesystem to include backout data,\n" >> \ $DRYRUN_REPORT | tee -a ${LOGFILE} printf "and working space for compression.\n\n" >> $DRYRUN_REPORT | tee -a ${LOGFILE} printf "%-44s%10s%10s%8s\n" " Backout Dir" "Kb Avail" "Kb Needed" "Enough?" >> $DRYRUN_REPORT printf "%-44s%10s%10s%8s\n" " $BACKOUTDIR" $backout_kbytes_avail \ $backout_kbytes_needed $backout_enough >> $DRYRUN_REPORT fi # Due to problems in the past in which we would have benefitted # from the logging of the space check, we will always display and # log the report cat $DRYRUN_REPORT | tee -a ${LOGFILE} if [ "$enough_to_install" = "no" ]; then $GETTEXT "\nNot enough disk space to apply entire patch set.\n" | tee -a ${LOGFILE} do_file_cleanup exit 22 fi if [ "$backout_enough" = "NO" ]; then $GETTEXT "\nNot enough disk space to save patch backout data.\n" | tee -a ${LOGFILE} do_file_cleanup exit 23 fi if [ "$saveold" = "yes" ]; then $GETTEXT "\nThere is sufficient space, including backout space.\n\n" | tee -a ${LOGFILE} else $GETTEXT "\nThere is sufficient disk space, not including backout space.\n\n" | tee -a ${LOGFILE} fi } #################### PrintCenter #################### # Description: # Prints contents of stdin centered in 80 columns # Parameters: # None. (reads stdin) # Globals Used: # None. # Globals Set: # None. function PrintCenter { $AWK '{ n = length($0) fmt = "%" 40-(n/2) "s%s\n" printf(fmt, " ", $0) }' } #################### update_release_file #################### # Description: # Update the contents of /etc/release to reflect the # new MU that was applied. # Parameters: # None. # Globals Used: # ROOTDIR, RELEASE_INFO_TEMPLATE, HEAD, RELEASE_FILE, MU_RELEASE_LINE # Globals Set: # None. function update_release_file { # Don't update release file if we're patching to a Service area if [ "$service_specified" = "yes" ]; then return fi # Also, don't update release file if no patches were actually installed. if [ " ${succeeded_list}" = " " ]; then return fi if [ ! -f $ROOTDIR/$RELEASE_FILE ]; then $GETTEXT "\nRelease file, $ROOTDIR/$RELEASE_FILE, not found.\n" | tee -a ${LOGFILE} return fi $HEAD -3 $ROOTDIR/$RELEASE_FILE > $ROOTDIR/$RELEASE_FILE.tmp$$ $MV $ROOTDIR/$RELEASE_FILE.tmp$$ $ROOTDIR/$RELEASE_FILE # And add release information to the end of the release file if [ "$UserInterrupted" = "no" ]; then $ECHO $MU_RELEASE_LINE | PrintCenter >> $ROOTDIR/$RELEASE_FILE $GETTEXT "\nUpdated `$ECHO $ROOTDIR/$RELEASE_FILE | $COMPACT_SLASHES` successfully.\n" | tee -a ${LOGFILE} fi } #################### verify_patch_patch 4470489 ####### # # Description : Introduced into MU7 as there is a patchdb # corruption issue and the patch thats required # to fix it has not yet been released # # # Parameters : None # # Globals Used : PATCHDIR PATCHADD PKGDBARG MU_PLATFORM # # Globals Set : # function verify_patch_patch { patchSparc="108987-08" patchX86="108988-09" fgrepSparc="112396-01" fgrepX86="112397-02" failedPatchCheck="" fail=0 # Check for patchadd patch if [ $MU_PLATFORM = "sparc" ] then patchAdep=$patchSparc elif [ $MU_PLATFORM = "i386" ] then patchAdep=$patchX86 fi $PATCHADD $PKGDBARG -p | grep $patchAdep >>/dev/null if [ $? != 0 ] then failedPatchCheck="$patchAdep" fail=1 fi # Check for fgrep patches if [ $MU_PLATFORM = "sparc" ] then patchAdep=$fgrepSparc elif [ $MU_PLATFORM = "i386" ] then patchAdep=$fgrepX86 fi $PATCHADD $PKGDBARG -p | grep $patchAdep >>/dev/null if [ $? != 0 ] then failedPatchCheck="$failedPatchCheck $patchAdep" fail=1 fi if [ $fail = 1 ] then $GETTEXT "\n\n***************************************************************\n" $GETTEXT "* Patch(es) $failedPatchCheck not installed - exiting. \n" $GETTEXT "* ----------------------------------------------------------- \n" $GETTEXT "* these are mandatory patch(es) for Solaris 8 MU7 ($MU_PLATFORM) as \n" $GETTEXT "* they resolve some patch database corruption issues. Please \n" $GETTEXT "* install the patch(es) before applying the Maintenance Update. \n" $GETTEXT "* The patch(es) may be obtained via SunSolve or your normal patch \n" $GETTEXT "* distribution channels. \n" $GETTEXT "***************************************************************\n\n" exit 28 fi } #################### display_patch_text 4459335 ####### # # Description : Introduced into MU5 to display textual # instructions to the end user from patches. # # Parameters : None # # Globals Used : PATCHDIR PATCHADD PKGDBARG # # Globals Set : # function display_patch_text { patchList="109906-06 109695-02 108875-09" text10990606="dhcpagent supports a new keyword in /etc/default/dhcpagent.\n Please merge the existing /etc/default/dhcpagent with the\n patched version of /etc/default/dhcpagent shipped with this\n patch.\n" text10969502="This patch introduces a change in the Opencard Parameters.\n Please merge the existing /etc/smartcard/opencard.properties\n with the patched version of the Opencard Parameters shipped\n with this patch.\n" text10887509="NOTICE: The following file is being replaced by this patch\n \n /etc/security/audit_event\n \n A copy of it has been saved under the name:\n \n /etc/security/audit_event.05150122\n \n The saved file needs to be scrutinized for any customizations\n that may have been made. Please merge these modifications\n into the new audit_event that was delivered by this patch.i" for patch in $patchList do $PATCHADD $PKGDBARG -p | grep $patch >>/dev/null if [ $? = 0 ] then tmp="text`echo $patch | $SED 's/-//'`" $GETTEXT "\n\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" $GETTEXT "You installed $patch this patch has some special instructions\n" $GETTEXT "as follows.\n" $GETTEXT "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" $GETTEXT "$(eval echo $`echo $tmp`)" $GETTEXT "\n================================================================\n\n" fi done } #################### add_SUNWsolnm #################### # Description: # Deliver the SUNWsolnm package. # Parameters: # None. # Globals Used: # PATCHDIR, PKGDBARG, ADMINFILE, PKGADDLOGFILE, GREP, LOGFILE # Globals Set: # None. function add_SUNWsolnm { # Don't apply SUNWsolnm package if we're patching to a Service area if [ "$service_specified" = "yes" ]; then return fi if [ ! -d $PATCHDIR/Pkg/SUNWsolnm ]; then $GETTEXT "No SUNWsolnm package exists in $PATCHDIR/Pkg.\n" | tee -a ${LOGFILE} return fi $GETTEXT "Doing pkgadd of SUNWsolnm.\n" | tee -a ${LOGFILE} $PKGADD $PKGDBARG -S -a $ADMINFILE -n -R $ROOTDIR -d $PATCHDIR/Pkg SUNWsolnm >$PKGADDLOGFILE 2>&1 sp_pkgadderr=$? sp_pkgaddfailed=0 if [ $sp_pkgadderr = 2 ]; then $GREP ERROR $PKGADDLOGFILE >/dev/null 2>&1 if [ $? = 0 ]; then sp_pkgaddfailed=1 fi elif [ $sp_pkgadderr != 0 -a $sp_pkgadderr != 10 -a $sp_pkgadderr != 20 ]; then sp_pkgaddfailed=1 fi if [ $sp_pkgaddfailed = 1 ]; then $ECHO "SUNWsolnm pkgadd failed, error=$sp_pkgadderr.\n" | tee -a ${LOGFILE} $GETTEXT "pkgadd error messages follow:\n" | tee -a ${LOGFILE} $GETTEXT "********************************************\n" | tee -a ${LOGFILE} $CAT $PKGADDLOGFILE | tee -a ${LOGFILE} $GETTEXT "********************************************\n" | tee -a ${LOGFILE} else $GETTEXT "\nSUNWsolnm package successfully installed.\n" | tee -a ${LOGFILE} fi } #################### Main program #################### # Make sure the required commands are present. eval_utilities # Make sure effective UID is '0' validate_uid # Parse command-line arguments. ARGS=$* parse_args $* # Identify softinfo files for Manager and Target systems find_softinfos $ROOTDIR # Get the OS Version of the target system get_OS_version $TRGSOFTINFO $MGRSOFTINFO $ROOTDIR if [ "$MgrOSVers" != "$MU_OS" ]; then $GETTEXT "System running install_mu must be Solaris $MU_OS.\n" | tee -a ${LOGFILE} $GETTEXT "System is running Solaris $MgrOSVers.\n" | tee -a ${LOGFILE} exit 17 fi if [ "$TrgOSVers" != "$MU_OS" ]; then $GETTEXT "install_mu can only patch Solaris $MU_OS systems.\n" | tee -a ${LOGFILE} $GETTEXT "Target system is version $TrgOSVers.\n" | tee -a ${LOGFILE} exit 17 fi # Ensure we have 108987-08 (sparc) or 108988-09 (x86) and # 112396-01 (sparc) or 112397-02 (X86) as they # fix the patchdb corruption being seen (bug 4470489) verify_patch_patch # Set PATCHDIR to absolute path to patch set set_patchdir #### COMMENTED OUT UNTIL DISKLESS CLIENT SUPPORT IS REENABLED IN THE MU ###### ## # Set the path to patchadd_mu and patchrm_mu ##TOOLSDIR=$PATCHDIR/../../common/Tools ## ##if [ ! -d $TOOLSDIR ]; then ## $GETTEXT "Directory with patch tools, $TOOLSDIR, not found.\n" ## print_usage ## exit 18 ##fi ##tmpdir=`$PWDCMD` ##cd $TOOLSDIR ##TOOLSDIR=`$PWDCMD` ##if [ ! -x $TOOLSDIR/patchadd_mu -o ! -r $TOOLSDIR/patchadd_mu ]; then ## $GETTEXT "$TOOLSDIR/patchadd_mu does not exist or is not executable.\n" ## print_usage ## exit 19 ## fi ##if [ ! -x $TOOLSDIR/patchrm_mu -o ! -r $TOOLSDIR/patchrm_mu ]; then ## $GETTEXT "$TOOLSDIR/patchrm_mu does not exist or is not executable.\n" ## print_usage ## exit 19 ##fi ##cd $tmpdir ##PATCHADD=$PATCHDIR/../../common/Tools/patchadd_mu # ======================================================================== # Set up trap handler trap 'handle_trap 1' 1 trap 'handle_trap 2' 2 trap 'handle_trap 3' 3 trap 'handle_trap 15' 15 # Broadcast what we are doing. # # From now on we write to LOGFILE # if [ "$DRYRUN_ONLY" = "true" ]; then $GETTEXT "\nRunning ${MS_NAME} ${RELEASE} (version $VERSION) `date`\n" tee -a ${LOGFILE} $GETTEXT "in Dry Run Mode. No patches will be applied.\n" $GETTEXT "\nLogging output to log file: ${LOGFILE}\n" | tee -a ${LOGFILE} $GETTEXT "\nUsing patch set in ${PATCHDIR}\n" else $GETTEXT "\nInstalling ${MS_NAME} ${RELEASE} (using install_mu $VERSION) \n `date`\n" | tee -a ${LOGFILE} $GETTEXT "\nLogging output to log file: ${LOGFILE}\n" | tee -a ${LOGFILE} $GETTEXT "\nInstalling patches located in ${PATCHDIR}\n" | tee -a ${LOGFILE} if [ "$ARGS" = "" ]; then ARGS="None." fi $GETTEXT "\nArguments to install_mu: " | tee -a ${LOGFILE} $GETTEXT "${xsaveold} $PKGDBARG \ $BACKOUTARG $PATCHDIR/\n\n" | sed 's/[ ][ ]*/ /g' | tee -a ${LOGFILE} if [ "$ROOTDIR" != "/" ]; then $GETTEXT "\nNote: You are applying the Maintenance Update to a client.\n" | tee -a ${LOGFILE} $GETTEXT "You will need to invoke install_mu a second time with the\n" | tee -a ${LOGFILE} $GETTEXT "-S option to patch the client's Service area (unless the\n" | tee -a ${LOGFILE} $GETTEXT "client's Service area has already been patched with the\n" | tee -a ${LOGFILE} $GETTEXT "Maintenance Update.) Failure to reinvoke install_mu will\n" | tee -a ${LOGFILE} $GETTEXT "leave you with an improperly patched and unstable client.\n\n" | tee -a ${LOGFILE} $GETTEXT "Note also that clients have only a subset of the packages\n" | tee -a ${LOGFILE} $GETTEXT "that a server has - you will see a number of messages about\n" | tee -a ${LOGFILE} $GETTEXT "patches not being installed due to none of the patch packages\n" | tee -a ${LOGFILE} $GETTEXT "being installed on the system. This is normal.\n\n" | tee -a ${LOGFILE} fi if [ "$service_specified" = "yes" ]; then $GETTEXT "\nNote: You are applying the Maintenance Update to a Service area.\n" | tee -a ${LOGFILE} $GETTEXT "You will need to invoke install_mu again with the -R option\n" | tee -a ${LOGFILE} $GETTEXT "for each client using this Service area. Failure to do so\n" | tee -a ${LOGFILE} $GETTEXT "will leave you with an improperly patched and unstable client.\n" | tee -a ${LOGFILE} $GETTEXT "If the Service area is shared by the server then you need to\n" | tee -a ${LOGFILE} $GETTEXT "apply the same patch set to your server's root area as well.\n\n" | tee -a ${LOGFILE} fi $GETTEXT "Please REBOOT your system after patch set has been applied.\n\n" fi # Create the admin file for pkgadd build_admin_file # Make sure there's enough disk space. Quit if there isn't enough space. if [ "$SKIP_DRYRUN" = "false" ]; then check_disk_space else $GETTEXT "Skipping disk space check.\n\n" | tee -a ${LOGFILE} fi # If dryrun mode then quit now. if [ "$DRYRUN_ONLY" = "true" ]; then do_file_cleanup exit 0 fi # Install all the patches in PATCHDIR typeset -i count=0 typeset -i num_patches num_patches=$(wc -l $PATCHDIR/.order | awk '{print $1}') # This cd is done to maintain the cwd to PATCHDIR to suppress auto unmounts cd $PATCHDIR for patch in `cat $PATCHDIR/.order`; do count=count+1 print -n "Installing ${patch} ($count of $num_patches) " | tee -a ${LOGFILE} activate_do_busy $PATCHADD ${xsaveold} $PKGDBARG $BACKOUTARG $PATCHDIR/$patch >> ${LOGFILE} 2>&1 result=$? stop_do_busy if [ ${result} -ne 0 ]; then $GETTEXT " Installation of ${patch} failed: \n ${PatchAddError[$result]}\n" failed_list="${failed_list} ${patch} ${result} : " else succeeded_list="${succeeded_list} ${patch}" fi done UserInterrupted="no" # Install or update .mu_applied. handle_mu_applied # Bugid 4459335 (patches produce output that is hidden in the log) display_patch_text # List the patches that succeeded and failed installation (if any). # Clean up log file do_file_cleanup # Install SUNWsolnm package if it exists # add_SUNWsolnm exit 0