#!/bin/ksh -p
#
# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# ident	"@(#)s9_p2v.ksh	1.1	08/05/06 SMI"
#

# usage: p2v [-u] {zonename}
#
# NOTE: this script runs in the global zone but the modules it executes are
# run inside the zone that is being p2v-ed.

verbose_out=">/dev/null"
LOGFILE=
MSG_PREFIX="p2v: "

usage()
{
	echo "$0 [-a] [-s] [-m msgprefix] [-u] [-v] zonename" >&2
	exit 1
}

# Log passed arguments to file descriptor 2
error()
{
        typeset fmt="$1"
        shift

        printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@"
        [[ -n $LOGFILE ]] && \
	    printf "[`date`] ${MSG_PREFIX}ERROR: ${fmt}\n" "$@" >&2
}

# Print and log provided text if the shell variable "verbose_mode" is set
verbose()
{
        typeset fmt="$1"
        shift

        [[ -n $OPT_V ]] && printf "${MSG_PREFIX}${fmt}\n" "$@"
        [[ -n $LOGFILE ]] && printf "[`date`] ${MSG_PREFIX}${fmt}\n" "$@" >&2
}

# Clean up on interrupt
trap_cleanup()
{
	msg=$(gettext "Postprocessing cancelled due to interrupt.")

	error "$msg"
	if [[ $zone_is_running -ne 0 ]]; then
		msg=$(gettext "Shutting down zone $ZONENAME...")
		error "$msg"
		zoneadm -z $ZONENAME halt
	fi

	exit 1
}

# Only make a copy if we haven't already done so.
safe_backup()
{
	typeset src="$1"
	typeset dst="$2"

	if [ ! -h $src -a ! -h $dst -a ! -d $dst -a ! -f $dst ] ; then
		cp -p $src $dst || exit 1
	fi
}

# Make a copy even if the destination already exists.
safe_copy()
{
	typeset src="$1"
	typeset dst="$2"

	if [ ! -h $src -a ! -h $dst -a ! -d $dst ] ; then
		cp -p $src $dst || exit 1
	fi
}

libc_psr_link()
{
	PSR32=$ZONEROOT/usr/platform/$PLAT/lib/libc_psr.so.1
	PSR64=$ZONEROOT/usr/platform/$PLAT/lib/sparcv9/libc_psr.so.1

	# first look for $HWCAP specific
	LIBC_MOE_32=`/usr/bin/moe -32 /platform/$PLAT/lib/libc_psr/'$HWCAP'`
	if [ -n "$LIBC_MOE_32" ]; then
		cp -p $LIBC_MOE_32 $PSR32 || exit 1
	elif [ -h /platform/$PLAT/lib/libc_psr.so.1 -o \
	    -f /platform/$PLAT/lib/libc_psr.so.1 ]; then
		# use $PLAT specific
		cp -p /platform/$PLAT/lib/libc_psr.so.1 $PSR32 || exit 1
	fi

	# Only set up sparcv9 libc_psr if sparcv9 is installed in the zone.
	if [ ! -d $ZONEROOT/usr/lib/sparcv9 ]; then
		return
	fi

	LIBC_MOE_64=`/usr/bin/moe -64 \
	    /platform/$PLAT/lib/sparcv9/libc_psr/'$HWCAP'`
	if [ -n "$LIBC_MOE_64" ]; then
		cp -p $LIBC_MOE_64 $PSR64 || exit 1
	elif [ -h /platform/$PLAT/lib/sparcv9/libc_psr.so.1 -o \
	    -f /platform/$PLAT/lib/sparcv9/libc_psr.so.1 ]; then
		cp -p /platform/$PLAT/lib/sparcv9/libc_psr.so.1 $PSR64 || exit 1
	fi
}

unset LD_LIBRARY_PATH
PATH=/usr/sbin:/usr/bin
export PATH

# Setup i18n output
TEXTDOMAIN="SUNW_OST_OSCMD"
export TEXTDOMAIN

#
# ^C Should cleanup; if the zone is running, it should try to halt it.
#
zone_is_running=0
trap trap_cleanup INT

#
# Parse the command line options.
#
# NOTE: If you add an option here, make sure to consider the implications
# to the OPT_A logic below.
#
OPT_U=
OPT_V=
OPT_M=
OPT_A=
OPT_L=
while getopts "auvm:l:" opt
do
	case "$opt" in
		a)	OPT_A="-a";;
		u)	OPT_U="-u";;
		v)	OPT_V="-v"; verbose_out="";;
		m)	MSG_PREFIX="$OPTARG"; OPT_M="-m \"$OPTARG\"";;
		l)	LOGFILE="$OPTARG"; OPT_L="-l \"$OPTARG\"";;
		*)	usage;;
	esac
done
shift OPTIND-1

