# This script initializes the backout data for a patch package
# directory format options.
# 
#pragma ident	"@(#)preinstall	1.1	11/11/11 SMI"
#
# Copyright (c) 1995, 2011, Oracle and/or its affiliates. All rights reserved.
#

# Definitions for override safety mechanism
PATCH_OVERRIDE_LIB="${PKG_INSTALL_ROOT}/usr/lib/patch/patch_override_lib"

# Check if we messaging is ready. Returns 0 on success, 1 on failure
# On successful run it leaves MSG_FILENAME variable defined
# 
# Usage:
#
# check_patch_script_messaging
#
check_patch_script_messaging()
{
	TMP_DIRNAME="/var/run"
	MSG_FILENAME="${TMP_DIRNAME}/patchadd_msg_file_${SUNW_PATCHID}"

	if [ -w "${MSG_FILENAME}" ] ; then
		unset TMP_DIRNAME
		return 0
	else
		unset TMP_DIRNAME
		unset MSG_FILENAME
		return 1
	fi
}

# This function prepares a message to be printed to stdout towards the end
# of postpatch execution. If the message file is not ready, it at least tries
# immediate output to stdout which is currently know to work only in patch
# level scripts.
#
# Usage:
#
# print_patch_message "text_of_message_to_be_printed_out"
#
print_patch_message()
{
	if check_patch_script_messaging; then
		echo "$*" >> "${MSG_FILENAME}"
	else
		echo "$*"
	fi

	return 0
}

# script in a fucntion so that it can be overridden if needed - generic
preinstall_deflt()
{
	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 $SCRIPTS_DIR`/pkginfo"

	#
	# 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

		rm -rf "$BUILD_DIR"
		mkdir -m 700 "$BUILD_DIR"
		if [ $? -ne 0 ]; then
			print_patch_message \
			    "Failed to make directory $BUILD_DIR"
			exit 1
		fi

		mkdir -p "$RELOC_DIR"
		mkdir -p "$ROOT_DIR"

		#
		# 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 not containing the class
		# identifier. Thus we need to merge the old CLASSES macro and
		# the installing patch pkgs CLASSES macro.

		nawk ' $1 ~ /CLASSES/ {print $0} ' $ORIGPKGINFO \
			| sed s/CLASSES=// > /tmp/installingCLASSES.$$
	 
		classIDs=`nawk ' $1 ~ /CLASSES/ {print $0} ' $INSTALLINGPKGINFO \
			| 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

		/usr/bin/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  ' {
				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 ftype, $3, $4; }
				else { print ftype, $3, substr($4, 0, equals); }
				print $0
			}
			' < $Our_Pkgmap | while read type class path; do
				# 
				# Read the whole line from pkgmap
				# We may need to propagate it into our 
				# prototype if it is already existing hard link
				#	
				read pline

				#
				# If this isn't replacing something, then it
				# just goes to the deletes list.
				#
				
				# Checking whether path is absolute or relative
				if echo "$path" | grep '^/' 2>&1 1>/dev/null ; then
					# It's an absolute path
					Chk_Path="$PKG_INSTALL_ROOT$path"
					Build_Path="$ROOT_DIR$path"
					Proto_From="$PKG_INSTALL_ROOT"
				else
					Chk_Path="$BASEDIR/$path"
					Build_Path="$RELOC_DIR/$path"
					Proto_From="$BASEDIR"
				fi

				# If we're up against a file system that can't be
				# written to, skip adding the link to the backout
				# package. Check the links parent directory for
				# writablility. If the parent directory does
				# not exist yet, check its parent directory until
				# the parent directory is $BASEDIR or
				# $PKG_INSTALL_ROOT

				dirPath=`dirname "$Chk_Path"`

				while [ "$dirPath" != "$Proto_From" ]; do
					# If path doesn't exist check parent path
					# We use ls here because [ -e $dirPath ]
					# is not supported in /bin/sh
					/usr/bin/ls "$dirPath" 1>/dev/null 2>&1
					if [ "$?" != 0 ]; then
						dirPath=`dirname "$dirPath"`
					else
						break
					fi
				done

				if [ "$dirPath" != "$Proto_From" ]; then
					/usr/bin/touch "$dirPath/.testpatchadd.$$" \
						> /dev/null 2>&1
					if [ $? !=  0 ]; then
						continue
					fi
					/usr/bin/rm -f "$dirPath/.testpatchadd.$$"
				fi

				if [ "$type" = 'l' ] ; then
					# If hardlink already exists we put it
					# into proto_file otherwise it goes
					# into deletes
					/usr/bin/ls "$Chk_Path" 1>/dev/null 2>&1
					if [ "$?" != 0 ]; then
						echo "$path" >> $BO_Deletes
					else
						echo "$pline" >> $PROTO_FILE
					fi
				elif [ -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
}

