#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
#

# NOTE: this script runs in the global zone and touches the non-global
# zone, so care should be taken to validate any modifications so that they
# are safe.

#
# Only change PATH if you give full consideration to GNU or other variants
# of common commands having different arguments and output.
#
export PATH=/usr/bin:/usr/sbin
unset LD_LIBRARY_PATH

. /usr/lib/brand/solaris/common.ksh

# Allows developer to override some things like PATH and PYTHONPATH
. /usr/lib/brand/solaris/developerenv.ksh

e_badinfo=$(gettext "Failed to get '%s' zone resource")
e_badfile=$(gettext "Invalid '%s' file within the zone")
v_mkdirs=$(gettext "Creating mount points")
v_change_var=$(gettext "Changing the pkg variant to nonglobal...")
e_change_var=$(gettext "Changing the pkg variant to nonglobal failed")
v_update=$(gettext "Updating the zone software to match the global zone...")
e_badupdate=$(gettext "Updating the Zone software failed")
v_adjust=$(gettext "Updating the image to run within a zone")
v_stacktype=$(gettext "Stack type '%s'")
v_fixnetsvcs=$(gettext "Adjusting network SMF services")
v_rminvalidsvcs=$(gettext "Disabling invalid SMF services")
v_enblsvc=$(gettext "Enable SMF svc '%s'")
v_dissvc=$(gettext "Disable SMF svc '%s'")
v_rmpkg=$(gettext "Remove package '%s'")
e_rmpkg=$(gettext "removing package '%s'")
v_exitgood=$(gettext "Postprocessing successful.")
f_missing_zone_zp=$(gettext "Missing zone or zonepath argument.")
m_usage=$(gettext "/usr/lib/brand/solaris/p2v [-uv] [-c sysconfig] zone zonepath")

set -A save_args "$0" "$@"

PKG=pkg
EXIT_CODE=$ZONE_SUBPROC_USAGE

# Clean up on failure
trap_exit()
{
	if (( $ZONE_IS_MOUNTED != 0 )); then
		error "$v_unmount"
		zoneadm -z $ZONENAME unmount
	fi
	vlog "Exiting with exit code $EXIT_CODE"
	finish_log zone

	exit $EXIT_CODE
}

#
# Comment out most of the old mounts since they are either unneeded or
# likely incorrect within a zone.  Specific mounts can be manually
# reenabled if the corresponding device is added to the zone.
#
fix_vfstab()
{
	if [[ -h $ZONEROOT/etc/vfstab || ! -f $ZONEROOT/etc/vfstab ]]; then
		error "$e_badfile" "/etc/vfstab"
		return
	fi

	tmpfile=$(mktemp -t)
	if [[ -z "$tmpfile" ]]; then
		error "$e_tmpfile"
		return
	fi

	nawk '{
		if (substr($1, 0, 1) == "#") {
			print $0
		} else if ($1 == "fd" || $1 == "/proc" || $1 == "swap" ||
		    $1 == "ctfs" || $1 == "objfs" || $1 == "sharefs" ||
		    $4 == "nfs" || $4 == "lofs") {
			print $0
		} else {
			print "#", $0
			modified=1
		}
	}
	END {
		if (modified == 1) {
			printf("# Modified by p2v ")
			system("/usr/bin/date")
			exit 0
		}
		exit 1
	}' $ZONEROOT/etc/vfstab >>$tmpfile

	if (( $? == 0 )); then
		if [[ ! -f $ZONEROOT/etc/vfstab.pre_p2v ]]; then
			safe_copy $ZONEROOT/etc/vfstab \
			    $ZONEROOT/etc/vfstab.pre_p2v
		fi
		safe_copy $tmpfile $ZONEROOT/etc/vfstab
	fi
	rm -f $tmpfile
}

#
# Delete or disable SMF services.
#
fix_smf()
{
	SMF_UPGRADE=/a/var/svc/profile/upgrade

	#
	# Fix network services if shared stack.
	#
	if [[ "$STACK_TYPE" == "shared" ]]; then
		vlog "$v_fixnetsvcs"

		NETPHYSDEF="svc:/network/physical:default"
		NETPHYSNWAM="svc:/network/physical:nwam"

		vlog "$v_enblsvc" "$NETPHYSDEF"
		zlogin -S $ZONENAME "echo /usr/sbin/svcadm enable $NETPHYSDEF \
		    >>$SMF_UPGRADE" </dev/null

		vlog "$v_dissvc" "$NETPHYSNWAM"
		zlogin -S $ZONENAME \
		    "echo /usr/sbin/svcadm disable $NETPHYSNWAM \
		    >>$SMF_UPGRADE" </dev/null

		# Disable routing svcs.
		vlog "$v_dissvc" 'svc:/network/routing/*'
		zlogin -S $ZONENAME \
		    "echo /usr/sbin/svcadm disable 'svc:/network/routing/*' \
		    >>$SMF_UPGRADE" </dev/null
	fi

	#
	# Disable well-known services that don't run in a zone.
	#
	vlog "$v_rminvalidsvcs"
	for i in $(egrep -hv "^#" \
	    /usr/lib/brand/solaris/smf_disable.lst \
	    /etc/brand/solaris/smf_disable.conf)
	do
		# Disable the svc.
		vlog "$v_dissvc" "$i"
		zlogin -S $ZONENAME \
		    "echo /usr/sbin/svcadm disable $i >>$SMF_UPGRADE" </dev/null
	done
}

