#!/bin/ksh

## Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
## Use is subject to license terms.

## Is it safe to proceed.
ARCH=`uname -p`
if [ $ARCH != i386 ]; then
	exit 0
fi

PATH="/usr/bin:/usr/sbin:${PATH}"; export PATH
ZDIR=/etc/zones
PKGCOND=/usr/bin/pkgcond
PKGINFO=/usr/bin/pkginfo
patchdir="${PWD}"

typeset -r ku_base_pid=118844-21
DONT_NEED_NEWBOOT_FILE="${ROOTDIR}/tmp/.we_dont_need_newboot"

 patch=118844-19
 uname=$(uname -v)
 uname=${uname##*_}    # remove text before underscore...

 if [ "$ROOTDIR" = "/" ] ; then
     if [[ $uname == "Generic" ]] || \
     [[ ${uname%-*} == ${patch%-*} && ${uname#*-} -lt ${patch#*-} ]]; then
         print -u2 "\
         In order to apply this patch, the Kernel patch ${patch},
         must be installed and the system rebooted prior
         to installing this patch. \
         "
         exit 1
    fi
 fi


last_patch() {

	## returns the number of patches installed at or above this rev.
        ## if this is the first rev of the patch
        ##      prepatch will return 0
        ##      postpatch, prebackout, and postbackout will return 1


	root_dir=${ROOTDIR:-/}

	## parse id and rev
	pid=`echo $1 | cut -d\- -f1`
	prev=`echo $1 | cut -d\- -f2`
	patch_cnt=0

	## get all installed refernces to the installed patch base id	
	installed_patches=`patchadd -p -R $root_dir -p | sed -n -e 's/Req.*//' -e 's/[a-zA-Z]*://g' -e 's/,//g' -e "/$pid/p"`
	
	for x in $installed_patches ; do
		base=`echo $x  | cut -d\- -f1`
		rev=`echo $x | cut -d\- -f2`
		if [ $pid -eq $base ] && [ $rev -ge $prev ] ; then 
			## count all installed patches includeing this patch
			patch_cnt=$(($patch_cnt + 1))
		fi
	done
	
	return $patch_cnt
}


is_new_boot_installed() {
	#Check if NewBoot has been applied on the system
	rm -f "${DONT_NEED_NEWBOOT_FILE}"
	last_patch $ku_base_pid
	if [ "$?" -gt "0" ] ; then
	   touch "${DONT_NEED_NEWBOOT_FILE}"
	fi
}


update_logindevperm() {
        cp $ROOTDIR/etc/logindevperm $ROOTDIR/etc/logindevperm.before-$PatchNum

}

function CheckForRequiredFiles {

#
# Look for required files;  if any are missing, bail out with an error.
#

	if [ ! -x $patchdir/zfs_cache ]; then
		echo ERROR: Missing zfs_cache command.
		exit 1;
	fi

	if [ ! -f $patchdir/fbtmplmeth.ksh ]; then
		echo ERROR: Missing reboot service method template
		exit 1;
	fi

	if [ ! -f $patchdir/fbtmplsvc.xml ]; then
		echo ERROR: Missing reboot service XML manifest
		exit 1;
	fi

	return 0

}

load_mods() {

	## load modules during patch installation per 6349240
	## to prevent warnings from displaying on the console
	## before the system is rebooted.

	## find all kernel driver modules in patch
	PLATFORM=`uname -i`
	CLASS=`uname -m`
 	ISA_INFO=`/usr/bin/isainfo -b`

	cd  ${patchdir:-$PWD}

		for i in *
		do
		pkginfo `echo $i | cut -f 1 -d '.'` > /dev/null 2>&1
			if [ $? -eq 0 ]; then
				grep " kernel\>" $i/pkgmap | grep -v mpxio | grep -v kmdb
				grep "/kernel\>" $i/pkgmap | grep "${PLATFORM}/" | grep -v "cpu" | grep -v kmdb
				grep "/kernel\>" $i/pkgmap | grep "$CLASS/" | grep -v "cpu" | grep -v "kmdb/"
			fi

		done 2>/dev/null | nawk '/^1 f/ && ! /\.conf|\/unix|\/genunix/ {print $4}' >/tmp/modules.$$

		if [ "$ROOTDIR" = "/" ] ; then

		##
		## Disable kernel module unloading
		##
		print "Disabling kernel module unloading ... \n"
		if [ -x /usr/bin/mdb ]; then
			if [ $ISA_INFO = 64 ]; then
				SAVED_ADDR=`echo '_kobj_printf/J' | mdb -k | cut -f2 -d ':'` 
				echo '_kobj_printf/Z systrace_stub' | mdb -kw > /dev/null 2>&1
			else
				SAVED_ADDR=`echo '_kobj_printf/X' | mdb -k | cut -f2 -d ':'`
				echo '_kobj_printf/W systrace_stub' | mdb -kw > /dev/null 2>&1
			fi
			echo "moddebug/W20000" | adb -kw /dev/ksyms /dev/mem | grep moddebug > /dev/null 2>&1

		else
			echo "mdb not found: patchadd not safe."
		fi

		while read line; do
		## preload each module
			[ -f /$line ] && {
				## echo "loading module $line"
				modload /$line > /dev/null 2>&1
			}

		done < /tmp/modules.$$

		fi

		if [ $ISA_INFO = 64 ]; then
			echo '_kobj_printf/Z '$SAVED_ADDR | mdb -kw > /dev/null 2>&1
		else
			echo '_kobj_printf/W '$SAVED_ADDR | mdb -kw  > /dev/null 2>&1
		fi

	mv /tmp/modules.$$ /tmp/backoutmods.$PatchNum
}


CheckZones()
{
        if [ "$ROOTDIR" = "/" -a -x /usr/bin/zonename ]; then
                ZONENAME=`/usr/bin/zonename`
                if [ ${ZONENAME} = "global" ]; then
                        GLOBAL_ZONE=true
                else
                        GLOBAL_ZONE=false
                fi
        else
                # Unable to determine zone
                GLOBAL_ZONE=true
        fi
}


fix_cputrak() {
	################################################################################
	# Removal of cputrack object is to circumvent the stringent check ( attribute
	# checking ) in the local zone environment.
	# ERROR: attribute verification of </usr/bin/cputrac> failed.
	################################################################################
	Quiet() {
	        typeset -r cmd_n_args=$*
		 eval "$cmd_n_args" > /dev/null 2>&1
	}
	
	typeset -r cpu_dir=$ROOTDIR/usr/bin
	typeset -r cpu_track=$cpu_dir/cputrack
	typeset -r cpu_pkg=SUNWcpcu
	
	Quiet removef -R $ROOTDIR $cpu_pkg $cpu_track
	Quiet removef -R $ROOTDIR -f $cpu_pkg
	
	# Destroy the hardlink regardless of the zone type (global/local(sparse, blank))
	/usr/bin/rm -f $cpu_track
	################################################################################
}

##
## Move kbtrans to the correct pkg if needed.
##
fix_kbtrans() {

	rmpkg=SUNWusb
	addpkg=SUNWckr
	kbtrans=/kernel/misc/amd64/kbtrans
	
	relocate_kbtrans() {
	
	
	        if [ ! -f $ROOTDIR/$kbtrans ] ; then
	                return
	        fi
	
		## This patch will add kbtrans to SUNWckr.  Manually remove it from SUNWusb
		## if it exists
		##
		Pgk=`pkgchk -R $ROOTDIR -l -p $kbtrans | grep $rmpkg`
		if [ "$?" =  "0" ] ; then
			removef -R $ROOTDIR $rmpkg $ROOTDIR/$kbtrans 1>/dev/null 2>&1
			removef -R $ROOTDIR -f $rmpkg
			installf -R $ROOTDIR $addpkg $ROOTDIR/$kbtrans
			installf -R $ROOTDIR -f $addpkg
			save_kbtrans_pkgmap
		fi
	}
	
   	relocate_kbtrans
	return 0
} 

##
## Save kbtrans entry
##
## Address 6343544 by using workaround 6312956 - removef doesn't update zones
## saved pkgmap files. We'll use cp -p to preserve permissions on the pkgmap
## file. Even though kbtrans may have moved from SUNWusb to SUNWckr, the
## pkgmap still needs to be cleaned up after the package switch is complete.
## 

save_kbtrans_pkgmap () {

	rmpkg=SUNWusb
	kbtrans_entry=kernel/misc/amd64/kbtrans
	mypatchid=${PatchNum}

	if [ ! -d $ROOTDIR/var/sadm/pkg/$rmpkg/save/pspool/$rmpkg ]; then
		return
	fi

	cd $ROOTDIR/var/sadm/pkg/$rmpkg/save/pspool/$rmpkg
	## Until removef/installf is fixed, we'll check if the entry is
	## still in the origin package's pkgmap

	if grep "$kbtrans_entry" pkgmap >/dev/null
	then
	    ## Save kbtrans entry in backoutpkgmap for postbackout restore
	    backoutpkgmap=/tmp/$rmpkg.pkgmap.$mypatchid
	    mkdir -p "`dirname $backoutpkgmap`"
	    cp -p pkgmap $backoutpkgmap
       	    grep "$kbtrans_entry" pkgmap > $backoutpkgmap

	    ## Remove kbtrans entry in present SUNWusb pkgmap to clean up
            cp -p pkgmap pkgmap.tmp
            grep -v "$kbtrans_entry" pkgmap > pkgmap.tmp
            mv pkgmap.tmp pkgmap

    	fi

}


##
## newboot support
## 
newboot () {


	BASEDIR=$ROOTDIR
	MEMSIZE_MIN=250
	MDPATCH=117435-02

	if [ -f "$BASEDIR/platform/i86pc/multiboot" ]; then
		return 0
	fi

	#
	# Check for sufficient system memory
	#
	memsize=`prtconf | grep "Memory size:" | cut -d' ' -f3`
	if [ "$memsize" -lt "$MEMSIZE_MIN" ]; then
		echo "Not enough memory for GRUB based boot process."
		exit 1
	fi

	#
	# Check for biosdev and make sure it works
	#
	if [ -x /sbin/biosdev ]; then
		/sbin/biosdev 2> /dev/null | grep "^0x80" > /dev/null
		if [ $? = 0 ]; then
			return 0
		fi
	fi

	#
	# biosdev failed. We can still apply the patch if the rootdisk
	# is the same as the bootdisk
	#
	bootdev=`prtconf -v /devices | sed -n '/boot-device/{n;p;}' | grep pci | cut -f 2 -d \'`
	rootdev=`df -k ${BASEDIR:-/} | nawk 'NR > 1 { print $1 }'`
	if [ -n "$bootdev" ] ; then
		ls -l $rootdev | grep $bootdev > /dev/null
		if [ $? = 0 ]; then
			return 0
		fi
	fi

	#
	echo ""
	echo "ERROR: One of the following may need to be corrected prior to installing this patch."
	echo "ERROR:"
	echo "ERROR: The boot device may be different from the root device on `hostname`."
	echo "ERROR: If you have booted from a floppy disk, eject the disk and reboot `hostname` prior"
	echo "ERROR: to installing this patch."
	echo ""
	echo "ERROR: You may have forgotten to reboot `hostname` AFTER installing $MDPATCH."
	echo "ERROR: This configuration requires that $MDPATCH be installed first and the"
	echo "ERROR: system must then be rebooted prior to installing this patch."
	echo ""
	exit 1
} 

ExecuteALLCmds () {

	newboot
	fix_kbtrans
	CheckForRequiredFiles
	load_mods
	update_logindevperm

	is_new_boot_installed

	return 0
}

LocalZones () {
# commands specific to non-gloabl zones
return 0

}

ExecuteInProperEnvironment () {

   ##  $PKGCOND is_path_writable $ROOTDIR/usr/bin && fix_cputrak
   /usr/bin/touch ${ROOTDIR}/usr/bin/.test.$$ > /dev/null 2>&1 && {
       fix_cputrak
       /usr/bin/rm /usr/bin/.test.$$ > /dev/null 2>&1
   }

   if $PKGCOND is_whole_root_nonglobal_zone > /dev/null 2>&1 ; then
       # Execute non-global whole root zone commands.
       # Should be same action as the default action.
	update_logindevperm
       return 0
   fi

   if $PKGCOND is_nonglobal_zone > /dev/null 2>&1 ; then
       # Execute non-global zone commands. Should be no action here
	update_logindevperm
       return 0
   fi

   if $PKGCOND is_netinstall_image > /dev/null 2>&1 ; then
       # Execute commands applicable to patching the mini-root.
       # There are usually no actions to take here since your patching
       # the mini-root on an install server.
       update_logindevperm	
       ExecuteALLCmds
       return 0
   fi

   if $PKGCOND is_mounted_miniroot > /dev/null 2>&1 ; then
       # Execute commands specific to the mini-root
       fix_kbtrans
       update_logindevperm 
       return 0
   fi

   if $PKGCOND is_diskless_client > /dev/null 2>&1 ; then
       # Execute commands specific to diskless client
       fix_kbtrans
       update_logindevperm 
       return 0
   fi

   if $PKGCOND is_alternative_root > /dev/null 2>&1 ; then
       # Execute commands specific to an alternate root
       ExecuteALLCmds
       return 0
   fi

   if $PKGCOND is_global_zone > /dev/null 2>&1 ; then
       # In a global zone and system is mounted on /.
       # Execute all commands.
       ExecuteALLCmds
       return 0
   fi

   return 1
} 

AlternateRoot()
# returns 0 if $PKG_INSTALL_ROOT -
{
	if [ -n "$ROOTDIR" ]; then
		if  [ "/" = "`echo $ROOTDIR |nawk '{gsub(/\/+/,"/"); print}'`" ]; then
			return 1
		else
			return 0
		fi
	else
		return 1
	fi
}

Check_miniroot()
{
	if [ ! -z "$PKGDBARG" ]; then
		echo $PKGDBARG | grep "^\-C" > /dev/null 2>&1
		if [ $? -eq 0 ] ; then
				if [ X"$PKG_NONABI_SYMLINKS" != X"true" ]; then
				return 1
			fi
		fi
	fi
	return 0
}

CheckLOFI()
{
	LOFITESTF1=/tmp/.lofitestf1.$$.$PatchNum
	LOFITESTF2=/tmp/.lofitestf2.$$.$PatchNum
	mkdir -p /tmp
	echo "1" >$LOFITESTF1
	echo "2" >$LOFITESTF2
	if  mount -F lofs $LOFITESTF1 $LOFITESTF2 >/dev/null 2>&1; then
		if [ X"`cat $LOFITESTF2`" = X"1" ]; then
			# well lofs mount does work
			_LOFI=0
		else
			#It does not work :(
			_LOFI=1
		fi
	else
		_LOFI=1
	fi
	umount $LOFITESTF2 >/dev/null 2>&1
	rm -f $LOFITESTF2 $LOFITESTF1
	return $_LOFI
}


zfs_zones_check()
{
	if [ ! -x "/usr/sbin/zoneadm" ]; then
		# zoneadm does not exist, so we have no problem here
		return 0
	fi
	zone_list=""
	
	#
	# Look at all zones for the "dataset" keyword.
	# We look at all installed zones.
	#
	for zone in $(zoneadm -R ${ROOTDIR:-/} list -i)
	do
		if [[ ${zone} == "global" ]]; then
			continue
		fi
		grep "<dataset name=" ${ROOTDIR}/${ZDIR}/${zone}.xml > \
		    /dev/null 2>&1
		if [ $? -eq 0 ]; then
			zone_list="${zone} ${zone_list}"
		fi
	done
	
	if [ -z ${zone_list} ]; then
		return 0
	else
		echo "The following zones contain datasets:"
		echo "\t${zone_list}"
		return 1
	fi
}

# Main

ZONENAME=global
[ -x /sbin/zonename ] && ZONENAME=`/sbin/zonename`

if AlternateRoot; then
	: # well no lofs mounts are needed
else
	if CheckLOFI; then
		: # we are ok to proceed
	else
		cat <<EOM
ERROR: lofs mount test failed. It can be caused by "exclude: lofs" entry in /etc/system.
       Please be sure that lofs mount is working before applying this patch.
EOM
		exit 1
	fi
fi

if Check_miniroot; then
	: # we are ok, PKG_NONABI_SYMLINKS is set up
else
	echo "ERROR: PKG_NONABI_SYMLINKS must be set to true for patchadd -C"
	exit 1
fi

{ pkgparam -R $ROOTDIR SUNWsndmu > /dev/null 2>&1 || pkgparam -R $ROOTDIR SUNWsndmr > /dev/null 2>&1; } \
&& {
	pkgparam -R $ROOTDIR SUNWopenssl-libraries > /dev/null 2>&1 \
	|| {
		cat <<EOM
Package SUNWopenssl-libraries must be installed before applying this patch.
This is required to satisfy a new dependency relationship packages SUNWsndmu and SUNWsndmr
have on package SUNWopenssl-libraries.
EOM
		exit 1
	}
}

##
## Disabled, because fix for CR# 6500872 is available now
##
## if zfs_zones_check; then
## 	:
## else
## 	cat <<EOM
## This patch cannot be installed without first performing some additional steps due to the
## presence of datasets in zones.  Please read the Special Install Instructions in the patch
## README for information on how to proceed.
## EOM
## 	exit 1
## fi
##


if [ -x "$PKGCOND" ] ; then
   ExecuteInProperEnvironment
else
   fix_cputrak
   CheckZones
   if [ "${GLOBAL_ZONE}" = "true" ]; then
        ExecuteALLCmds
   else
        LocalZones
   fi
fi 

exit 0