# script in a fucntion so that it can be overridden if needed - particular
preinstall_merge()
{
	# If additional operations are required for this package, place
	# those package-specific commands here.

	#XXXSpecial_CommandsXXX#
#!/bin/sh
#
#ident	"@(#)preinstall	1.2	09/11/13 SMI"
#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

PATH="/usr/bin:/usr/sbin:${PATH}"; export PATH
MFSTSCAN=/lib/svc/bin/mfstscan
SVCCFG=/usr/sbin/svccfg
SVCADM=/usr/sbin/svcadm
SVCPROP=/usr/bin/svcprop
AWK=/usr/bin/awk
CP=/usr/bin/cp
RM=/usr/bin/rm

STOP_DELAY=60

MANIFEST=/var/svc/manifest/network/iscsi_initiator.xml
MANIFEST_DEATH=/etc/svc/iscsi_initiator.xml.death
SVCINST=svc:/network/iscsi_initiator:default
DEATHROW_FILE=/etc/svc/deathrow
ISCSIRPKG=SUNWiscsir

#
# Helper function. Delete the manifest hash value.
#

svc_delhash()
{
	$SVCCFG delhash -d $MANIFEST >/dev/null 2>&1
	if [ "$?" != "0" ];then
		# this Solaris release doesn't have delhash command
		pg_name=`$MFSTSCAN -t $MANIFEST`
		if $SVCPROP -q -p $pg_name smf/manifest; then
			$SVCCFG -s smf/manifest delpg $pg_name
		fi
	fi
}

#
# Helper function. Handle services deathrow file.
#
svc_deathrow()
{
	DEATHROW_FILE=${PKG_INSTALL_ROOT}/etc/svc/deathrow
	MANIFEST_FILE=${PKG_INSTALL_ROOT}${MANIFEST}
	MANIFEST_FILE_DEATH=${PKG_INSTALL_ROOT}${MANIFEST_DEATH}

	ENTITIES=`SVCCFG_NOVALIDATE=1 $SVCCFG inventory ${MANIFEST_FILE}`
	for fmri in $ENTITIES; do
		# add to service deathrow file
		echo ${fmri} ${MANIFEST_DEATH} ${ISCSIRPKG} >> ${DEATHROW_FILE}
	done
#	mv ${MANIFEST_FILE} ${MANIFEST_FILE_DEATH}
}

#
# Helper function. To hard code a deathrow file.
#
svc_deathrow_hardcode()
{
	DEATHROW_FILE=${PKG_INSTALL_ROOT}/etc/svc/deathrow

	echo "svc:/network/iscsi_initiator:default ${MANIFEST_DEATH} ${ISCSIRPKG}" >> ${DEATHROW_FILE}
	echo "svc:/network/iscsi_initiator ${MANIFEST_DEATH} ${ISCSIRPKG}" >> ${DEATHROW_FILE}
}

#
# Helper function. Wait the service to be disabled.
#
wait_disable() {
	while [ ${nsec:=0} -lt $STOP_DELAY ]; do
		state=`$SVCPROP -p restarter/state $SVCINST`
		if [ "$state" = "disabled" -o "$state" = "maintenance" ]; then
			nstate=`$SVCPROP -p restarter/next_state $SVCINST`
			if [ "$nstate" = "none" ]; then
				return 0
			fi
		fi
		/usr/bin/sleep 1
		nsec=`expr ${nsec} + 1`
	done
	return 1
}

if [ ! -f ${PKG_INSTALL_ROOT}${MANIFEST} ]; then
	#
	# Manifest file may get deleted before the running of this script
	# e.g., during upgrade
	#
	svc_deathrow_hardcode
	return 0
fi

old_fmri=no

ENTITIES=`$SVCCFG inventory ${PKG_INSTALL_ROOT}${MANIFEST}`
for fmri in $ENTITIES; do
	if [ "$fmri" = "$SVCINST" ]; then
		old_fmri=yes
	fi
done

if [ "$old_fmri" = "no" ]; then
	# nothing to do as there is no old fmri
	return 0
fi

#
# To remove the old fmri
#
if [ -r /etc/svc/volatile/repository_door ]; then
	smf_alive=yes
else
	smf_alive=no
fi

if [ "$PKG_INSTALL_ROOT" != "" -a "$PKG_INSTALL_ROOT" != "/" ]; then
	smf_alive=no
fi

if [ "$smf_alive" = "no" ]; then
	svc_deathrow
else
	ENTITIES=`$SVCCFG inventory $MANIFEST`
	for fmri in $ENTITIES; do
		# If this fmri refers to an instance, or a service
		$SVCPROP -p restarter/state $fmri >/dev/null 2>/dev/null
		if [ $? -eq 1 ]; then
			# a service
			$SVCCFG delete $fmri 2>/dev/null
			continue
		fi

		#
		# Disable the instance
		#
		$SVCADM disable $fmri 2>/dev/null
		if [ $? -eq 0 ]; then
			wait_disable
			if [ $? -eq 0 ]; then
				#disabled
				$SVCCFG delete -f $fmri 2>/dev/null
				continue
			fi
		fi

		#
		# Forcily removal
		#
		ctid=`$SVCPROP -p restarter/contract $fmri 2>/dev/null`
		tctid=`$SVCPROP -p restarter/transient_contract $fmri 2>/dev/null`

		$SVCCFG delete -f $fmri

		#
		# Kill remaining processes.
		#
		if [ -n "${tctid}" -a "${ctid}" -gt 1 ]; then
			# kill the stopper
			/usr/bin/pkill -9 -c $tctid
		fi
		if [ -n "${ctid}" -a "{ctid}" -gt 1 ]; then
			# kill remaining processes
			/usr/bin/pkill -9 -c $ctid
		fi
	done
	#
	# Delete manifest hash value
	#
	svc_delhash
	#
	# Nail it down
	#
	svc_deathrow
fi
	:
}

# load the override lib if it exists
if [ -f "${PATCH_OVERRIDE_LIB}" -a -r "${PATCH_OVERRIDE_LIB}" ] ; then
   . "${PATCH_OVERRIDE_LIB}"
fi

# execute the script functions
preinstall_deflt "$@"
preinstall_merge "$@"

exit 0
