#!/bin/ksh

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

typeset -r ku_base_pid=118844-21

PATH="/usr/bin:/usr/sbin:${PATH}"; export PATH
PKGCOND=/usr/bin/pkgcond
NAWK="/bin/nawk"
DIRNAME="/bin/dirname"
PDO_REBOOT_LOC="/var/run/.solpatchsys/.pdo.patch-reboot-required.lock"
SataKU=118855-14

DONT_NEED_NEWBOOT_FILE="${ROOTDIR}/tmp/.we_dont_need_newboot"

is_pcfs_boot=yes
CMD=apply

BASEDIR=$ROOTDIR

## save kbtrans pkgmap 
mypatchid=${PatchNum}
rmpkg=SUNWusb
kb_pkgmap=$rmpkg.pkgmap.$mypatchid
if  test -s /tmp/$kb_pkgmap ; then
        mv /tmp/$kb_pkgmap $ROOTDIR/var/sadm/patch/$mypatchid/$kb_pkgmap
fi

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
}

function PatchInstalled {
	patchadd -p -R $ROOTDIR | sed 's/Obs.*//' | grep $1 2>&1 > /dev/null

}

function MdFixDevids {

	MD_CONF=$ROOTDIR/kernel/drv/md.conf

	#
	# Modify the md.conf file to have SVM resynch the devids of
	# volume members
	#
	if [ -f $MD_CONF ] && \
		! /bin/grep 'md_devid_destroy[ 	]*=[ 	]*1[ 	]*;' $MD_CONF \
		    > /dev/null 2>&1; then

		#
		# The string "# Begin MDD database info (do not edit)" will be
		# present in md.conf VERBATIM if there are SVM volumes present.
		# the string will always be the same, since it's put there by
		# code, not by humans.
		#
		/bin/sed -e \
		    '/# Begin MDD database info (do not edit)/ a\
md_devid_destroy=1;' $MD_CONF > /tmp/md.conf.$$
		/bin/mv /tmp/md.conf.$$ $MD_CONF
		/bin/chmod 644 $MD_CONF
		/bin/chown root:sys $MD_CONF
	fi
}

