# This script initializes the backout data for a patch package
# directory format options.
# 
#       @(#)preinstall 1.11 00/10/30 SMI
#
# Copyright (c) 1995 by Sun Microsystems, Inc.
# All rights reserved
#

PATH=/usr/sadm/bin:$PATH
recovery="no"

if [ "$PKG_INSTALL_ROOT" = "/" ]; then
	PKG_INSTALL_ROOT=""
fi

# Check to see if this is a patch installation retry.
if [ "$INTERRUPTION" = "yes" ]; then
    if [ -d "$PKG_INSTALL_ROOT/var/tmp/$SUNW_PATCHID.$PKGINST" ] || [ -d "$PATCH_BUILD_DIR/$SUNW_PATCHID.$PKGINST" ]; then
        recovery="yes"
    fi
fi

if [ -n "$PATCH_BUILD_DIR" -a -d "$PATCH_BUILD_DIR" ]; then
	BUILD_DIR="$PATCH_BUILD_DIR/$SUNW_PATCHID.$PKGINST"
else
	BUILD_DIR="$PKG_INSTALL_ROOT/var/tmp/$SUNW_PATCHID.$PKGINST"
fi

FILE_DIR=$BUILD_DIR/files
RELOC_DIR=$BUILD_DIR/files/reloc
ROOT_DIR=$BUILD_DIR/files/root
PROTO_FILE=$BUILD_DIR/prototype
ORIGPKGINFO=$PKG_INSTALL_ROOT/var/sadm/pkg/$PKGINST/pkginfo
PKGINFO_FILE=$BUILD_DIR/pkginfo
THIS_DIR=`dirname $0`
INSTALLINGPKGINFO=`dirname $THIS_DIR`

if [ "$PATCH_PROGRESSIVE" = "true" ]; then
	# If this is being used in an old-style patch, insert
	# the old-style script commands here.

	#XXXOld_CommandsXXX#

	exit 0
fi