if [[ -n $OPT_A ]]; then
        for i in \
            `zoneadm list -ip | nawk -F: '{if ($6 == "solaris9") print $2}'`
        do
                logfile="/var/tmp/$i.p2v.$$.log"
		touch $logfile

                echo "Updating zone $i (log: $logfile)...\c"
                /usr/lib/brand/solaris9/s9_p2v $OPT_U $OPT_V -m "$MSG_PREFIX" \
		    -l $logfile $i
                res=$?
                echo "done"
                if [ $res -ne 0 ]; then
                        echo "Error updating zone $i, see $logfile."
                fi
        done
	exit 0
fi

if [ $# -lt 1 ]; then
	usage
fi
if [ $# -gt 1 ]; then
	usage
fi

if [[ -n $LOGFILE ]]; then
	exec 2>>$LOGFILE
fi

ZONENAME=$1
export ZONENAME

v_gathering=$(gettext "Gathering information about zone %s")
e_badinfo=$(gettext "Failed to get '%s' zone resource")
v_mkdirs=$(gettext "Creating mount points")
v_libc_psr=$(gettext "Linking in appropriate libc_psr.so for this platform")
v_etc_system=$(gettext "Processing /etc/system")
v_booting=$(gettext "Booting zone to single user mode")
e_badboot=$(gettext "Zone boot failed")
v_module=$(gettext "Applying p2v module %s")
e_module=$(gettext "p2v module %s failed: %s")
v_unconfig=$(gettext "Performing zone sys-unconfig")
e_unconfig=$(gettext "sys-unconfig failed")
v_halting=$(gettext "Halting zone")
e_badhalt=$(gettext "Zone halt failed")
v_exitgood=$(gettext "Postprocessing successful.")
e_exitfail=$(gettext "Postprocessing failed.")

verbose "$v_gathering" "$ZONENAME"
ZONEPATH=`zoneadm -z $ZONENAME list -p  | nawk -F: '{print $4}'`
if [ $? -ne 0 ]; then
        error "$e_badinfo" "zonename"
        error "$e_exitfail"
        exit 1;
fi

ZONEROOT=$ZONEPATH/root

#
# Keep track of the current host so we can later determine if the zone has
# been migrated to a new system.
#
if [ ! -h $ZONEROOT/.host.orig ]; then
	/usr/bin/hostid >$ZONEROOT/.host.orig || exit 1
fi

# Collect some additional info about the zone.
STACK_TYPE=`zoneadm -z $ZONENAME list -p  | nawk -F: '{print $7}'`
if [ $? -ne 0 ]; then
        error "$e_badinfo" "stacktype"
        error "$e_exitfail"
        exit 1;
fi

NET=`LC_ALL=C zonecfg -z $ZONENAME info net`
if [ $? -ne 0 ]; then
        error "$e_badinfo" "net"
        error "$e_exitfail"
        exit 1;
else
	NETIF=`echo $NET | nawk '{
		for (i = 1; i < NF; i++) {
			if ($i == "physical:") {
				if (length(net) == 0) {
					i++
					net = $i
				} else {
					multiple=1
				}
			}
		}
	}
	END {	if (!multiple)
			print net
	}'`
fi

# Now do the work to update the zone.

# Before booting the zone we may need to create a few mnt points, just in
# case they don't exist for some reason.
#
# Whenever we reach into the zone while running in the global zone we
# need to validate that none of the interim directories are symlinks
# that could cause us to inadvertently modify the global zone.
verbose "$v_mkdirs"
if [ ! -f $ZONEROOT/tmp -a ! -d $ZONEROOT/tmp ]; then
	mkdir -p $ZONEROOT/tmp
	chmod 1777 $ZONEROOT/tmp
fi
if [ ! -h $ZONEROOT/etc -a ! -f $ZONEROOT/etc/mnttab ]; then
	touch $ZONEROOT/etc/mnttab
	chmod 444 $ZONEROOT/etc/mnttab
fi
if [ ! -f $ZONEROOT/proc -a ! -d $ZONEROOT/proc ]; then
	mkdir -p $ZONEROOT/proc
	chmod 755 $ZONEROOT/proc
fi
if [ ! -f $ZONEROOT/dev -a ! -d $ZONEROOT/dev ]; then
	mkdir -p $ZONEROOT/dev
	chmod 755 $ZONEROOT/proc
fi
if [ ! -h $ZONEROOT/etc -a ! -h $ZONEROOT/etc/svc -a ! -d $ZONEROOT/etc/svc ];
then
	mkdir -p $ZONEROOT/etc/svc/volatile
	chmod -R 755 $ZONEROOT/etc/svc
fi

# Check if there is a missing platform specific lib for this platform that we
# should use in the zone.
if [ ! -h $ZONEROOT/usr -a -d $ZONEROOT/usr -a \
    ! -h $ZONEROOT/usr/platform -a -d $ZONEROOT/usr/platform ]; then

	PLAT=`/usr/bin/uname -i`
	if [ ! -h $ZONEROOT/usr/platform/$PLAT -a \
            ! -d $ZONEROOT/usr/platform/$PLAT ]; then
		# First make the $PLAT, $PLAT/lib and $PLAT/lib/sparcv9 dirs.

		verbose "$v_libc_psr"

		#
		# We don't make sparcv9 if there are no other sparcv9 libs
		# installed in the zone.
		#
		if [ -d $ZONEROOT/usr/lib/sparcv9 ]; then
			mkdir -m 755 -p $ZONEROOT/usr/platform/$PLAT/lib/sparcv9
		else
			mkdir -m 755 -p $ZONEROOT/usr/platform/$PLAT/lib
		fi
		chown root:sys $ZONEROOT/usr/platform/$PLAT
		chown -R root:bin $ZONEROOT/usr/platform/$PLAT/lib

		# find and link optimal libc_psr 
		libc_psr_link
        fi
fi

# Process the /etc/system file.
if [ ! -h $ZONEROOT/etc -a -d $ZONEROOT/etc ]; then
	verbose "$v_etc_system"
	safe_backup $ZONEROOT/etc/system $ZONEROOT/etc/system.pre_p2v
	/usr/lib/brand/solaris9/s9_system < $ZONEROOT/etc/system \
	    >/tmp/system.$$ || exit 1
	safe_copy /tmp/system.$$ $ZONEROOT/etc/system || exit 1
	rm -f /tmp/system.$$

	if [ ! -h $ZONEROOT/.etc_system.timestamp ]; then
		touch $ZONEROOT/.etc_system.timestamp || exit 1
	fi
fi

verbose "$v_booting"

#
# Boot the zone so that we can run p2v modules inside of it.  Use the -f
# flag since we're running this from inside the install process (the
# zone is still in the incomplete state).
#
# We use the old 'boot -f -s' syntax to work around an arg parsing bug in S10.
#
# Note that checking the exit status here is fairly pointless since we boot
# with -f so we bypass all of the error checking.  It is possible for the
# the zone to boot but not really be running anything useful.
#
zone_is_running=1
zoneadm -z $ZONENAME boot -f -s
if [ $? -ne 0 ]; then
        error "$e_badboot"
        error "$e_exitfail"
        exit 1;
fi

#
# As part of the p2v module interface the following environment variables
# are set in the environment for each module:
#	FILEDIR (path to brand-specific file repository)
#	ZONE_STACK_TYPE
#	ZONE_NETIF
# Additional variables may be added as needed.
#
failed=
MODDIR=/usr/lib/brand/solaris9/mods
for m in `/usr/bin/ls $MODDIR`; do

	verbose "$v_module" "$m"

	# We may need to manage stdout vs. stderr for modules
	# at some point.
	zlogin -S $ZONENAME " \
		FILEDIR=/.SUNWnative/usr/lib/brand/solaris9/files; \
		export FILEDIR; \
		ZONE_STACK_TYPE=$STACK_TYPE; \
		export ZONE_STACK_TYPE; \
		ZONE_NETIF=$NETIF; \
		export ZONE_NETIF; \
		/.SUNWnative/$MODDIR/$m" $verbose_out </dev/null 

	if [[ $? -ne 0 ]]; then
		error "$e_module" "$m" "$?"
		failed=1
		break
	fi
done

#
# Since the zone might have booted but the modules might not
# be installed we do an additional check here to verify that the modules
# actually ran within the zone.  This depends on the behavior of the
# S40_setup_preload module creating the symlink.
#
if [ ! -h $ZONEROOT/usr/lib/secure/s9_preload.so.1 ]; then
        error "$e_badboot"
        error "$e_exitfail"
	zoneadm -z $ZONENAME halt
	exit 1
fi

if [[ -z $failed && -n $OPT_U ]]; then
	verbose "$v_unconfig"

	#
	# The sys-unconfig can hang if the zone is still in the process of
	# booting when we try to run sys-unconfig.  Wait until the startup
	# rc scripts are done, which we do by checking for sulogin, or waiting
	# 20 seconds, whichever comes first.
	#
	for i in 0 1 2 3 4 5 6 7 8 9
	do
		pgrep -z $ZONENAME sulogin >/dev/null 2>&1 && break
		sleep 2
	done

	if [ $i -eq 9 ]; then
		echo `gettext "WARNING: zone did not finish booting."`
	fi

	echo "yes" | zlogin -S $ZONENAME sys-unconfig >/dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		error "$e_unconfig"
		failed=1
		break
	fi
fi

if [[ -z $OPT_U ]]; then
	verbose "$v_halting"
	zoneadm -z $ZONENAME halt
	if [[ $? -ne 0 ]]; then
		#
		# This may need to be propagated up as a truly fatal
		# problem -- if we can't halt the zone, it's going to be hard
		# to delete things from it.
		#
		error "$e_badhalt"
		failed=1
	fi
fi
zone_is_running=0

if [[ -n $failed ]]; then
	error "$e_exitfail"
	exit 1
else
	verbose "$v_exitgood"
	exit 0
fi