function SataSupport {

	#
	# 
	#
	PATCHID=$PatchNum
	PATCHEXEC=$1
	PATCHROOT=$1
	
	ZPOOL_CACHE=$ROOTDIR/etc/zfs/zpool.cache
	FBTEMPLATE=$PATCHROOT/fbtmplmeth.ksh
	FBSVCMETH=patch-${PATCHID}-fb
	ROOT_FBSVCMETH=$ROOTDIR/lib/svc/method/$FBSVCMETH
	FBSVCTMPL=$PATCHROOT/fbtmplsvc.xml
	# The service name (from the <service> tag in fbtmplsvc.xml):
	FBSVCNAME=system/patch-${PATCHID}-fb
	FBSVCINSTANCE=${FBSVCNAME}:default
	
	function patchfunc {
		/bin/echo 'export SYSV3=	# for echo -n'
		/bin/echo '/bin/echo "Patch '$PATCHID' requires some additional operations:" > /dev/console'
		/bin/echo '/bin/echo "Please wait while ZFS pools are reimported:" > /dev/console'
		while read nextline; do
			VOLNAME=`/bin/echo $nextline | /bin/awk '{print $NF}'`
			/bin/echo '/bin/echo -n "Reimporting ZFS pool: '$VOLNAME'..." > /dev/console'
			/bin/echo 'if ! /sbin/'${nextline}' > /dev/null 2>&1; then'
			/bin/echo '    /bin/echo "failed." > /dev/console'
			/bin/echo "    /bin/echo '******* WARNING *******'"
			/bin/echo "    /bin/echo \"zpool import of pool \\\`"$VOLNAME"' failed.\""
			/bin/echo "    /bin/echo '******* WARNING *******'"
			/bin/echo "else"
			/bin/echo '/bin/echo "done." > /dev/console'
			/bin/echo "fi"
		done
		/bin/echo 'unset SYSV3'
	}
	
	#
	# No ZFS pools?  No need for the smf service.
	#
	if [ ! -f $ZPOOL_CACHE ]; then
    		return 0;
	fi
	
	#
	# Look for required files;  if any are missing, bail out with an error.
	#
	if [ ! -x $PATCHEXEC/zfs_cache ]; then
    		echo ERROR: Missing zfs_cache command.
    		exit 1;
	fi
	if [ ! -f $FBTEMPLATE ]; then
    		echo ERROR: Missing reboot service method template
    		exit 1;
	fi
	if [ ! -f $FBSVCTMPL ]; then
    		echo ERROR: Missing reboot service XML manifest
    		exit 1;
	fi

	#
	# If patching the live system, no repository directive is needed.
	# (and supplying one in that case can cause trouble)
	#
	if [[ "$ROOTDIR" == "/" ]] || [[ -z "$ROOTDIR" ]]; then
		REPOSITORY=
	else
		REPOSITORY="repository $ROOTDIR/etc/svc/repository.db"
	fi

	#
	# Build the list of script commands we need to add to the template
	#
	$PATCHEXEC/zfs_cache -R $ROOTDIR | patchfunc > /tmp/zfscmds.$$
	if ! /bin/mv $ZPOOL_CACHE $PATCHROOT/zpool.cache.patch-$PATCHID \
	     > /dev/null 2>&1 ; then
		#
		# If we can't move it, we must delete it
		#
		rm -f $ZPOOL_CACHE
	fi

	#
	# Modify the smf method template with the list of commands we just
	# generated.  These commands include zpool import and progress
	# display (output to /dev/console) support code.
	#
	/bin/sed -e "/"'%%ZFSCMDS%%'"/r /tmp/zfscmds.$$" \
		 -e "s/"'%%PATCHID%%'"/$PATCHID/g"		$FBTEMPLATE | \
	    /bin/grep -v '%%ZFSCMDS%%' > $ROOT_FBSVCMETH
	/bin/chmod 555 $ROOT_FBSVCMETH
	/bin/chown root:sys $ROOT_FBSVCMETH
	/bin/rm -f /tmp/zfscmds.$$

	#
	# Put the service manifest in /var/svc/manifest so it can be imported   
	# on next reboot.  The run-once service will run soon after.
	# 
	/bin/sed "s/"'%%PATCHID%%'"/$PATCHID/g" \
	    $FBSVCTMPL > $ROOTDIR/var/svc/manifest/system/patch-${PATCHID}.xml

}










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
}

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
}

LocalZones () {

  # Only for FCS
  return 0
} 