#
# Unless specifically denied, initialize the backout patch data by
# creating the build directory and copying over the original pkginfo
# which pkgadd saved in case it had to be restored.
#
if [ "$PATCH_NO_UNDO" != "true" ] && [ "$recovery" = "no" ]; then
	if [ -d $BUILD_DIR ]; then
		rm -r $BUILD_DIR
	fi

	# If this is a retry of the same patch then recovery is set to
	# yes. Which means there is a build directory already in
	# place with the correct backout data.

	if [ "$recovery" = "no" ]; then
		mkdir $BUILD_DIR
		mkdir -p $RELOC_DIR
		mkdir $ROOT_DIR
	fi

	#
	# Here we initialize the backout pkginfo file by first
	# copying over the old pkginfo file and then adding the
	# ACTIVE_PATCH parameter so the backout will know what patch
	# it's backing out.
	#
	# NOTE : Within the installation, pkgparam returns the
	# original data.
	#
	pkgparam -v $PKGINST | nawk '
		$1 ~ /PATCHLIST/	{ next; }
		$1 ~ /CLASSES/		{ next; }
		$1 ~ /PATCH_OBSOLETES/	{ next; }
		$1 ~ /ACTIVE_OBSOLETES/	{ next; }
		$1 ~ /SUNW_OBSOLETES/	{ next; }
		$1 ~ /ACTIVE_PATCH/	{ next; }
		$1 ~ /SUNW_PATCHID/	{ next; }
		$1 ~ /UPDATE/		{ next; }
		$1 ~ /SCRIPTS_DIR/	{ next; }
		$1 ~ /PATCH_NO_UNDO/	{ next; }
		$1 ~ /INSTDATE/		{ next; }
		$1 ~ /PKGINST/		{ next; }
		$1 ~ /OAMBASE/		{ next; }
		$1 ~ /PATH/		{ next; }
		{ print; } ' | sed s,\'\"\'\"\',ApOsTrOpHe,g > $PKGINFO_FILE

        # The previous is needed to workaround pkgparam
        # inserting '"'"' for every ' it finds in the
        # pkginfo file. see bugid 4052001.

	# If there is an undo script delivered with this patch pkg
	# there is a possibility that when the patch gets backed out
	# it may not get executed due to the CLASSES macro in the
	# original pkginfo file may not contain the class
	# identifier. Thus we need to merge the old CLASSES macro and
	# the installing patch pkgs CLASSES macro.

	nawk ' $1 ~ /CLASSES/ {print $0} ' $INSTALLINGPKGINFO/pkginfo \
		| sed s/CLASSES=// > /tmp/installingCLASSES.$$
 
	classIDs=`nawk ' $1 ~ /CLASSES/ {print $0} ' $ORIGPKGINFO \
		| sed s/CLASSES=//`
 
	for instClass in $classIDs; do
		notFound=`grep "$instClass" /tmp/installingCLASSES.$$`
		if [ -z "$notFound" ]; then
			newCLASSESlist="$instClass $newCLASSESlist"
		fi
		notFound=""
	done
 
	classes=`nawk ' $1 ~ /CLASSES/ {print $0} ' $ORIGPKGINFO`  
	newCLASSESlist="$classes $newCLASSESlist"  
	
	echo "$newCLASSESlist" >> $PKGINFO_FILE
	echo "ACTIVE_PATCH=$SUNW_PATCHID" >> $PKGINFO_FILE
	echo "ACTIVE_OBSOLETES=$ACTIVE_OBSOLETES" >> $PKGINFO_FILE

	rm /tmp/installingCLASSES.$$

	# And now initialize the backout prototype file with the
	# pkginfo file just formulated.
	echo "i pkginfo" > $PROTO_FILE

	# Copy over the backout scripts including the undo class
	# action scripts
	for script in $SCRIPTS_DIR/*; do
		srcscript=`basename $script`
		targscript=`echo $srcscript | nawk '
			{ script=$0; }
			/u\./ {
				sub("u.", "i.", script);
				print script;
				next;
			}
			/patch_/ {
				sub("patch_", "", script);
				print script;
				next;
			}
			{ print "dont_use" } '`
		if [ "$targscript" = "dont_use" ]; then
			continue
		fi

		echo "i $targscript=$FILE_DIR/$targscript" >> $PROTO_FILE
		cp $SCRIPTS_DIR/$srcscript $FILE_DIR/$targscript
	done

	#
	# Now add entries to the prototype file that won't be passed to
	# class action scripts. If the entry is brand new, add it to the
	# deletes file for the backout package.
	#
	Our_Pkgmap=`dirname $SCRIPTS_DIR`/pkgmap
	BO_Deletes=$FILE_DIR/deletes

	nawk -v basedir=${BASEDIR:-/} '
		BEGIN { count=0; }
		{
			token = $2;
			ftype = $1;
		}
		$1 ~ /[#\!:]/ { next; }
		$1 ~ /[0123456789]/ {
			if ( NF >= 3) {
				token = $3;
				ftype = $2;
			} else {
				next;
			}
		}
		{ if (ftype == "i" || ftype == "e" || ftype == "f" || ftype == "v" || ftype == "d") { next; } }
		{
			equals=match($4, "=")-1;
			if ( equals == -1 ) { print $3, $4; }
			else { print $3, substr($4, 0, equals); }
		}
		' < $Our_Pkgmap | while read class path; do
			#
			# NOTE: If pkgproto is passed a file that is
			# actually a hard link to another file, it
			# will return ftype "f" because the first link
			# in the list (consisting of only one file) is
			# viewed by pkgproto as the source and always
			# gets ftype "f".
			#
			# If this isn't replacing something, then it
			# just goes to the deletes list.
			#
			if valpath -l $path; then
				Chk_Path="$BASEDIR/$path"
				Build_Path="$RELOC_DIR/$path"
				Proto_From="$BASEDIR"
			else	# It's an absolute path
				Chk_Path="$PKG_INSTALL_ROOT$path"
				Build_Path="$ROOT_DIR$path"
				Proto_From="$PKG_INSTALL_ROOT"
			fi

			#
			# Hard links have to be restored as regular files.
			# Unlike the others in this group, an actual
			# object will be required for the pkgmk.
			#
       			if [ -f "$Chk_Path" -a ! -h "$Chk_Path" ] ; then
				mkdir -p `dirname $Build_Path`
				cp -p $Chk_Path $Build_Path
				cd $Proto_From
				pkgproto -c $class "$Build_Path=$path" 1>> $PROTO_FILE 2> /dev/null
				cd $THIS_DIR
			elif [ -h "$Chk_Path" -o \
			     -c "$Chk_Path" -o \
			     -b "$Chk_Path" -o \
			     -p "$Chk_Path" ]; then
				pkgproto -c $class "$Chk_Path=$path" \
					1>> $PROTO_FILE 2> /dev/null
			else
				echo $path >> $BO_Deletes
			fi
		done
fi

# If additional operations are required for this package, place
# those package-specific commands here.

#XXXSpecial_CommandsXXX#

if [ "${BASEDIR}" = "/" ]; then
   rel=`uname -r`
elif [ -n "$PATCH_CLIENT_VERSION" ]; then
   rel=$PATCH_CLIENT_VERSION
else
   release_file=${BASEDIR}/var/sadm/system/admin/INST_RELEASE
   if [ -f "$release_file" ]; then
      rel=`grep VERSION $release_file | nawk -F= '{print $2}'`
   else
      echo "It's not clear the OS version of the system. Patch Terminating.."
      echo
      exit 1   
   fi
fi

echo

case ${rel} in
   5.6|2.6|5.7|2.7|7|5.8|2.8|8|5.9|2.9|9)
                SUFFIX="SunOS_5.`expr ${rel} : '.*\(.\)'`"
        ;;
   *)   echo
        echo " This OS version is not supported by this patch"
        echo " Patch terminating.."
        echo
        exit 1
        ;;
esac

#
# Need to check to make sure certain OS patches are installed
# This checking could not be done using pkginfo's SUNW_REQUIRES
# because the requirement patch is dependent on OS version.
#

echo "Checking for required patches."

#
# There is a known Solaris bug (Sub BugID 4625879) in which
# "patchadd -p" fails to while running as non-root. Solaris
# pkgadd facility will attempt to execute this script as
# "install" uid (if it exist).
#
# Work-around fix would be to use "showrev -p" in case
# "patchadd" fails. Howver, in Jumpstart environment,
# Sun patch to fix this patchadd is required for jumpstart
# because showrev method does not work.
#
patchadd -p > /dev/null
if [ $? -eq 3 ]
then
	PATCHCHK="showrev -p"
else
	PATCHCHK="patchadd -R ${BASEDIR} -p"
fi

#
# Define the required patches depending on the OS version
#
case ${rel} in
   5.6|2.6|6)
	required_patch="105181-31"
	;;
   5.7|2.7|7)
	required_patch="107473-07 106541-19"
	;;
   5.8|2.8|8)
	# Need to handle a special case for PRIMEPOWER system which has "FJSV"
	#  in uname output
	sys=`uname -i | awk 'BEGIN { FS="," } {print $1}'`
	echo "System is $sys"
	if [ "$sys" = "FJSV" ]
	then
		required_patch="108993-18 108528-14"
	else
		required_patch="111413-06 108993-18 108528-14"
	fi
	;;
   5.9|2.9|9)
	required_patch=""
	;;
esac

PATCHESINSYS=/tmp/patchesinsystem.$$

#get the list of patches in the system
$PATCHCHK > $PATCHESINSYS

#
# Loop through every required patch and build a list of missing
#  patches into "missing_patch" variable
# 
# If the system contains all the required patch, then 
#   "missing_patch" variable will be empty.
#
for cur_patch in `echo $required_patch`
do
	patchID=`echo $cur_patch | awk -F"-" '{ print $1 }'`
	patchLVL=`echo $cur_patch | awk -F"-" '{ print $2 }'`

	# if the required patch is obsoleted then we search the patches
	# that has obsoleted the required patch
	isobsoleted=`grep -c "^Patch:.*Obsoletes:.*${patchID}.*Requires:.*$" $PATCHESINSYS`
	if [ $isobsoleted -eq 0 ]
	then
		result=`fgrep "Patch: ${patchID}-" $PATCHESINSYS`
		if [ -z "$result" ]
		then
			missing_patch="$missing_patch\n\t$cur_patch (or higher)"
		else
			# Need to make sure we have the minimum level
			system_patch=`fgrep "Patch: $patchID-" $PATCHESINSYS | \
			nawk ' BEGIN { highest_patch_lvl=0; }
			{
				if ($2 > highest_patch_lvl)
					highest_patch_lvl=$2;
			}
			END { print highest_patch_lvl }'`
			system_patchLVL=`echo $system_patch | awk -F"-" '{ print $2 }'`
			if [ "$system_patchLVL" -lt "$patchLVL" ]
			then
				missing_patch="$missing_patch\n\t$cur_patch (or higher)"
			fi
		fi
	else
		obsoletedby=`grep "^Patch:.*Obsoletes:.*${patchID}.*Requires:.*$" $PATCHESINSYS | nawk '{print $2}'`
		ismissing=1
		for obspatch in $obsoletedby
		do
			obsoletedbypatchID=`echo $obspatch | awk -F"-" '{ print $1 }'`
			obsoletedbypatchLVL=`echo $obspatch | awk -F"-" '{ print $2 }'`
			if [ $obsoletedbypatchID = $patchID ]
			then
				if [ $patchLVL -le $obsoletedbypatchLVL ]
				then
					ismissing=0
					break
				fi
			else
				echo "\nVolume Manager requires system patch $cur_patch. While searching for\nthis required patch $cur_patch, it has been detected that this patch has\nbeen now obsoleted by patch $obspatch. Hence, this meets Volume Manager\nrequirement for this patch.\n"
				ismissing=0
				break
			fi
		done
		if [ $ismissing -eq 1 ]
		then
			missing_patch="$missing_patch\n\t$cur_patch (or higher)"
		fi
	fi
done

missing_patch_cnt=`echo $missing_patch | wc -w`
missing_patch_cnt=`expr $missing_patch_cnt / 3`
						
if [ $missing_patch_cnt -gt 1 ]
then
	echo "\nThe following patches were not detected in this system. It\n\
is required that you install these patches before you can\n\
continue; otherwise Volume Manager may not function properly."
	echo $missing_patch
	echo "\n"
elif [ $missing_patch_cnt -eq 1 ]
then
	# There is only one missing patch, so go through the list of
	# patches and print a unique message
	for cur_patch in `echo $required_patch`
	do
		patchID=`echo $cur_patch | awk -F"-" '{ print $1 }'`
		patchLVL=`echo $cur_patch | awk -F"-" '{ print $2 }'`
		result=`$PATCHCHK | fgrep "Patch: ${patchID}-"`
		if [ -z "$result" ]
		then
			echo "\nPatch $cur_patch (or higher) is not detected in this system. It\n\
is required that you install this patch before you can\n\
continue; otherwise Volume Manager may not function properly.\n"
		else
			# Need to make sure we have the minimum level
			system_patch=`$PATCHCHK | fgrep "Patch: $patchID-" | \
			nawk ' BEGIN { highest_patch_lvl=0; }
			{
				if ($2 > highest_patch_lvl)
					highest_patch_lvl=$2;
			}
			END { print highest_patch_lvl }'`
			system_patchLVL=`echo $system_patch | awk -F"-" '{ print $2 }'`
			if [ "$system_patchLVL" -lt "$patchLVL" ]
			then
				echo "\nPatch $system_patch has been detected in this system. However, it needs\n\
to be at patch revision $patchLVL or higher. It is required that you install\n\
the latest patch level before you can continue; otherwise Volume Manager\n\
may not function properly.\n"
			fi
		fi
	done
fi

rm $PATCHESINSYS

if [ $missing_patch_cnt -gt 0 ]
then
	echo "\nThis patch installation will now be aborted.\n"
	exit 1
fi

exit 0
