#!/sbin/sh -
# %W% %G% %U% - %Q%
#ident "%Z%unixvm:%M% %I%"

# Copyright (c) 2000 VERITAS Software Corporation.  ALL RIGHTS RESERVED.
# UNPUBLISHED -- RIGHTS RESERVED UNDER THE COPYRIGHT
# LAWS OF THE UNITED STATES.  USE OF A COPYRIGHT NOTICE
# IS PRECAUTIONARY ONLY AND DOES NOT IMPLY PUBLICATION
# OR DISCLOSURE.
# 
# THIS SOFTWARE CONTAINS CONFIDENTIAL INFORMATION AND
# TRADE SECRETS OF VERITAS SOFTWARE.  USE, DISCLOSURE,
# OR REPRODUCTION IS PROHIBITED WITHOUT THE PRIOR
# EXPRESS WRITTEN PERMISSION OF VERITAS SOFTWARE.
# 
#               RESTRICTED RIGHTS LEGEND
# USE, DUPLICATION, OR DISCLOSURE BY THE GOVERNMENT IS
# SUBJECT TO RESTRICTIONS AS SET FORTH IN SUBPARAGRAPH
# (C) (1) (ii) OF THE RIGHTS IN TECHNICAL DATA AND
# COMPUTER SOFTWARE CLAUSE AT DFARS 252.227-7013.
#               VERITAS SOFTWARE
# 1600 PLYMOUTH STREET, MOUNTAIN VIEW, CA 94043


: ${VOLROOT_DIR:=$__VXVM_ROOT_DIR}
. ${VOL_SCRIPTS_LIB:-$VOLROOT_DIR/usr/lib/vxvm/lib}/vxcommon

usage=`egettxt "Usage: vxbootsetup [medianame ...]" vxvmshm:458`
progname=$0

exec 4>&2

# doit - execute a command, printing the command in verbose mode
doit() {
	[ "$verbose" ] && cat <<-% >&4
			! $*
			%
	"$@"
}

# isusedslice - determine if a slice is already used for the desired purpose
isusedslice()
{
	[ $# -lt 2 ] && return 1
        while read part tag flags offset size
        do
                case $part in
                '#'*)   continue;;
                esac
                [ "$part" -ne "$1" ] && continue
		[ "$tag" != "$2" ] && return 1
		[ $# -eq 2 ] && return 0
		[ $# -eq 4 ] && [ "$offset" -eq "$3" ] &&
			[ "$size" -eq "$4" ] && return 0
		return 1
        done
        return 1
}

isfreeslice()
{
        while read part tag flags offset size
        do
                case $part in
                '#'*)   continue;;
                esac
                if [ "$part" -eq $1 ]
                then
                        [ "$tag" = 0x0 -a "$size" -eq 0 ] && return 0
                        return 1
                fi
        done
        return 1
}

set_OS_variables
while getopts :v c
do
	case $c in
	v)	verbose=yes; v_opt=-v;;
	?)	echo "$usage" >&2; quit 1;;
	esac
done
shift `expr $OPTIND - 1`

eval `vxparms`
if [ -z "$V_ROOT" ]
then
	export progname; egettxt \
		"$progname: Unexpected output from vxparms" vxvmshm:55
	quit 1
fi

# search for file systems to create partitions for
root_vol=
swap_vol=
usr_vol=usr
var_vol=var
opt_vol=opt

# must find the root file system on a volume
# For Solaris, don't bother handling anything other than the root volume
# and swap.     
get_mountdevice / root root_dg
[ -z "$root_vol" ] && {
	egettxt >&2 \
	     'vxbootsetup: NOTE: Root file system is not defined on a volume.' \
	     vxvmshm:553
	quit 0
}
[ "X$root_dg" != Xrootdg ] && {
	egettxt >&2 \
    'vxbootsetup: ERROR: Volume for root file system not in rootdg disk group' \
	     vxvmshm:552
	quit 1
}