#
# Detect SVM root and return the list of raw devices under the mirror
#
get_rootdev_list()
{
	metadev=`grep -v "^#" $BASEDIR/etc/vfstab | \
		grep "[	 ]/[ 	]" | nawk '{print $2}'`
	if [[ $metadev = /dev/rdsk/* ]]; then
		rootdevlist=`echo "$metadev" | sed -e "s#/dev/rdsk/##"`
	elif [[ $metadev = /dev/md/rdsk/* ]]; then
		metavol=`echo "$metadev" | sed -e "s#/dev/md/rdsk/##"`
		rootdevlist=`metastat -p $metavol |\
		    grep -v "^$metavol[	 ]" | nawk '{print $4}'`
	fi
	for rootdev in $rootdevlist
	do
		echo /dev/rdsk/$rootdev
	done
}

#
# multiboot: install grub on the boot slice
#
install_grub()
{
	# Stage 2 blocks must remain untouched
	STAGE1=$BASEDIR/boot/grub/stage1
	STAGE2=$BASEDIR/boot/grub/stage2

	if [ $is_pcfs_boot = yes ]; then
		#
		# Note: /stubboot/boot/grub/stage2 must stay untouched.
		#
		mkdir -p $BASEDIR/stubboot/boot/grub
                cd  $BASEDIR/boot/grub ; echo menu.lst | cpio -pum $BASEDIR/stubboot/boot/grub
		bootdev=`grep -v "^#" $BASEDIR/etc/vfstab | grep pcfs | \
			grep "[	 ]/stubboot[ 	]" | nawk '{print $1}'`
		rpcfsdev=`echo "$bootdev" | sed -e "s/dev\/dsk/dev\/rdsk/"`
		if [ X"$rpcfsdev" != X ]; then
			print "Installing grub on $rpcfsdev"
			$BASEDIR/sbin/installgrub $STAGE1 $STAGE2 $rpcfsdev
		fi
	fi

	get_rootdev_list | while read rootdev
	do
		if [ X"$rpcfsdev" != X ]; then
			echo "create GRUB menu in $BASEDIR/stubboot"
			$BASEDIR/sbin/bootadm update-menu -R $BASEDIR/stubboot \
				-o $rootdev,$BASEDIR
		else
			echo "Creating GRUB menu in $BASEDIR"
			$BASEDIR/sbin/bootadm update-menu -R ${BASEDIR:-/} -o $rootdev
		fi
		print "Installing grub on $rootdev"
		$BASEDIR/sbin/installgrub $STAGE1 $STAGE2 $rootdev
	done
}

restore_boot()
{
	if [ -z $rpcfsdev ]; then
		get_rootdev_list | while read rootdev
		do
			print "Restoring pboot on $rootdev"
			dd if=$BASEDIR/usr/platform/i86pc/lib/fs/ufs/pboot \
				of=${rootdev%??}s2 bs=512 count=1 > /dev/null 2>&1
		done
		return
	fi

	# handle pcfs boot partition
	num=1
	fdisk -W - ${rpcfsdev%:boot}  | grep -v '^*' | grep -v '^$' | \
	while read id act bhead bcyl ehead ecyl rsect numsect
	do
		# Ignore entry if not X86 /boot partition
		# ID '190' is the X86BOOT partition (see man fdisk(1M))
		if [ $id -ne "190" ] ; then
			num=$(expr $num + 1)
			continue
		fi

		# Found X86 boot partition - save contents
		BOOTPART=${rpcfsdev%p0:boot}p${num}

		echo "Restoring boot sector on pcfs device <${BOOTPART}>"
		dd if=$BASEDIR/boot/mdboot.patch.$PatchNum of=$BOOTPART bs=512 count=1 > /dev/null 2>&1
	done
}

#
# move pcfs from /stubboot to /boot, transfer contents accordingly
#
pcfs_grub_to_dca()
{
	bootdev=`grep -v "^#" $BASEDIR/etc/vfstab | grep pcfs \
		| grep "[ 	]/stubboot[	 ]" | nawk '{print $1}'`
	if [ X"$bootdev" = "X" ]; then
		is_pcfs_boot=no
		return		# nothing to do
	fi

	# move /stubboot to /boot
	rpcfsdev=`echo "$bootdev" | sed -e "s/dev\/dsk/dev\/rdsk/"`

	df -l | grep /stubboot >/dev/null 2>&1
	if [ $? != 0 ]; then
		mount -F pcfs $bootdev $BASEDIR/stubboot
	fi

	# copy everything from /boot to /stubboot
	( cd $BASEDIR/boot
	find . | cpio -pdum $BASEDIR/stubboot > /dev/null 2>&1
	rm -fr $BASEDIR/boot/* )

	echo "Unmounting $bootdev at $BASEDIR/stubboot"
	ERRMSG=$(umount $bootdev 2>&1)
	if [ $? -ne 0 ] ; then
		[ -n "${ERRMSG}" ] && echo "${ERRMSG}"
	fi

	# adjust /etc/vfstab
	cp $BASEDIR/etc/vfstab $BASEDIR/etc/vfstab-
	sed -e 	"s/[ 	]\/stubboot[ 	]/	\/boot	/" \
	    -e 	'/p0:boot/ {
			s/[	 ]yes[	 ]/	no	/
			}' \
	    <$BASEDIR/etc/vfstab- >$BASEDIR/etc/vfstab

	## shudong - the above sed may need some more entries?

	echo "Mounting $bootdev at $BASEDIR/boot"
	ERRMSG=$(mount -F pcfs $bootdev $BASEDIR/boot 2>&1)
	if [ $? -ne 0 ] ; then
		[ -n "${ERRMSG}" ] && echo "${ERRMSG}"
	fi
}

fix_bootpath()
{
	BOOTENVRC=$BASEDIR/boot/solaris/bootenv.rc

	grep "^setprop[	 ]\{1,\}bootpath" $BOOTENVRC > /dev/null
	if [ $? = 0 ]; then
		return
	fi

	rootdev=`grep -v "^#" $BASEDIR/etc/vfstab | \
	    grep "[	 ]\{1,\}/[	 ]\{1,\}" | nawk '{print $1}'`
	bootpath=`/bin/ls -l $BASEDIR/$rootdev | nawk '{ print $11 }' |\
	    sed -e 's#[./]*/devices/#/#'`
	echo "setprop bootpath $bootpath" >> $BOOTENVRC
}

pcfs_dca_to_grub()
{

	bootdev=`grep -v "^#" $BASEDIR/etc/vfstab | grep pcfs \
		| grep "[ 	]/boot[	 ]" | nawk '{print $1}'`
	if [ X"$bootdev" = X ]; then
		is_pcfs_boot=no
		return		# nothing to do
	fi

	# move /boot to /stubboot
	rpcfsdev=`echo "$bootdev" | sed -e "s/dev\/dsk/dev\/rdsk/"`

	# Remount boot partition as /stubboot, set up new /boot
	mkdir -p $BASEDIR/stubboot
	echo "Unmounting $bootdev at $BASEDIR/boot"
	ERRMSG=$(umount $bootdev 2>&1)
	if [ $? -ne 0 ] ; then
		[ -n "${ERRMSG}" ] && echo "${ERRMSG}"
		echo "Unable to umount $bootdev."
		exit 1
	fi
	ERRMSG=$(mount -F pcfs $bootdev $BASEDIR/stubboot 2>&1)
	if [ $? -ne 0 ] ; then
		[ -n "${ERRMSG}" ] && echo "${ERRMSG}"
		echo "Unable to mount $bootdev on $BASEDIR/stubboot."
		exit 1
	fi

	# copy contents from /stubboot to /boot
	( cd $BASEDIR/stubboot
	find . | cpio -pdum $BASEDIR/boot > /dev/null 2>&1 )

	# adjust /etc/vfstab
	cp $BASEDIR/etc/vfstab $BASEDIR/etc/vfstab-
	sed -e "s/[ 	]\/boot[ 	]/	\/stubboot	/"  \
	    -e	'/p0:boot/ {
			s/[	 ]no/	yes/
			}
		' <$BASEDIR/etc/vfstab- >$BASEDIR/etc/vfstab

	# save boot sector in mdboot.patch.$PatchNum
	num=1
	fdisk -W - ${rpcfsdev%:boot}  | grep -v '^*' | grep -v '^$' | \
	while read id act bhead bcyl ehead ecyl rsect numsect
	do
		# Ignore entry if not X86 /boot partition
		# ID '190' is the X86BOOT partition (see man fdisk(1M))
		if [ $id -ne "190" ] ; then
			num=$(expr $num + 1)
			continue
		fi

		# Found X86 boot partition - save contents 
		BOOTPART=${rpcfsdev%p0:boot}p${num}

		echo "Saving boot sector from ${BOOTPART} to mdboot.patch.$PatchNum"
		dd of=$BASEDIR/stubboot/mdboot.patch.$PatchNum if=$BOOTPART bs=512 count=1 > /dev/null 2>&1
	done
}

to_new_boot()
{
	# Some dca sata only systems were installed without a bootpath.
	# They need to be fixed in order for new-boot to work with them.
	fix_bootpath

	# fix pcfs file system
	pcfs_dca_to_grub

	# install grub boot block and create grub menu
	install_grub

	# create boot archive
	$BASEDIR/boot/solaris/bin/create_ramdisk -R /$BASEDIR
}

to_old_boot()
{

	rm $BASEDIR/platform/i86pc/boot_archive

	# fix pcfs file system
	pcfs_grub_to_dca

	# restore pboot
	restore_boot

	# on first reboot, boot-archive is still active
	echo "exit 0" > $BASEDIR/lib/svc/method/boot-archive
	chmod +x $BASEDIR/lib/svc/method/boot-archive
}

fix_cputrak () {

	##
	## fix cputrack links
	##
	
	RT_DIR=
	[ "$ROOTDIR" = '/' ] && RT_DIR=$ROOTDIR || RT_DIR="$ROOTDIR/"
	
	typeset -r cpu_pkg=SUNWcpcu
	typeset -r cpu_dir=$ROOTDIR/usr/bin
	typeset -r cpu_track=$cpu_dir/cputrack
	
	################################################################################
	# If the $ROOTDIR/usr/bin/cputrack file exists, then the links have been
	# established in the "global" zone already.
	################################################################################
	
	Quiet() {
	        typeset -r cmd_n_args=$*
		 eval "$cmd_n_args" > /dev/null 2>&1
	}
	
	Quiet removef -R $ROOTDIR $cpu_pkg ${RT_DIR}usr/bin/cputrack
	Quiet removef -R $ROOTDIR -f $cpu_pkg
	
	Quiet installf -R $ROOTDIR $cpu_pkg ${RT_DIR}usr/bin/cputrack=../lib/isaexec
	Quiet installf -R $ROOTDIR -f $cpu_pkg
	
	cd $ROOTDIR/usr/bin
	/usr/bin/rm -f cputrack
	
	[ ! -e "$cpu_track" ] && ln ../lib/isaexec cputrack
}

newboot() {	

	## newboot runs only in global zones or on systems with no zones configured.

	grep -v "^[ 	]*#" $BASEDIR/etc/vfstab | grep "[ 	]/[ 	]*nfs[ 	]" > /dev/null 2>&1
	if [ $? -eq 0 ] ; then
        	# Check for diskless, alternate root case.
        	return 0
	fi

	case $CMD in
		apply)
			## apply to system only one time
			if [ ! -f "${DONT_NEED_NEWBOOT_FILE}" ] ; then
				to_new_boot
			fi
			rm -f "${DONT_NEED_NEWBOOT_FILE}"
			;;
		backout)
			to_old_boot
			;;
	esac
}

