#!/usr/bin/sh # # (c)Copyright 1996 Hewlett-Packard Co., All Rights Reserved. # $Header: /ignite/cold_fusion/src/server_admin/fix_patches 10.6 1998/08/24 17:35:14 paulc Exp $ # # Program to modify patch 'checkinstall' control files to NOT exit with # an error if they find they have been superseded. Due to an SD # problem, the checkinstall must then be moved to run as a fileset # checkinstall rather than a product checkinstall. This is specifically # for use by manufacturing (or distributors) when setting up "patch" # release distribution, for example, 10.20 ACE and/or Hardware Extensions. # # Usage: fix_patches # # Example depot path might be: /var/opt/ignite/depots/Rel_B.10.20/apps_800 exitval=0 if [[ $1 != /* || ! -d $1 ]]; then echo echo 'Usage: fix_patches [patch] [patch] ...' echo echo ' Where is the path to the SD directory depot,' echo ' beginning with "/", and [patch] (optional) is a specific' echo ' patch to fix in that depot.' echo echo 'Example:' echo echo ' fix_patches /var/opt/ignite/depots/Rel_B.10.20/apps_800' echo echo ' This program will look at the product "checkinstall" scripts' echo ' of all products in the depot of the form PHCO_,' echo ' PHKL_, PHNE_, and PHSS_. If they' echo ' match the expected "bad" format, they will be edited to' echo ' the "good" format. The "good" format exits with the SD' echo ' EXCLUDE code instead of an ERROR if the checkinstall script' echo ' finds that its patch has been superseded by a patch currently' echo ' on the system.' echo echo 'WARNING: make sure the depot is not in use before running this program.' exit 1 fi depot_path=$1 shift patches=$* if [[ ! -d $depot_path/catalog ]]; then echo "The directory $depot_path does not appear to contain an SD depot" exit 1 fi if [[ $(id -u) -ne 0 ]]; then echo "Fix_patches must be done by the root user." exit 1 fi if ps -ef | grep -Eq "swagent[[:space:]]+${depot_path%%+(/)}[[:space:]]+"; then echo "The depot $depot_path is in use. Wait for the SD-UX operation" echo "to complete, then restart fix_patches. Unregister the depot if" echo "necessary to prevent new SD operations from starting." exit 1 fi cd $depot_path/catalog # General note: poking around in SD depots directly is VERY subject to # the whims of the SD version. A more robust way of getting lists # of patches with checkinstalls would be to start with something like: # swlist -l product -a instance_id -a software_spec -a control_files \ # -s PH??_[0-9]* #----------------------------------------- # First, look for checkinstall scripts at the product level. It # is these that must be modified and moved to the fileset level. # Some patches have multiple filesets, named after core filesets, # under the patch product, instead of one fileset which is named # the same as the patch. The former type of patch will be ignored # because of difficulties in handling the DSA portion of the checkinstall. # # If a fileset level checkinstall exists, it will be checked and, if it's # in the expected format, modified as needed. if [ -n "$patches" ]; then patches=$(ls -d $patches 2>/dev/null) else patches=$(ls -d PH@(CO|KL|NE|SS)_[0-9]* 2>/dev/null) fi if [[ -z $patches ]]; then echo "WARNING: No patches (or no patches specified on the command line)" echo " were found in the depot $depot_path." echo " No changes were made." exit 0 fi # Check for multiple versions. Multiple versions of patches should not # exist in the depot. Ignore patches with multiple versions. Product # directories with "." in them indicate potential multiple versions. # Warn if multiple versions exist. repeats=$(print "$patches" | sed 's/\..*//' | uniq -d) # Turn any products like "PHCO_1234.1" to "PHCO_1234". Also, if # (e.g.) both PHCO_1234.1 and PHCO_1234 exist, remove both from list. patches=$(print "$patches" | sed 's/\..*//' | uniq -u) if [[ -n $repeats ]]; then print "WARNING: The following patch(es) have multiple versions:" print " "$repeats print print " Please check your depot. These patches will be IGNORED." print fi # Here's the section of 'checkinstall' that we're trying to fix with this # program... # ===================================================================== # # If there is a semaphore around, this patch has already been # # superseded by another patch that is currently installed. Fail. # if [ -r $_SUPERSEDEDIR/$_PATCHID ]; then # echo "ERROR: the patch '$_PATCHID' has been superseded by the" # echo " following patch : \c" # head -1 $_SUPERSEDEDIR/$_PATCHID # echo " >>>> This patch MUST NOT be installed. <<<<" # exit 1 # fi # ===================================================================== pos=0 # For printing. patches_changed="" print "\nBeginning 'checkinstall' modifications..." for cpatch in $patches; do # If this patch is stored under a "dot" name/directory, get that directory. patchdir=$(print $cpatch?(.*)) # If multiple filesets (always have 'pfiles') under this product, skip it. fileset=$(ls $patchdir | grep -v pfiles) if [[ $(print "$fileset" | wc -w) -gt 1 ]]; then print "\nWARNING: Patch $cpatch has multiple filesets. Skipping it." pos=0 continue fi # See if fileset checkinstall exists, if so, run modification/check on it, # otherwise run on product checkinstall, otherwise skip. modfile=$patchdir/$fileset/checkinstall [[ ! -f $modfile ]] && modfile=$patchdir/pfiles/checkinstall [[ ! -f $modfile ]] && continue wc -l $modfile | read prelines junk awk ' BEGIN {state=0} /superseded.*patch.*currently installed.*Fail/ { sub(" *Fail.*", "") } # Check for already fixed. /WARNING:[ ]*the patch.*has been superseded by the/ { state=10 } /ERROR:[ ]*the patch.*has been superseded by the/ { sub("ERROR:( )?", "WARNING:") state=1 } # Check for already fixed. />>>>[ ]*This patch WILL NOT be installed/ && state==10 { state=20 } />>>>[ ]*This patch MUST NOT be installed/ && state==1 { sub("MUST", "WILL") state=2 } # Check for already fixed. /exit[ ]+3/ && state==20 { state=30 } /exit[ ]+1/ && state==2 { sub("1", "3") state=3 } { print $0 } END { if (state==3) exit(0) else if (state==30) exit(2) else exit(1) }' $modfile > /tmp/fix_patches$$ res=$? do_swmod=0 if [[ $res -eq 1 ]]; then if [[ $modfile = $patchdir/pfiles/checkinstall ]]; then print "\nWARNING: Patch $cpatch checkinstall not in expected format." print " No change can be made. This patch will still generate" print " ERRORs if loaded after a patch which supersedes it." else print "\nWARNING: Existing 'special' fileset checkinstall for $cpatch." print " No change can be made. This patch will still generate" print " ERRORs if loaded after a patch which supersedes it." fi pos=0 exitval=1 elif [[ $res -eq 2 ]]; then # The checkinstall has already been fixed, but it may be still # under pfiles (product level) and not moved into place. This # is handled below. Just print a note here. do_swmod=1 if [[ $modfile = $patchdir/pfiles/checkinstall ]]; then print "\nProgram was apparently previously interrupted." print "Completing modifications to patch $cpatch." else print "\nPatch $cpatch is already fixed. Redoing swmodify." fi # Don't print patch name later, we mentioned it here already. pos=-1 elif wc -l /tmp/fix_patches$$ | read lines junk [[ $lines -ne $prelines ]]; then print "\nERROR: Editing problem with $cpatch checkinstall." print " Lines were lost or added. No changes made." pos=0 exitval=1 else cp /tmp/fix_patches$$ $modfile rm /tmp/fix_patches$$ do_swmod=1 fi if (( do_swmod )); then if [[ $modfile = $patchdir/pfiles/checkinstall ]]; then mv $modfile $patchdir/$fileset/checkinstall fi # Remove reference to product level checkinstall. Since swmodify # will error if reference is already gone, check first. if grep -Eqs '^tag[[:space:]]+checkinstall' $cpatch/pfiles/INFO; then if swmodify -d -u -xcontrol_files=checkinstall $cpatch @ $depot_path then : All ok else echo "\nERROR: swmodify failed; depot could be in use by another" echo " process. Unregister the depot and restart." exit 1 fi fi # swmodify in the info on the fileset level checkinstall. if swmodify -d -xcontrol_files=checkinstall $cpatch.$fileset @ $depot_path then : All ok else echo "\nERROR: swmodify failed; depot could be in use by another" echo " process. Unregister the depot and restart." exit 1 fi patches_changed="$patches_changed ${cpatch}" # Print in columns. Skip printing if patch status already mentioned. if [[ $pos -ne -1 ]]; then printf "%-14s" $cpatch (( (pos += 1) % 5 == 0 )) && print else pos=0 fi fi done print if [ -z "$patches_changed" ]; then print print "No changes were made." fi exit $exitval