# We need to handle swap for dump devices. We need to create partitions on
# on a disk to which swap volume has been mirrored.

firstswap=
swapflag=

exec 3<&0 < /etc/vfstab
while read dev rdev mpoint fstype more
do
	case $fstype in
	swap)   if [ -z "$firstswap" ] && swapflag=yes
		then
		firstswap=yes
		case $dev in
		/dev/vx/dsk/rootdg/*)
		vol="`expr "\$dev" : '/dev/vx/dsk/rootdg/\(.*\)'`"
		;;
		/dev/vx/dsk/*)
		vol="`expr "\$dev" : '/dev/vx/dsk/\(.*\)'`"
		;;
		*)      continue;;
		esac
		fi
		;;
	*)      continue;;
	esac
	[ -n "$swapflag" ] && swap_vol=$vol
done
exec 0<&3 3<&-

if [ ! -z "$swap_vol" ]
then
volumelist="\"$root_vol\", \"$swap_vol\", \"$usr_vol\", \"$var_vol\", \"$opt_vol\""
else
volumelist="\"$root_vol\", \"$usr_vol\", \"$var_vol\", \"$opt_vol\""
fi

# if we are updating all boot disks, then keep a list of eeprom device
# aliases, so that we can remove those that we don't re-add

if [ $# -eq 0 ] && vxeeprom supported
then
	aliases="`vxeeprom -m devalias 'vx-[^	 ]*' | awk '{ print \$1 }'`"
fi

# look for all single-subdisk plexes associated with the root, swap
# and stand volumes.  Create partitions for each such subdisk.  If
# any disk names are given on the command line, limit the operation to
# that disk.

anyfail=
vxprint -g rootdg -s 2> /dev/null \
	-F "%usetype %vname %name %dmname %daname %len %pl_offset %devoffset" \
	-e "assoc.assoc in ($volumelist)" |
(
    while read usetype vol sd dmname daname len ploffset devoffset
    do
	rootdisksetup=
	if vxcheckda $daname > /dev/null 2> /dev/null
	then
		if not dogi_name_is_slice $daname
		then
			
			continue
		fi
		if not dogi_is_whole_slice $daname
		then
			continue
		fi
	else
		continue
	fi

	if [ $# -gt 0 ]
	then
		found=
		for arg in "$@"
		do
			[ "X$arg" = "X$dmname" ] && found=yes
		done
		[ -z "$found" ] && continue
	fi

	# If the subdisk is not addressable (i.e. not mapped into the
	# logical range of the volume length), ignore it.
	vlen=`vxprint -F"%len" $vol`
	if [ "$ploffset" -ge "$vlen" ]
	then
		vol=
		continue
	fi

	#
	# Ignore the ghost sub disk if any
	#
	if [ $len -eq $VOL_GHOST_LEN ]
	then
		vol=
		continue
	fi

	case $vol in

	$root_vol) [ "X$usetype" = Xroot ] || continue
		   tag=$V_ROOT;  slice=0;  flags=0x200; rootdisksetup=yes;;

	# additional types of volumes are currently never actually
	# handled in this script
	$swap_vol) [ "X$usetype" = Xswap ] || continue
		   tag=$V_SWAP;  slice=1;  flags=0x201;;
	$usr_vol)  
		   dogi_slice_rawpath $daname usrpath
		   $PRTVTOC -f $tmpfile1 $usrpath
		   usr_slice=
		   for i in 6 7 3 4 5
		   do
			if isusedslice $i $V_USR < $tmpfile1
			then
				usr_slice=$i
				break
			fi
		   done

		   [ -z "$usr_slice" ] && for i in 6 7 3 4 5
		   do
			if isfreeslice $i $V_USR < $tmpfile1
			then
				usr_slice=$i
				break
			fi
		   done
		   if [ -z "$usr_slice" ]
		   then
			export daname progname;  egettxt \
		     "$progname: No free partition found on device $daname for /usr." \
			vxvmshm:1020 $progname "$@" >&2
			continue
		   fi
		   tag=$V_USR;  slice=$usr_slice;  flags=0x200;;
	$var_vol)  
		  dogi_slice_rawpath $daname varpath
		  $PRTVTOC -f $tmpfile1 "$varpath"
		   var_slice=
                   for i in 6 7 3 4 5
                   do
                        if isusedslice $i $V_VAR < $tmpfile1
                        then
                                var_slice=$i
                                break
                        fi
                   done

                  [ -z "$var_slice" ] && for i in 6 7 3 4 5
                  do
                       if isfreeslice $i $V_VAR < $tmpfile1
                       then
                               var_slice=$i
                               break
                       fi
                  done
                  if [ -z "$var_slice" ]
                  then
			export daname progname; egettxt \
                     "$progname: No free partition found on device $daname for /var." \
                        vxvmshm:1021 $progname "$@" >&2
			continue
                   fi
                   tag=$V_VAR;  slice=$var_slice;  flags=0x200;;
	$opt_vol)
		  dogi_slice_rawpath $daname optpath
		  $PRTVTOC -f $tmpfile1 "$optpath"

		  pub_slice_offset=
		  exec 3<&0 < $tmpfile1
		  while read slice tag flags start size
		  do
			if [ "X$tag" = "X$VOL_PUB_SLICE_TAG" ]
			then
				pub_slice_offset=$start
			fi
		  done
		  exec <&3 3<&-

		  if [ -z "$pub_slice_offset" ]
		  then
		  	export daname;  egettxt \
				"vxmksdpart: Device $daname has no public region partition" \
				vxvmshm:978 >&2
			quit 1
		  fi

		  devoffset="`expr $devoffset + $pub_slice_offset`"

		  opt_slice=
		  for i in 5 4 3 7 6
		  do
	 		if isusedslice $i 0x0 $devoffset $len < $tmpfile1
			then
				opt_slice=$i
				break
			fi
		  done
		  if [ ! -z "$opt_slice" ]
		  then
			opt_slice=
			continue
		  fi

		  for i in 5 4 3 7 6
		  do
	 		if isfreeslice $i 0 $len < $tmpfile1
			then
				opt_slice=$i
				break
			fi
		  done
		  if [ -z "$opt_slice" ]
		  then
			export daname progname; egettxt \
		    "$progname: No free partition found on device $daname for /opt." \
				vxvmshm:1019 $progname "$@" >&2
			continue
		  fi
		  tag=0x0;  slice=$opt_slice;  flags=0x200;;

	*)	  continue;;
	esac

	doit vxmksdpart -f $v_opt -g rootdg "$sd" $slice $tag $flags
	[ $? -ne 0 ] && anyfail=yes
	[ -z "$anyfail" ] || quit 1
	[ -n "$rootdisksetup" ] || continue

	# Get needed path names for new root disk
	dogi_slice_to_device $daname device
	dogi_device_slice $device 0 newroot_slice
	dogi_slice_rawpath $newroot_slice newroot_rawpath
	dogi_slice_blkpath $newroot_slice newroot_blkpath

	# Set the path to the boot block
	bblkpath=/usr/lib/fs/ufs

	# Check to see if this is a Solaris 2.5 system, in which case 
	# we should use the bootblock in /usr/platform. Unfortunately,
	# uname doesn't have an 'i' option in pre-2.5, so this is a
	# bit convoluted.
	osrel=`uname -r`
	if [ "X$osrel" != "X5.4" -a "X$osrel" != "X5.3" ]
	then
		sysplat=`uname -i`
		if [ -f /usr/platform/${sysplat}/lib/fs/ufs/bootblk ]
		then
			bblkpath=/usr/platform/${sysplat}/lib/fs/ufs
		fi
	fi
			
	# installboot is different for x86
	if [ "X`uname -p`" = "Xi386" ]; then
		part_dev=`echo $newroot_rawpath | sed 's/s0/p0/g'`
		doit fdisk -n -b $bblkpath/mboot $part_dev

		newroot_rawpath_x86=`echo $newroot_rawpath | sed 's/s0/s2/g'`
		doit /usr/sbin/installboot $bblkpath/pboot \
			$bblkpath/bootblk $newroot_rawpath_x86
	else
		doit /usr/sbin/installboot $bblkpath/bootblk $newroot_rawpath
	fi

	# For systems that support setting their eeproms,
	# determine correct path independent of disk name spaces
	# and set up appropriate aliases.
	newroot_path=
	dogi_get_prompath $newroot_slice newroot_path
	if [ -n "$newroot_path" ] 
	then
		vxeeprom supported && {
			doit vxeeprom devalias "vx-$dmname" $newroot_path
		}

		# Add an alias for the new root volume
		cat <<-! | fgrep -v -x "vx-$dmname" > $tmpfile1
		$aliases
		!
		aliases="`cat $tmpfile1`"

	fi

	# Determine what root disk drivers need to be force loaded
	#
	# Case 1: non-SSA disk
	# --------------------
	# newroot_slice   = c0t0d0s0
	# newroot_blkpath = /dev/dsk/c0t0d0s0
	#
	# Case 2: SSA disk not under AP facility
	# --------------------------------------
	# newroot_slice   = c1t0d0s0
	# newroot_blkpath = /dev/dsk/c1t0d0s0
	#
	# Case 3: SSA disk under AP facility
	# ----------------------------------
	# newroot_slice   = mc1t0d0s0
	# newroot_blkpath = /dev/ap/dsk/mc1t0d0s0
	#
	# This is what "get_drv_list"/"forceload" functions will place
	# into /etc/system...
	#
	# +---------------------+----------------------------------------+
	# |                     |                $drv                    |
	# |                     +-----------+-------------+--------------+
	# |                     |   Case 1  |    Case 2   |     Case 3   |
	# +---------------------+-----------+-------------+--------------+
	# | forceload: drv/$drv |       sd  |       ssd   |      ap_dmd  |
	# |                     |      esp  |  SUNW,pln   |      pseudo  |
	# |                     |   espdma  |  SUNW,soc   |              |
	# |                     |     sbus  |      sbus   |              |
	# +---------------------+-----------+-------------+--------------+
	#
	# For Case 3 here's what should be loaded into /etc/system...
	#
	# +---------------------+----------------+
	# |                     |     $drv       |
	# |                     +----------------+
	# |                     |   Case 3       |
	# +---------------------+----------------+
	# | forceload: drv/$drv |      ssd       |
	# |                     | SUNW,pln       |
	# |                     | SUNW,soc       |
	# |                     |     sbus       |
	# |                     |   ap_dmd *     |
	# |                     |       ap       |
	# |                     |   pseudo *     |
	# +---------------------+----------------+
	#
	# These are all loaded by AP facility's "apboot" command and
	# because the Volume Manager does not allow a root metadisk to
	# be encapsulated without having run "apboot" with the root
	# metadisk specified, there is nothing that actually needs to
	# be loaded at this point for this case.
	#
	# To simplify things we allow the argument "$newroot_blkpath"
	# to be passed to the "get_drv_list" function for all cases. 
	# For case 3 this means that "get_drv_list" will only return
	# "ap_dmd" and "pseudo", but because /etc/system is already set
	# with the entire set of forceloads the ensuing call to the
	# function "forceload_drv" won't re-add them.  Hence, no harm
	# no foul for this case.
	#
	get_drv_list $newroot_blkpath > $tmpfile1
	if [ -s $tmpfile1 ] ; then
		cat $tmpfile1 | while read drv; do
			forceload_drv $drv
		done
	else
		egettxt "VxVM Can't determine root disk drivers"  vxvmshm:501
		quit 1
	fi
    done
    [ -n "$aliases" ] && doit vxeeprom devunalias $aliases
)

quit 0