lu_warn() {
	lu_pid=121431-02                ## x86 patch id
## 	if [ "$MACH" = "sparc" ] ; then 
##         	lu_pid=121431-02        ## sparc patch id
## 	fi
	
	if [ -d $ROOTDIR/var/sadm/pkg/SUNWluu ] ; then 
        ## lu is installed
        	if last_patch $lu_pid ; then
                	## latest lu patches are not installed, danger will robinson
                	echo
                	echo "You should install $lu_pid or higher."
                	echo "This action is strongly advised."
                	echo
        	fi
	fi

}


ExecuteALLCmds () {

        newboot
        lu_warn
        [ -f /tmp/backoutmods.$PatchNum ] && {
           mv /tmp/backoutmods.$PatchNum $ROOTDIR/var/sadm/patch/$PatchNum/backoutmods
        }

	#
	# If SataKU patch is installed and this is the first KU patch 
	# to be added on top of it, then we must perform the following...
	#

	if PatchInstalled $SataKU ; then
		last_patch $SataKU
		status=$?
		if [ $status -eq 2 ] ; then
			#
			# cp scripts and templates to patch save area.  Used by both patchadd and patchrm
			#
			patch_tools=$ROOTDIR/var/sadm/patch/$PatchNum
			cp $patchdir/fbtmp* $patchdir/zfs_cache $patch_tools
			MdFixDevids
			SataSupport $patch_tools
			/usr/sbin/bootadm update-archive -R $ROOTDIR
		fi
	fi

        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.
       return 0
   fi

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

   if $PKGCOND is_netinstall_image > /dev/null 2>&1 ; then
       # Execute commands applicable to patching the mini-root.
       ExecuteALLCmds
       return 0
   fi

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

   if $PKGCOND is_diskless_client > /dev/null 2>&1 ; then
       # Execute commands specific to diskless client
       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 /.
       ExecuteALLCmds
       return 0
   fi
   return 1
} 

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

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

##
## Disable pdo, so after this patch installation reboot *MUST* happen
##
if AlternateRoot; then
	:
else
	cat > /var/run/nopatch <<EOF
#!/sbin/sh
echo "You must perform a reconfigure reboot before invoking any additional patch commands." >&2
exit 1
EOF
	chmod +x /var/run/nopatch
	mount -O -F lofs /var/run/nopatch /usr/lib/patch/pdo
fi

exit 0