#
# Remove well-known pkgs that do not work inside a zone.
#
rm_pkgs()
{
	for i in $(egrep -hv "^#" /usr/lib/brand/solaris/pkgrm.lst \
	    /etc/brand/solaris/pkgrm.conf)
	do
		$PKG info $i >/dev/null 2>&1
		if (( $? != 0 )); then
			continue
		fi

		vlog "$v_rmpkg" "$i"
		zlogin -S $ZONENAME LC_ALL=C \
		    /usr/bin/pkg -R /a uninstall -r $i </dev/null >&2 || \
		    error "$e_rmpkg" $i
	done
}

#
# Parse the command line options.
#
OPT_V=

while getopts "v" opt
do
	case "$opt" in
	v)	# Used by vlog()
		OPT_V="-v"
		;;
	*)	fail_usage ""
		;;
	esac
done
shift OPTIND-1

(( $# != 2 )) && fail_usage "$f_missing_zone_zp"

# If fixing up the image fails, force an uninstall.
EXIT_CODE=$ZONE_SUBPROC_FATAL
init_zone zone "$1" "$2"
eval $(bind_legacy_zone_globals zone)

#
# failure should unmount the zone if necessary;
#
ZONE_IS_MOUNTED=0
trap trap_exit EXIT

start_log zone install "${save_args[@]}"

#
# Do some validation on the paths we'll be accessing
#
safe_dir etc
safe_dir etc/dfs
safe_dir etc/zones
safe_dir var
safe_dir var/log
safe_dir var/pkg

# If these paths exist, they must be directories.  If they don't exist,
# they will be created below.
safe_opt_dir etc/svc
safe_opt_dir system
safe_opt_dir system/volatile

# 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.
vlog "$v_mkdirs"
if [[ ! -f $ZONEROOT/tmp && ! -d $ZONEROOT/tmp ]]; then
	mkdir -m 1777 -p $ZONEROOT/tmp || exit $EXIT_CODE
fi
if [[ ! -h $ZONEROOT/etc && ! -f $ZONEROOT/etc/mnttab ]]; then
	touch $ZONEROOT/etc/mnttab || exit $EXIT_CODE
	chmod 444 $ZONEROOT/etc/mnttab || exit $EXIT_CODE
fi
if [[ ! -f $ZONEROOT/proc && ! -d $ZONEROOT/proc ]]; then
	mkdir -m 755 -p $ZONEROOT/proc || exit $EXIT_CODE
fi
if [[ ! -f $ZONEROOT/dev && ! -d $ZONEROOT/dev ]]; then
	mkdir -m 755 -p $ZONEROOT/dev || exit $EXIT_CODE
fi
if [[ ! -d $ZONEROOT/system/volatile ]]; then
	mkdir -m 755 -p $ZONEROOT/system/volatile || exit $EXIT_CODE
fi

# symlink: /etc/svc/volatile -> /system/volatile
if [[ ! -d $ZONEROOT/etc/svc ]]; then
	mkdir -m 755 -p $ZONEROOT/etc/svc || exit $EXIT_CODE
fi
if [[ -e $ZONEROOT/etc/svc/volatile || -h $ZONEROOT/etc/svc/volatile ]]; then
	rm -rf $ZONEROOT/etc/svc/volatile || exit $EXIT_CODE
fi
ln -s ../../system/volatile $ZONEROOT/etc/svc/volatile

# symlink: /var/run -> /system/volatile
if [[ -e $ZONEROOT/var/run || -h $ZONEROOT/var/run ]]; then
	rm -rf $ZONEROOT/var/run || exit $EXIT_CODE
fi
ln -s ../system/volatile $ZONEROOT/var/run

# Check for zones inside of image.
warn_zones

STACK_TYPE=$(zoneadm -z $ZONENAME list -p | nawk -F: '{print $7}')
if (( $? != 0 )); then
	error "$e_badinfo" "stacktype"
fi
vlog "$v_stacktype" "$STACK_TYPE"

# Note that we're doing this before update-on-attach has run.
fix_vfstab

#
# Mount the zone so that we can do all of the updates needed on the zone.
#
vlog "$v_mounting"
ZONE_IS_MOUNTED=1
zoneadm -z $ZONENAME mount -f || fatal "$e_badmount"

#
# Any errors in these functions are not considered fatal.  The zone can be
# be fixed up manually afterwards and it may need some additional manual
# cleanup in any case.
#

log "$v_adjust"
# cleanup SMF services
fix_smf
# remove invalid pkgs
rm_pkgs

# If anything below here fails, a subsequent attach can likely fix it up.
EXIT_CODE=$ZONE_SUBPROC_UNAVAILABLE

#
# This is unintuitive:  The call to 'zoneadm unmount' tears down the scratch
# zone.  Upon completion of 'zoneadm unmount' the zone is in the same state
# that it would be in after 'zoneadm sysboot'.  That is, brands other than
# labeled have the complete active ZBE mounted on the zone root.
#
vlog "$v_unmount"
zoneadm -z $ZONENAME unmount || fatal "$e_badunmount"
ZONE_IS_MOUNTED=0

if is_brand_labeled; then
	# The labeled brand needs to mount the zone's root dataset back onto
	# ZONEROOT so we can finish processing.
	mount_active_be -C zone || fatal "$f_mount_active_be"
fi

#
# Change the pkg variant from global zone to non-global zone.  This assumes
# that the caller has already:
#   - updated the image format
#   - refreshed the catalog in the zone's image
#   - set the pkg image property use-system-repo=true
#
log "$v_change_var"
$PKG -R "$ZONEROOT" change-variant --no-refresh -I \
    variant.opensolaris.zone=nonglobal || pkg_err_check "$e_change_var"

trap - EXIT
vlog "$v_exitgood"
finish_log zone
exit $ZONE_SUBPROC_OK
