OpenSolaris_b135/lib/brand/lx/zone/lx_distro_install.ksh

#!/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 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# This script is called from /usr/lib/brand/lx/lx_install.
# 
# options passed down from lx_install:
#	-z $ZONENAME
#	-r $LINUX_ROOT
#
# options passed down from zoneadm -z <zone-name> install
#	-d <Linux-archives-dir>
#	[core | server | desktop | development | all]
#
# The desktop cluster will be installed by default.
#

# Restrict executables to /bin, /usr/bin and /usr/sbin
PATH=/bin:/usr/bin:/usr/sbin
export PATH


# Setup i18n output
TEXTDOMAIN="SUNW_OST_OSCMD"
export TEXTDOMAIN

# Log passed arguments to file descriptor 2
log()
{
	[[ -n $logfile ]] && echo "$@" >&2
}

#
# Send the provided printf()-style arguments to the screen and to the
# logfile.
#
screenlog()
{
	typeset fmt="$1"
	shift

	printf "$fmt\n" "$@"
	[[ -n $logfile ]] && printf "$fmt\n" "$@" >&2
}

# Print and log provided text if the shell variable "verbose_mode" is set
verbose()
{
	[[ -n $verbose_mode ]] && echo "$@"
	[[ -n $logfile ]] && [[ -n $verbose_mode ]] && echo "$@" >&2
}

#
# Print to the screen if the shell variable "verbose_mode" is set, but always
# send the output to the log.
#
verboselog()
{
	[[ -n $verbose_mode ]] && echo "$@"
	[[ -n $logfile ]] && echo "$@" >&2
}

bad_rpmdir=$(gettext "'%s' is not a valid RPM directory!")

mb_req=$(gettext "(%s MB required, %s MB available)")
no_space=$(gettext "Not enough free space available in '%s'")

inst_clust=$(gettext "Installing cluster '%s'")
unknown_clust=$(gettext "ERROR: Unknown cluster name: '%s'")

unknown_media=$(gettext "Unknown or unreadable media loaded in %s")

eject_fail=$(gettext "Attempt to eject '%s' failed.")

lofi_failed=$(gettext "Attempt to add '%s' as lofi device FAILED.")
lofs_failed=$(gettext "Attempt to lofs mount '%s' on '%s' FAILED.")

media_spec=$(gettext "the provided media (%s)")

distro_mediafail=\
$(gettext "Attempt to determine Linux distribution from\n  %s FAILED.")

mini_bootfail=$(gettext "Attempt to boot miniroot for zone '%s' FAILED.")
mini_copyfail=$(gettext "Attempt to copy miniroot for zone '%s' FAILED.")
mini_initfail=$(gettext "Attempt to initialize miniroot for zone '%s' FAILED.")
mini_instfail=$(gettext "Attempt to install RPM '%s' to miniroot FAILED.")
mini_mediafail=$(gettext "Install of zone '%s' miniroot from\n  %s FAILED.")
mini_setfail=$(gettext "Attempt to setup miniroot for zone '%s' FAILED.")

mini_mntfsfail=\
$(gettext "Attempt to mount miniroot filesystems for zone '%s' FAILED.")

rpm_initfail=\
$(gettext "Attempt to initialize RPM database for zone '%s' FAILED.")

symlink_failed=$(gettext "Attempt to symbolically link '%s' to '%s' FAILED.")

discinfo_nofile=$(gettext "ERROR: Discinfo file '%s' not found!")
discinfo_notreadable=$(gettext "ERROR: Discinfo file '%s': not readable!")
discinfo_wrongarch=\
$(gettext "ERROR: '%s': disc architecture is '%s'; install requires 'i386'!")

wrong_serial=$(gettext "Incorrect serial number found on provided %s.")
wrong_ser_expect=$(gettext "  (found #%s, expected #%s)")

wrong_cd=$(gettext "Incorrect CD inserted (found %s, wanted %s)")

zone_initrootfail=\
$(gettext "Attempt to initialize root filesystem for zone '%s' FAILED.")

zone_haltfail=$(gettext "Unable to halt zone '%s'!")
zone_instfail=$(gettext "Install of zone '%s' from '%s' FAILED '%s'.")
zone_mediafail=$(gettext "Install of zone '%s' from\n  %s FAILED.")

zone_rootfail=\
$(gettext "ERROR: The specified zone root directory '%s' could not be created.")
zone_rootsub=\
$(gettext "ERROR: The specified zone root subdirectory '%s' does not exist.")

mk_mntfail=$(gettext "Could not create the mount directory '%s'")
mountfail=$(gettext "Mount of '%s' on '%s' FAILED.")

insert_discmsg=\
$(gettext "Please insert %s, or a\n  %s DVD in the removable media")

mount_proper_iso1=$(gettext "Please mount the ISO for %s or a")
mount_proper_iso2=$(gettext "%s DVD on device '%s'")

silent_nodisc=$(gettext "ERROR: Cannot install from CDs in silent mode.")
silent_nolofi=\
$(gettext "ERROR: Cannot install from lofi-based CD ISOs in silent mode.")

install_msg=$(gettext "Installing zone '%s' from\n  %s.")
install_ndiscs=\
$(gettext "You will need CDs 1 - %s (or the equivalent DVD) to")
install_nisos=\
$(gettext "You will need ISO images representing CDs 1 - %s (or the equivalent")

locate_npkgs=$(gettext "Attempting to locate %s packages...")

install_one_rpm=$(gettext "Installing 1 %spackage.")
install_nrpms_few=\
$(gettext "Installing %s %spackages; this may take a few minutes...")
install_nrpms_several=\
$(gettext "Installing %s %spackages; this may take several minutes...")

install_longwait=\
$(gettext "NOTE: There may be a long delay before you see further output.")

install_defmkfail=$(gettext "Could not create the temporary directory '%s'")
install_defcpfail=$(gettext "Could not make a local copy of deferred RPM '%s'")
install_dist=$(gettext "Installing distribution '%s'...")
install_zonefail=$(gettext "Attempt to install zone '%s' FAILED.")

no_distropath=$(gettext "ERROR: Distribution path '%s' doesn't exist.")

install_done=$(gettext "Installation of %s to zone\n  '%s' completed %s.")
install_failed=$(gettext "Installation of %s to zone\n  '%s' FAILED %s.")

eject_final_msg=\
$(gettext "Would you like the system to eject the %sinstall %s when")
eject_final_prompt=$(gettext "installation of '%s' is complete? (%s)")
eject_final_status=$(gettext "The %sinstall %s %s be ejected.")

#
# Get the device underlying a specified mounted file system and return it in
# the shell variable "mount_dev"
#
# Returns 0 on success, 1 on failure.
#
get_mountdev()
{
	typeset mount_dir="$1"
	typeset device
	unset mount_dev

	#
	# Obtain information on the specified mounted device.
	#
	device=`{ df -k "$mount_dir" | egrep "^/" ; } 2>/dev/null` || return 1
	mount_dev=$(echo $device | awk -e '{print $1}' 2>/dev/null)

	[[ "`echo $mount_dev | cut -c 1`" = "/" ]] && return 0

	unset mount_dev
	return 1
}

#
# Get the directory name a specified device is mounted as and return it in
# the shell variable "mount_dir"
#
# Returns 0 on success, 1 on failre.
#
get_mountdir()
{
	typeset mount_dev="$1"
	typeset dir
	unset mount_dir

	[[ -b "$mount_dev" ]] || return 1  

	#
	# Obtain information on the specified mounted device.
	#
	dir=`{ df -k "$mount_dev" | egrep "^/" ; } 2>/dev/null` || return 1
	mount_dir=$(echo $dir | awk -e '{print $6}' 2>/dev/null)

	[[ "`echo $mount_dir | cut -c 1`" = "/" ]] && return 0

	unset mount_dir
	return 1
}

#
# Check the free disk space of the passed filesystem against the passed
# argument.
#
# Returns 0 on success, 1 on failure.
#
check_mbfree()
{
	typeset dir="$1"
	typeset mb_required=$2

	#
	# Return free space in partition containing passed argument in MB
	#
	typeset mbfree=`{ LC_ALL=C df -k "$dir" | \
	    egrep -v Filesystem ; } 2>/dev/null` || return 1
	mbfree=$(echo $mbfree | awk -e '{print $4}' 2>/dev/null)

	((mbfree /= 1024))
	if ((mbfree < mb_required)); then
		screenlog "$no_space" "$zoneroot"
		screenlog "$mb_req" "$mb_required" "$mb_free"
		return 1
	fi
	return 0
}

#
# Find packages by attempting to expand passed RPM names to their full filenames
# in the passed RPM directory.
#
# Arguments:
#
#	Argument 1:  Path to mounted install media
#	Arguments [2 - n]:  RPM names to process
#
# The expanded filenames are returned in the shell array "rpm_names."
#
# For example:
#
#	find_packages /mnt/iso dev kernel tetex redhat-menus
#
# would return something like:
#
#	rpms_found[0]:  dev-3.3.12.3-1.centos.0.i386.rpm
#	rpms_found[1]:  kernel-2.4.21-32.EL.i586.rpm
#	rpms_found[2]:  tetex-1.0.7-67.7.i386.rpm
#	rpms_found[3]:  redhat-menus-0.39-1.noarch.rpm
#
# The routine returns 0 on success, 1 on an error.
#
find_packages()
{
	typeset found=0
	typeset left=0

	typeset rpmdir="$1/$rd_rpmdir"
	typeset curdir=${PWD:=$(pwd)}

	typeset arch
	typeset procinfo
	typeset rpmglob
	typeset rpmfile

	unset rpms_found
	unset rpms_left

	shift
	cd "$rpmdir"

	typeset rpmcheck="$(echo *.rpm)"

	if [[ "$rpmcheck" = "*.rpm" ]]; then
		screenlog "$bad_rpmdir" "$rpmdir"
		cd "$curdir"
		return 1
	fi

	#
	# If the miniroot is booted, and the archs list isn't already set,
	# ask the zone's rpm command for the list of compatible architectures.
	#
	if [[ -n $miniroot_booted && -z $archs ]]; then
		procinfo=$(zlogin "$zonename" /bin/rpm --showrc | \
		    grep "^compatible archs")

		[[ $? -eq 0 ]] &&
		    archs=$(echo $procinfo | sed 's/^compatible archs : //')

		[[ -n $archs ]] &&
		    log "RPM-reported compatible architectures: $archs"
	fi

	#
	# Either the miniroot isn't booted or asking rpm for the information
	# failed for some reason, so make some reasonable assumptions.
	#
	if [[ -z $archs ]]; then
		procinfo=$(LC_ALL=C psrinfo -vp | grep family)

		#
		# Check for additional processor capabilities
		#
		if [[ "$procinfo" = *" family 6 "* ||
		    "$procinfo" = *" family 15 "* ||
		    "$procinfo" = *" family 16 "* ||
		    "$procinfo" = *" family 17 "* ]]; then
			if [[ "$procinfo" = *AuthenticAMD* ]]; then
				#
				# Linux gives "athlon" packages precedence
				# over "i686" packages, so duplicate that
				# here.
				#
				archs="athlon i686"
			else
				archs="i686"
			fi
		fi

		archs="$archs i586 i486 i386 noarch"

		log "Derived compatible architectures: $archs"
	fi

	verboselog "RPM source directory:\n  \"$rpmdir\"\n"

	if [[ $# -eq 1 ]]; then
		msg=$(gettext "Attempting to locate 1 package...")
		screenlog "$msg"
	else
		screenlog "$locate_npkgs" "$#"
	fi

	for rpm in "$@"; do
		#
		# Search for the appropriate RPM, using the compatible
		# architecture list contained in "archs" to look for the best
		# match.
		#
		# For example, if the processor is an i686, and the rpm is
		# "glibc", the script will look for the files (in order):
		#
		#    glibc[.-][0-9]*.i686.rpm
		#    glibc[.-][0-9]*.i586.rpm
		#    glibc[.-][0-9]*.i486.rpm
		#    glibc[.-][0-9]*.i386.rpm
		#    glibc[.-][0-9]*.noarch.rpm
		#    glibc[.-][0-9]*.fat.rpm
		#
		# and will stop when it finds the first match.
		#
		# TODO: Once the miniroot is booted, we should verify that
		#	the rpm name has been expanded to "$rpmfile" properly
		#	by comparing "$rpm" and the output of:
		#
		#	zlogin -z <zone> /bin/rpm --qf '%{NAME}' -qp $rpmfile
		#
		for arch in $archs; do
			#
			# Use the filename globbing functionality of ksh's
			# echo command to search for the file we want.
			#
			# If no matching file is found, echo will simply
			# return the passed string.
			#
			rpmglob="$rpm[.-][0-9]*.$arch.rpm"
			rpmfile="$(echo $rpmglob)"

			[[ "$rpmfile" != "$rpmglob" ]] && break

			unset rpmfile
                done

                if [[ -z $rpmfile ]]; then
			rpms_left[$left]="$rpm"
			((left += 1))
                else
                        rpms_found[$found]="$rpmfile"
			((found += 1))
                fi
	done

	cd "$curdir"
	log "\"$rpmdir\": matched $found of $# packages."
	log "\"$rpmdir\": $left RPMs remaining."
	return 0
}

#
# Build the rpm lists used to install a machine.
#
# The first argument is the number of discs in the distribution.  The
# second, optional, argument is the metacluster to install.
#
# The array "distro_rpm[]" is built from the individual package RPM arrays
# read in from an individual distribution definition file.
#
build_rpm_list()
{
	# Default to a desktop installation
	typeset cluster=desktop
	typeset cnt=0
	typeset pkgs

	for clust in "$@"; do
		((cnt += 1))
		case $clust in
			core)	cluster=core ;;
			desk*)	cluster=desktop ;;
			serv*)	cluster=server ;;
			dev*)	cluster=developer ;;
			all)	cluster=all
				break;;
			*)      screenlog "$unknown_clust" "$clust"
				exit $ZONE_SUBPROC_USAGE ;;
		esac
	done

	if [ $cnt -gt 1 ]; then
		msg=$(gettext "Too many install clusters specified")
		screenlog "$msg"
		exit $ZONE_SUBPROC_USAGE
	fi

	screenlog "$inst_clust" $cluster

	case $cluster in
		core)		distro_rpms=$distro_core_rpms ;;
		desktop)	distro_rpms=$distro_desktop_rpms ;;
		server)		distro_rpms=$distro_server_rpms ;;
		developer)	distro_rpms=$distro_developer_rpms ;;
		all)		distro_rpms=$distro_all_rpms ;;
	esac

	# The RPMs in the miniroot must all be installed properly as well
	distro_rpms="$distro_miniroot_rpms $distro_rpms"
}

#
# Install the "miniroot" minimal Linux environment that is booted single-user
# to complete the install.
#
# This works by doing feeding the RPM list needed for the installation one
# by one to rpm2cpio(1).
#
# Usage:
#    install_miniroot <mounted media dir> <names of RPMS to install>
#      
#
install_miniroot()
{
        typeset mediadir="$1"
	typeset rpm

	shift

	#
	# There's a quirk in our version of ksh that sometimes resets the
	# trap handler for the shell.  Since RPM operations will be the
	# longest part of any given install, make sure that an interrupt while
	# the command is running will bring the miniroot down and clean up
	# the interrupted install.
	#
	trap trap_cleanup INT

	if [[ $# -eq 1 ]]; then
		msg=$(gettext "Installing %s miniroot package...")
	else
		msg=$(gettext "Installing %s miniroot packages...")
	fi

	screenlog "\n$msg" "$#"

        for rpm in "$@"; do
		verboselog "\nInstalling \"$rpm\" to miniroot at\n" \
			"  \"$zoneroot\"..."

		rpm2cpio "$mediadir/$rd_rpmdir/$rpm" | \
		    ( cd "$rootdir" && cpio -idu ) 1>&2

		if [[ $? -ne 0 ]]; then
			screenlog "$mini_instfail" "$rpm"
			return 1
		fi
        done

	screenlog ""
	return 0
}

#
# Install the zone from the mounted disc image by feeding a list of RPMs to
# install from this image to RPM running on the zone via zlogin(1).
#
# Usage:
#    install_zone <path to mounted install media> [<names of RPMS to install>]
#
# If the caller doesn't supply a list of RPMs to install, we install any
# we previously stashed away in the deferred RPMs directory.
#
install_zone()
{
	#
	# Convert the passed install media pathname to a zone-relative path
	# by stripping $rootpath from the head of the path.
	#
	typeset zonerpmdir="${1##$rootdir}/$rd_rpmdir"

	typeset defdir="$rootdir/var/lx_install/deferred_rpms"
	typeset mounted_root="$1"
	typeset rpmopts="-i"

	typeset defer
	typeset deferred_found
	typeset install_rpms
	typeset nrpms
	typeset rpm
	typeset rpmerr

	shift

	#
	# If the caller provided a list of RPMs, determine which of them
	# should be installed now, and which should be deferred until
	# later.
	#
	if [[ $# -gt 0 ]]; then
		if [[ -n $deferred_rpms ]]; then
			[[ -d $defdir ]] || if ! mkdir -p $defdir; then
				screenlog "$install_defmkfail" "$mntdir"
				return 1
			fi

			msg=$(gettext "Checking for deferred packages...")
			screenlog "$msg"

			find_packages "$mounted_root" $deferred_rpms
			deferred_found="${rpms_found[@]}"
			numdeferred=${#rpms_found[@]}
		else
			deferred_found=""
		fi

		install_rpms="$@"
		nrpms=$#

		#
		# If this distro has any deferred RPMs, we want to simply
	 	# copy them into the zone instead of installing them.  We
		# then remove them from the list of RPMs to be installed on
		# this pass.
		#
		for rpm in $deferred_found; do
			if echo "$install_rpms" | egrep -s "$rpm"; then
				verboselog "Deferring installation of \"$rpm\""

				#
				# Remove the RPM from the install_rpms list
				# and append it to the deferred_saved array
				#
				install_rpms=$(echo "$install_rpms " |
				    sed "s/ $rpm / /g")

				# remove trailing spaces, if any
				install_rpms=${install_rpms%%+( )}

				deferred_saved[${#deferred_saved[@]}]="$rpm"

				if ! cp "$mounted_root/$rd_rpmdir/$rpm" \
				    "$defdir"; then
					screenlog "$install_defcpfail" "$rpm"
					return 1
				fi
			fi

			#
			# If we've deferred the installation of EVERYTHING,
			# simply return success
			#
			[[ -z $install_rpms ]] && return 0
		done

		[[ -n $deferred_found ]] & verbose ""
	elif [[ -z $deferred_saved ]]; then
		# There are no deferred RPMs to install, so we're done.
		return 0
	else
		# Install the RPMs listed in the deferred_saved array
		install_rpms=${deferred_saved[@]}
		nrpms=${#deferred_saved[@]}
		zonerpmdir=/var/lx_install/deferred_rpms
		defer="deferred "
	fi

	#
	# There's a quirk in our version of ksh that sometimes resets the
	# trap handler for the shell.  Since RPM operations will be the
	# longest part of any given install, make sure that an interrupt while
	# the command is running will bring the miniroot down and clean up
	# the interrupted install.
	#
	trap trap_cleanup INT

	#
	# Print a message depending on how many RPMS we have to install.
	#
	# 25 RPMS seems like a reasonable boundary between when an install may
	# take a "few" or "several" minutes; this may be tuned if needed.
	#
	screenlog ""

	if [[ $nrpms -eq 1 ]]; then
		screenlog "$install_one_rpm" "$defer"
	elif [[ $nrpms -lt 25 ]]; then
		screenlog "$install_nrpms_few" "$nrpms" "$defer"
	else
		screenlog "$install_nrpms_several" "$nrpms" "$defer"

		#
		# For installs of over 600 packages or so, it can take rpm a
		# really, REALLY long time to output anything, even when
		# running in verbose mode.
		#
		# For example, when doing an "all" install from a DVD or DVD
		# ISO, depending on the speed of the optical drive and the
		# speed of the machine's CPU(s), it may be up to TEN MINUTES or
		# MORE before rpm prints out its "Processing..." message even
		# though it is, in fact, processing the entire package list,
		# checking for dependencies (something it is unfortunately
		# entirely silent about.)
		#
		# Since the user might otherwise think the install was hung
		# when running in verbose mode, warn them that it could be
		# quite a while before they see any further output from the
		# installer.
		#
		#
		[[ $nrpms -gt 600 ]] && verbose "$install_longwait"
	fi

	log ""
	log "Installing: $install_rpms"
	log ""
	log "NOTE:  Any messages appearing below prefixed with \"warning:\""
	log "       and/or that do not cause the installer to abort the"
	log "       installation process may safely be ignored."
	log ""

	echo

	# If verbose mode is selected, run rpm in verbose mode as well.
	[[ -n $verbose_mode ]] && rpmopts="-ivh"

	#
	# LX_INSTALL must be defined when running this command in order to
	# enable switches built into various emulated system calls to allow
	# the dev package (which may not actually write to /dev) to function.
	#
	zlogin "$zonename" "( cd "$zonerpmdir" ; LX_INSTALL=1 \
	    /bin/rpm $rpmopts --force --aid --nosignature --root /a \
	    $install_rpms )"

	rpmerr=$?

	if [[ $rpmerr -ne 0 ]]; then
		log ""
		log "Zone rpm install command exited abnormally, code $rpmerr"
		log ""

		screenlog "$zone_instfail" "$zonename" "$zonerpmdir" "$rpmerr"
		return 1
	fi

	log ""
	log "$nrpms package(s) installed."

	return 0
}

#
# Attempt to unmount all file systems passed on the command line
#
# Returns 0 if all umounts succeeded, otherwise the number of umount failures
#
umount_list()
{
	typeset failures=0
	typeset mounted

	unset umount_failures

	for mounted in "$@"; do
		if ! umount "$mounted"; then
			umount_failures="$umount_failures $mounted"
			((failures += 1))
		fi
	done

	return $failures
}

#
#
# Set up lofi mounts required for chroot(1M) to work on a new root directory
# located in /a within a zone.
#
newroot_lofimnt()
{
	typeset dev
	typeset mounted
	typeset target

	unset newroot_mounted

	#
	# /usr and /lib get lofs mounted in the zone on /native read-only
	#
	# $zoneroot/dev gets lofs mounted on /native/dev read/write to allow
	# the use of native devices.
	#
	mount -F lofs -r /lib "$rootdir/a/native/lib" || return 1
	newroot_mounted="$rootdir/a/native/lib"

	if ! mount -F lofs -r /usr "$rootdir/a/native/usr"; then
		umount "$rootdir/a/native/lib"
		unset newroot_mounted
		return 1
	fi

	newroot_mounted="$newroot_mounted $rootdir/a/native/usr"

	if ! mount -F lofs "$zoneroot/root/native/dev" \
	    "$rootdir/a/native/dev"; then
		umount_list $newroot_mounted
		unset newroot_mounted
		return 1
	fi

	newroot_mounted="$newroot_mounted $rootdir/a/native/dev"

	#
	# This is a bit ugly; to provide device access within the chrooted
	# environment RPM will use for its install, we will create the same
	# symlinks "$rootdir/dev" contains in the new dev directory, and will
	# lofs mount the balance of "$rootdir/dev" into the same locations in
	# /dev in the new filesystem we're installing to.
	#
	for dev in "$zoneroot"/root/dev/*
	do
		if [[ "$dev" = "$zoneroot/root/dev/*" ]]; then
			log "ERROR: No files found in $zoneroot/root/dev"
			umount_list $newroot_mounted
			return 1
		fi

		target="$rootdir/a/dev/$(basename $dev)"

		#
		# If the device file is a symbolic link, create a new link
		# in the target directory with the same source.
		#
		# If the device file is any other file or directory, lofs
		# mount it from the device directory into the target directory.
		#
		if [[ -h $dev ]]; then
			typeset source=$(LC_ALL=C file -h "$dev")

			#
			# Remove extraneous text from the output of file(1) so
			# we're left only with the target path of the symbolic
			# link.
			#
			source="${source##*link to }"

			[[ -a "$target" ]] && /bin/rm -f "$target"

			if ! ln -s "$source" "$target"; then
				screenlog "$symlink_failed" "$source" "$target"
				umount_list $newroot_mounted
				unset newroot_mounted
				return 1
			fi
		else
			[[ ! -a "$target" ]] && touch "$target"

			if ! mount -F lofs "$dev" "$target"; then
				screenlog "$lofs_failed" "$dev" "$target"
				umount_list $newroot_mounted
				unset newroot_mounted
				return 1
			fi

			newroot_mounted="$newroot_mounted $target"
		fi

	done

	return 0
}

#
# Replace the root directory of a zone with the duplicate previously created
# in the zone's /a directory.
#
replace_miniroot()
{
	#
	# The zoneadm halt will automatically unmount any file systems
	# mounted via lofs in the zone, so that saves us from having to
	# methodically unmount each one.
	#
	if ! zoneadm -z "$zonename" halt; then
		screenlog "$zone_haltfail" "$zonename"
		return 1
	fi

	unset miniroot_booted
	unset newroot_mounted

	[[ -d "$zoneroot/a" ]] && rm -rf "$zoneroot/a"
	[[ -d "$zoneroot/oldroot" ]] && rm -rf "$zoneroot/oldroot"

	#
	# Copy the logfile or we'll lose all details of the install into the
	# new root directory, so strip "$zoneroot" off the pathname of the
	# current logfile and use it to generate the pathname of the log file
	# in the new root directory.
	#
	[[ -n $logfile && -f "$logfile" ]] &&
	    cp "$logfile" "$rootdir/a${logfile##$rootdir}"

	mv -f "$rootdir/a" "$zoneroot/a" || return 1
	mv -f "$rootdir" "$zoneroot/oldroot" || return 1
	mv -f "$zoneroot/a" "$rootdir" || return 1

	#
	# After the directory munging above, we've moved the new copy of the
	# logfile atop the logfile we WERE writing to, so if we don't reopen
	# the logfile here the shell will continue writing to the old logfile's
	# inode, meaning we would lose all log information from this point on.
	#
	[[ -n $logfile ]] && exec 2>>"$logfile"

	rm -rf "$zoneroot/oldroot"

	#
	# Remove the contents of the /dev directory created by the install.
	#
	# We don't technically need to do this, but the zone infrastructure
	# will mount $zoneroot/dev atop $rootdir/dev anyway, hiding its
	# contents so we may as well clean up after ourselves.
	#
	# The extra checks are some basic paranoia due to the potentially
	# dangerous nature of this command but are not intended to catch all
	# malicious cases
	#
	[[ "$rootdir" != "" && "$rootdir" != "/" ]] && rm -rf "$rootdir"/dev/*

	return 0
}

setup_miniroot()
{
	unset miniroot_booted

	if ! "$cwd/lx_init_zone" "$rootdir" mini; then
		screenlog "$mini_initfail" "$zonename"
		return 1
	fi

	if ! copy_miniroot; then
		screenlog "$mini_copyfail" "$zonename"
		return 1
	fi

	#
	# zoneadm gets upset if the zone root directory is group or world
	# readable or executable, so make sure it isn't before proceeding.
	#
	chmod 0700 "$zoneroot"

	msg=$(gettext "Booting zone miniroot...")
	screenlog "$msg"

	if ! zoneadm -z "$zonename" boot -f; then
		screenlog "$mini_bootfail" "$zonename"
		return 1
	fi

	miniroot_booted=1

	#
	# Now that the miniroot is booted, unset the compatible architecture
	# list that find_packages was using for the miniroot so that it will
	# get the list from rpm for the full install.
	#
	unset archs

	#
	# Mount all the filesystems needed to install the new root
	# directory.
	#
	if ! newroot_lofimnt; then
		screenlog "$mini_mntfsfail" "$zonename"

		if [[ -n $newroot_mounted ]]; then
			umount_list $newroot_mounted
			unset newroot_mounted
		fi
		return 1
	fi

	#
	# Attempt to initialize the RPM database for the new zone
	#
	if ! zlogin "$zonename" /bin/rpm --initdb --root /a; then
		screenlog "$rpm_initfail" "$zonename"
		return 1
	fi

	msg=$(gettext "Miniroot zone setup complete.")
	screenlog "$msg"
	return 0
}

finish_install()
{
	#
	# Perform some last cleanup tasks on the newly installed zone.
	#
	# Note that the zlogin commands aren't checked for errors, as the
	# newly installed zone will still boot even if the commands fail.
	#
	typeset file

	typeset defdir=$rootdir/var/lx_install/deferred_rpms

	msg=$(gettext "Completing installation; this may take a few minutes.")
	screenlog "$msg"

	if [[ -d $defdir ]]; then
		rm -f $defdir/*.rpm
		rmdir $defdir
	fi

	# Run ldconfig in the new root
	zlogin "$zonename" /usr/sbin/chroot /a \
	    /sbin/ldconfig -f /etc/ld.so.conf

	#
	# Create the /etc/shadow and /etc/gshadow files if they don't already
	# exist
	#
	[[ -a "$rootdir/a/etc/shadow" ]] ||
	    zlogin "$zonename" /usr/sbin/chroot /a /usr/sbin/pwconv

	[[ -a "$rootdir/a/etc/gshadow" ]] ||
	    zlogin "$zonename" /usr/sbin/chroot /a /usr/sbin/grpconv

	#
	# Make sure all init.d and rc[0-6].d links are set up properly.
	#
	for file in `ls "$rootdir/a/etc/init.d"`; do
		zlogin "$zonename" /usr/sbin/chroot /a \
		    /sbin/chkconfig --del $file > /dev/null 2>&1

		zlogin "$zonename" /usr/sbin/chroot /a \
		    /sbin/chkconfig --add $file > /dev/null 2>&1
	done

	replace_miniroot

	rmdir -ps "$media_mntdir"

	if ! "$cwd/lx_init_zone" "$rootdir"; then
		screenlog "$zone_initrootfail" "$zonename"
		return 1
	fi

	return 0
}

#
# Duplicate the installed "miniroot" image in a subdirectory of the base
# directory of the zone.
#
# This is done so that a new root directory can be created that will be used
# as the root of a chrooted directory that RPM running on the zone will install
# into.
#
copy_miniroot()
{
	#
	# Create the directory $zoneroot/a if it doesn't already exist
	#
	[[ -d "$zoneroot/a" ]] ||
		{ mkdir -p "$zoneroot/a" || return 1 ; }

	msg=$(gettext "Duplicating miniroot; this may take a few minutes...")
	screenlog "$msg"

	#
	# Duplicate the miniroot to /a, but don't copy over any /etc/rc.d or
	# lxsave_ files.
	#
	( cd "$rootdir"; find . -print | egrep -v "/etc/rc\.d|lxsave_" | \
	    cpio -pdm ../a )

	[[ -d "$rootdir/a" ]] && rm -rf "$rootdir/a" 2>/dev/null
	mv -f "$zoneroot/a" "$rootdir/a" || return 1

	return 0
}

#
# Read the first six lines of the  .discinfo file from the root of the passed
# disc directory (which should either be a mounted disc or ISO file.)
#
# The read lines will be used to set appropriate shell variables on success:
#
#     rd_line[0]:  Disc Set Serial Number (sets rd_serial)
#     rd_line[1]:  Distribution Release Name (sets rd_release)
#     rd_line[2]:  Distribution Architecture (sets rd_arch)
#     rd_line[3]:  Disc Number$[s] in Distribution (sets rd_cdnum)
#     rd_line[4]:  "base" directory for disc (currently unused)
#     rd_line[5]:  RPM directory for disc (sets rd_rpmdir)
#
# Returns 0 on success, 1 on failure.
#
read_discinfo()
{
        typeset rd_file="$1/.discinfo"

	unset rd_arch
	unset rd_cdnum
	unset rd_disctype
	unset rd_pers
	unset rd_release
	unset rd_rpmdir
	unset rd_serial

	#
	# If more than one argument was passed to read_discinfo, the second
	# is a flag meaning that we should NOT print a warning message if
	# we don't find a .discinfo file, as this is just a test to see if
	# a distribution ISO is already mounted on the passed mount point.
	#
	if [[ ! -f "$rd_file" ]]; then
		[[ $# -eq 1 ]] &&
		    screenlog "$discinfo_nofile" "$rd_file"
		return 1
	fi

	verbose "Attempting to read \"$rd_file\"..."

        if [[ ! -r "$rd_file" ]]; then
		screenlog "$discinfo_notreadable" "$rd_file"
		return 1
	fi

	typeset rd_line
        typeset linenum=0

	while read -r rd_line[$linenum]; do
                #
                # If .discinfo architecture isn't "i386," fail here as
		# we only support i386 distros at this time.
                #
                if [[ $linenum = 2 && "${rd_line[2]}" != "i386" ]]; then
			screenlog "$discinfo_wrongarch" "$rd_file" \
			    "${rd_line[2]}"
			return 1
		fi

                #
                # We've successfully read the first six lines of .discinfo
		# into $rd_line, so do the appropriate shell variable munging.
                #
                if ((linenum == 5)); then
			rd_serial=${rd_line[0]}
			rd_release=${rd_line[1]}

			# CentOS names their releases "final"
			[[ "$rd_release" = "final" ]] && rd_release="CentOS"

			#
			# Line four of the .discinfo file contains either a
			# single disc number for a CD or a comma delimited list
			# representing the CDs contained on a particular DVD.
			#
			rd_cdnum=${rd_line[3]}

			if [[ "$rd_cdnum" = *,* ]]; then
				rd_disctype="DVD"
			else
				rd_disctype="CD"
			fi

			rd_rpmdir=${rd_line[5]}

			#
			# If the specified RPM directory doesn't exist, this is
			# not a valid binary RPM disc (it's most likely a
			# source RPM disc), so don't add it to the list of
			# valid ISO files.
			#
			[[ ! -d "$1/$rd_rpmdir" ]] && return 1

			if [[ "$rd_cdnum" = "1" &&
			   "$rd_release" = "Red Hat"* ]]; then
				typeset rh_glob

				#
				# If this is a Red Hat release, get its
				# personality name from the name of the
				# redhat-release RPM package.
				#
				# Start by looking for the file
				# "redhat-release-*.rpm" in the directory
				# RedHat/RPMS of the ISO we're examining by
				# using ksh's "echo" command to handle
				# filename globbing.
				#
				# If no matching file is found, echo will
				# simply return the passed string.
				#
				rh_glob="$1/RedHat/RPMS/redhat-release-*.rpm"
				rd_pers="$(echo $rh_glob)"

				if [[ "$rd_pers" != "$rh_glob" ]]; then
					#
					# An appropriate file was found, so
					# extract the personality type from the
					# filename.
					#
					# For example, the presence of the file:
					#
					#   redhat-release-3WS-13.5.1.i386.rpm
					#
					# would indicate the ISO either
					# represents a "WS" personality CD or
					# a "WS" installation DVD.
					#
					# Start the extraction by deleting the
					# pathname up to the personality type.
					#
					rh_glob="*/redhat-release-[0-9]"
					rd_pers="${rd_pers##$rh_glob}"

					#
					# Now remove the trailing portion of the
					# pathname to leave only the personality
					# type, such as "WS" or "ES."
					#
					rd_pers="${rd_pers%%-*\.rpm}"
				else
					unset rd_pers
				fi
			fi

			return 0
		fi

                ((linenum += 1))
        done < "$rd_file"

        #
        # The file didn't have at least six lines, so indicate that parsing
	# failed.
        #
        return 1
}

#
# Mount install media within the zone.
#
# The media will be mounted at $zoneroot/root/media, either via a loopback
# mount (if it's a managed removable disc) or directly (if the media is an ISO
# file or if the specified filename is a block device.)
#
# Returns 0 on success, 1 on failure, 2 if no disc was available
#
mount_install_media()
{
	typeset device="$1"
	typeset mount_err

	unset removable
	unset zone_mounted

	[[ -z $mntdir ]] && return 1

	[[ -d $mntdir ]] || if ! mkdir -p $mntdir; then
		screenlog "$mk_mntfail" "$mntdir"
		unset mntdir
		return 1
	fi

	if [[ "$install_media" = "disc" && "$managed_removable" = "1" ]]; then
		#
		# The removable disc device is an automatically managed one,
		# so just wait for the device mounter to notice a disc has been
		# inserted into the drive and for the disc to appear at the
		# mount point.
		#
		typeset mount_interval=2
		typeset mount_timeout=10
		typeset mount_timer=0

		typeset nickname=$(basename $device)

		eject -q "$nickname" > /dev/null 2>&1 || return 2
		removable="$nickname"

		#
		# Double check that the device was mounted.  If it wasn't, that
		# usually means the disc in the drive isn't in a format we can
		# read or the physical disc is unreadable in some way.
		#
		# The mount_timer loop is needed because the "eject -q" above
		# may report a disc is available before the mounter associated
		# with the drive actually gets around to mounting the device,
		# so we need to give it a chance to do so.  The mount_interval
		# allows us to short-circuit the timer loop as soon as the
		# device is mounted.
		#
		while ((mount_timer < mount_timeout)); do
			[[ -d "$device" ]] && break

			sleep $mount_interval
			((mount_timer += mount_interval))
		done

		if [[ ! -d "$device" ]]; then
			screenlog "\n$unknown_media" "$device"
			return 2
		fi

		mount -F lofs -r "$device" "$mntdir"
		mount_err=$?
	else
		#
		# Attempt to mount the media manually.
		#
		# First, make sure the passed device name really IS a device.
		#
		[[ -b "$device" ]] || return 2

		#
		# Now check to see if the device is already mounted and lofi
		# mount the existing mount point into the zone if it is.
		#
		if get_mountdir "$device"; then
			mount -F lofs -r "$mount_dir" "$mntdir"
			mount_err=$?
		else
			[[ "$install_media" = "disc" ]] && removable="$device"

			# It wasn't mounted, so go ahead and try to do so.
			mount -F hsfs -r "$device" "$mntdir" 
			mount_err=$?
		fi

		# A mount_err of 33 means no suitable media was found
		((mount_err == 33)) && return 2
	fi

	if ((mount_err != 0)); then
		screenlog "$mountfail" "$device" "$mntdir"
		unset mntdir
		return 1
	fi

	zone_mounted="$mntdir"
	verbose "Mount of \"$device\" on \"$mntdir\" succeeded."
	return 0
}

# Eject the disc mounted on the passed directory name
eject_removable_disc()
{
	screenlog ""
	verbose "  (Attempting to eject '$removable'... \c"

	if [[ -n $zone_mounted ]]; then
		umount "$zone_mounted"
		unset zone_mounted
	fi

	if ! eject "$removable"; then
		verbose "failed.)\n"
		screenlog "$eject_fail" "$removable"

		msg=$(gettext "Please eject the disc manually.")
		screenlog "$msg"
	else
		verbose "done.)\n"
	fi

	unset removable
}

#
# Ask for the user to provide a disc or ISO.
#
# Returns 0 on success, 1 on failure.
#
prompt_for_media()
{
	# No prompting is allowed in silent mode.
	if [[ -n $silent_mode ]]; then
		log "$silent_err_msg"
		return 1
	fi

	if [[ "$1" != "" ]]; then
		msg="$release_name, CD $1"
	else
		typeset disc=$(gettext "disc")

		msg=$(gettext "any")
		msg="$msg $release_name $disc"
	fi

	if [[ "$install_media" = "disc" ]]; then
		screenlog "$insert_discmsg" "$msg" "$release_name"

		msg=$(gettext "drive and press <RETURN>.")
		screenlog "  $msg"

		[[ -n $removable ]] && eject_removable_disc
	else
		if [[ -n $zone_mounted ]]; then
			umount "$mntdir"
			unset zone_mounted
		fi

		#
		# This is only be printed in the case of a user
		# specifying a device name as an install medium.
		# This is handy for testing the installer or if the user
		# has ISOs stored in some strange way that somehow
		# breaks the "install from ISO" mechanism, as ISOs
		# can be manually added using lofiadm(1M) command and
		# the resulting lofi device name passed to the
		# installer.
		#
		screenlog "$mount_proper_iso1" "$msg"
		screenlog "  $mount_proper_iso2" "$release_name" "$mntdev"

		msg=$(gettext "and press <RETURN>.")
		screenlog "  $msg"
	fi

	read && return 0
	
	return 1
}

#
# Get a particular CD of a multi-disc set.
#
# This basically works by doing the following:
#
#     1) Mount the disc
#     2) Read the disc's .discinfo file to see which CD it is or represents
#     3) If it doesn't contain the desired CD, ask the user for a disc
#	 containing the CD we wanted.
#
# Returns 0 on success, 1 on failure.
#
get_cd()
{
	typeset mntdev="$1"

	typeset cdnum
	typeset discname
	typeset enter
	typeset mount_err
	typeset prompted


	if [[ $# -eq 2 ]]; then
		# Caller specified a particular CD to look for
		cdnum="$2"
		discname="$release_name, CD $cdnum"
	else
		# Caller wanted any disc
		discname="a $release_name disc"
	fi

	verboselog "\nChecking for $discname on device"
	verboselog "  \"$mntdev\"\n"

	while :; do
		# Check to see if a distro disc is already mounted
		mntdir="$media_mntdir"

		unset rd_disctype
		if ! read_discinfo "$mntdir" "test"; then
			mount_install_media "$mntdev"
			mount_err=$?

			#
			# If the mount succeeded, continue on in the main
			# script
			#
			if ((mount_err == 0)); then
				read_discinfo "$mntdir"
			elif ((mount_err == 2)); then
				# No medium was found, so prompt for one.
				prompt_for_media "$cdnum" && prompted=1 continue

				unset mntdir
				return 1
			else
				# mount failed
				unset mntdir
				return 1
			fi
		fi

		if [[ -n $distro_serial && 
		    "$rd_serial" != "$distro_serial" ]]; then
			screenlog "$wrong_serial" "$install_disctype"
			screenlog "  $wrong_ser_expect" "$rd_serial" \
			    "$distro_serial"

			#
			# If we're installing from ISOs, don't prompt the user
			# if the wrong serial number is present, as there's
			# nothing they can do about it.
			#
			[[ "$install_media" = "ISO" ]] && return 1

			prompt_for_media "$cdnum" && continue

			umount "$mntdir"
			unset zone_mountdir
			return 1
		fi

		#
		# Make sure that the mounted media is CD $cdnum.
		#
		# If it is, return to the caller, otherwise eject the
		# disc and try again. 
		#
		if [[ "$rd_disctype" = "CD" ]]; then
			verboselog "Found CD #$rd_cdnum," \
			    "Serial #$rd_serial"
			verboselog "Release Name \"$rd_release\""

			[[ -n $rd_pers ]] &&
			    verboselog "Detected RedHat Personality" \
				"\"$rd_pers\""

			verboselog ""

			# If we didn't care which CD it was, return success
			[[ "$cdnum" = "" ]] && return 0

			# Return if the CD number read is a match
			[[ "$rd_cdnum" = "$cdnum" ]] && return 0
		else
			verboselog "\nFound DVD (representing CDs" \
			    "$rd_cdnum), Serial #$rd_serial"
			verboselog "Release Name \"$rd_release\"\n"

			[[ -n $rd_pers ]] &&
			    verboselog "Detected RedHat Personality" \
				"\"$rd_pers\""

			verboselog ""

			# If we didn't care which CD it was, return success
			[[ "$cdnum" = "" ]] && return 0

			#
			# Since a DVD represents multiple CDs, make sure the
			# DVD inserted represents the CD we want.
			#
			{ echo "$rd_cdnum," | egrep -s "$cdnum," ; } &&
			    return 0
		fi

		if [[ -n $prompted ]]; then
			if [[ "$rd_disctype" = "CD" ]]; then
				screenlog "$wrong_cd" "$rd_cdnum" "$cdnum"
			else
				msg=$(gettext "Incorrect DVD inserted.")
				screenlog "$msg"

				log "(DVD represented CDs $rd_cdnum," \
				    " wanted CD $cdnum)"
			fi
		fi

		#
		# If we're installing from ISOs, don't prompt the user if the
		# wrong CD is mounted, as there's nothing they can do about it.
		#
		[[ "$install_media" = "ISO" ]] && return 1

		prompt_for_media "$cdnum" && prompted=1 && continue

		umount "$mntdir"
		unset zone_mountdir
		return 1
	done
}

#
# Find out which distro the mounted disc belongs to by comparing the
# mounted disc's serial number against those contained in the various
# distro files.
#
# When a match is found, the shell variable "distro_file" will be set to
# the name of the matching file.  Since that will have been the last file
# sourced by the shell, there's no need for the caller to do it again; the
# variable is only set in case it's of some use later.
#
# Returns 0 on success, 1 on failure.
#
get_disc_distro()
{
	typeset distro
	typeset distro_files="$(echo $distro_dir/*.distro)"

	unset distro_file
	
	[[ "$distro_files" = "$distro_dir/*.distro" ]] && return 1

	for distro in $distro_files; do
		[[ ! -f "$distro" ]] && continue
		
		verbose "Checking for disc distro \"$distro\"..."

		. "$distro" > /dev/null

		[[ "$rd_serial" != "$distro_serial" ]] && continue

		distro_file="$distro"
		release_name="$rd_release $distro_version"
		distro_ncds=${#distro_cdorder[@]}

		return 0
	done

	return 1
}

#
# Iterate through the install media to install the miniroot and full zone
#
# The install media may be physical discs, a lofi mounted ISO file, or
# iso files located in a directory specified by the user.
#
# All installations, regardless of media type, use a CD as their basic media
# unit.  DVDs or ISOs representing DVDs actually contain multiple "CDs" of
# installation packages.
#
# The variable "distro_ncds," as set elsewhere, represents the number
# of CDs required to install the distribution.  Whether the installation
# actually requires multiple physical discs or ISOs depends upon their content.
#
# Returns 0 on success, 1 on failure.
#
iterate_media()
{
	typeset cdnum=1
	typeset cds
	typeset disc_rpms
	typeset err_media
	typeset err_msg
	typeset install_type="$1"
	typeset ldevs
	typeset mountdev
	typeset rh_pers

	shift

	if [[ "$install_type" = "miniroot" ]]; then
		typeset i

		disc_rpms=$distro_miniroot_rpms
		err_msg="$mini_mediafail"

		# For miniroot installs, ask for CDs in numerical order
		cds[0]="zero_pad"

		for i in ${distro_cdorder[@]}; do
			cds[$cdnum]=$cdnum
			((cdnum += 1))
		done

		cdnum=1
	else
		disc_rpms=$distro_rpms
		err_msg="$zone_mediafail"

		#
		# For full zone installs, ask for CDs in the order RPM needs
		# to find the packages.
		#
		set -A cds "zero_pad" ${distro_cdorder[@]}
	fi

	if [[ "$install_media" = "ISO" ]]; then
		set -A ldevs "zero_pad" "$@"
	else
		mountdev="$1"
		err_media="$release_name, CD ${cds[$cdnum]} (or DVD)"
	fi

	unset rpms_left_save

	while ((cdnum <= distro_ncds)); do
		[[ -z ${cds[$cdnum]} ]] && ((cdnum += 1)) && continue

		if [[ "$install_media" = "ISO" ]]; then
			typeset isonum="${cds[$cdnum]}"

			#
			# If this routine was called with a single ISO device
			# name, it must be a DVD, so refer to that one lofi
			# device (and associated ISO pathname)
			#
			[[ $# -eq 1 ]] && isonum=1

			err_media="ISO \"${iso_pathnames[$isonum]}\""
			mountdev="${ldevs[$isonum]}"
		fi

		#
		# If the disc needed in the install order isn't the one in
		# the drive, ask for the correct one.
		#
		if ! get_cd "$mountdev" "${cds[$cdnum]}"; then
			screenlog "$err_msg" "$zonename" "$err_media"
			return 1
		fi

		# set the RedHat personality type, if applicable
		[[ -n $rd_pers && -z $rh_pers ]] && rh_pers=$rd_pers

		#
		# We now know the actual type of media being used, so
		# modify the "err_media" string accordingly.
		#
		if [[ "$install_media" = "disc" ]]; then
			if [[ "$rd_disctype" = "DVD" ]]; then
				err_media="$release_name DVD"
			else
				err_media="$release_name, CD ${cds[$cdnum]}"
			fi
		fi

		find_packages "$mntdir" $disc_rpms

		#
		# Save a copy of $rpms_left.  Other functions clobber it.
		#
		rpms_left_save="${rpms_left[@]}"

		if [[ -n $rpms_found ]]; then
			if [[ "$install_type" = "miniroot" ]]; then
				verboselog "\nInstalling miniroot from"
				verboselog "  $err_media...\n" 

				if ! install_miniroot "$mntdir" \
				    "${rpms_found[@]}"; then
					screenlog "$err_msg" "$zonename" \
						"$err_media"
					return 1
				fi
			else
				screenlog "\n$install_msg\n" "$zonename" \
				    "$err_media"

				if ! install_zone "$mntdir" \
				    ${rpms_found[@]}; then
					screenlog "$err_msg" "$zonename" \
					    "$err_media"
					return 1
				fi
			fi

			#
			# Mark installation from this CD (or ISO representing
			# this CD) as completed.
			#
			if [[ "$rd_disctype" = "CD" ]]; then
				unset cds[$cdnum]
			fi
		fi

		# A DVD install takes a single disc, so stop iterating
		[[ "$rd_disctype" = "DVD" ]] && break

		# If there are no RPMs left, we're done.
		[[ -z $rpms_left_save ]] && break

		disc_rpms="$rpms_left_save"
		((cdnum += 1))

		if [[ "$install_media" != "ISO" ]]; then
			#
			# modify the err_media variable to reflect the next
			# CD in the sequence
			#
			err_media="$release_name, CD ${cds[$cdnum]}"
		else
			# Unmount the last used ISO if appropriate
			if [[ -n $zone_mounted ]]; then
				umount "$zone_mounted"
				unset zone_mounted
			fi
		fi
	done

	if [[ -n $zone_mounted ]]; then
		umount "$zone_mounted"
		unset zone_mounted
	fi

	if [[ -n $rpms_left_save ]]; then
		#
		# Uh oh - there were RPMS we couldn't locate.  This COULD
		# indicate a failed installation, but we need to check for
		# a RedHat personality "missing" list first.
		#
		if [[ -n $rh_pers && "$rh_pers" != "AS" ]]; then
			typeset missing

			if [[ $rh_pers = "WS" ]]; then
				missing="$distro_WS_missing"
			elif [[ $rh_pers = "ES" ]]; then
				missing="$distro_ES_missing"
			fi

			#
			# If any packages left in "rpm_left_save" appear in the
			# list of packages expected to be missing from this
			# personality, remove them from the "rpm_left_save"
			# list.
			#
			if [[ -n $missing ]]; then
				typeset pkg

				for pkg in $missing
				do
					rpm_left_save=$(echo "$rpm_left_save " |
					    sed "s/$pkg //g")

					#
					# If all of the packages in
					# "rpm_left_save" appeared in this
					# personality's list of "expected
					# missing" packages, then the
					# installation completed successfully.
					#
					[[ -z ${rpm_left_save%%+( )} ]] &&
					    return 0
				done
			fi
		fi

		log "\nERROR: Unable to locate some needed packages:\n" \
		    "  ${rpms_left_save%%+( )}\n"
		screenlog "$err_msg" "$zonename"
		return 1
	fi

	return 0
}

#
# Install a zone from installation media
#
# Returns 0 on success, 1 on failure
#
install_from_media()
{
	msg=$(gettext "Installing miniroot for zone '%s'.")
	screenlog "$msg" "$zonename"

	iterate_media "miniroot" $@ || return 1

	if ! setup_miniroot; then
		screenlog "$mini_setfail" "$zonename"
		return 1
	fi

	msg=$(gettext "Performing full install for zone '%s'.")

	screenlog "\n$msg" "$zonename"

	iterate_media "full" $@ || return 1

	#
	# Attempt to install deferred RPMS, if any
	#
	if [[ -n $deferred_rpms ]]; then
		if ! install_zone ""; then
			return 1
		fi
	fi

	finish_install
	return $?
}

#
# Add an entry to the valid distro list.
#
# The passed argument is the ISO type ("CD Set" or "DVD")
#
add_to_distro_list()
{
	typeset name

	distro_file[${#distro_file[@]}]="$distro"

	name="$release_name"
	[[ -n $redhat_pers ]] && name="$name $redhat_pers"

	select_name[${#select_name[@]}]="$name ($1)"
	release[${#release[@]}]="$release_name"
	iso_set[${#iso_set[@]}]="${iso_names[@]}"
	verboselog "Distro \"$name\" ($1) found."
}

#
# Find out which distros we have ISO files to support
#
# Do this by cycling through the distro directory and reading each distro
# file in turn looking for:
#
#     1) The number of discs in a distribution
#     2) The serial number of the distribution
#     3) The name of the distribution
#
# Based on this, we can determine based on the ISO files available which
# distributions, if any, we have a complete set of files to support.
#
# The function returns the supported isos in the array "iso_set."
#
validate_iso_distros()
{
	typeset cd
	typeset disctype
	typeset index
	typeset iso
	typeset ncds
	typeset pers
	typeset pers_cd
	typeset pers_index
	typeset serial

	typeset distro_files="$(echo $distro_dir/*.distro)"
	typeset nisos=${#iso_filename[@]}

	unset distro_file
	unset iso_set
	unset release
	unset select_name

	if [[ "$distro_files" = "$distro_dir/*.distro" ]]; then
		msg=$(gettext "Unable to find any distro files!")
		screenlog "$msg"
		return
	fi

	for distro in $distro_files; do
		#
		# We're done if we've already processed all available ISO files
		# or if there were none in the first place.
		#
		((${#iso_filename[@]} == 0)) && break

		[[ ! -f $distro ]] && continue

		. "$distro" > /dev/null
		ncds=${#distro_cdorder[@]}

		unset iso_names
		unset pers
		unset pers_cd

		verbose "\nChecking ISOs against distro file \"$distro\"..."

		index=0

		while ((index < nisos)); do
			#
			# If the filename has been nulled out, it's already
			# been found as part of a distro, so continue to the
			# next one.
			#
			if [[ -z ${iso_filename[$index]} ]]; then
				((index += 1))
				continue
			fi

			iso="${iso_filename[$index]}"
			serial="${iso_serial[$index]}"
			release_name="${iso_release[$index]}"
			redhat_pers="${iso_pers[$index]}"

			verbose "  ISO \"$iso\":"

			#
			# If the serial number doesn't match that for
			# this distro, check other ISOs
			#
			if [[ "$serial" != "$distro_serial" ]]; then
				((index += 1))
				continue
			fi

			verbose "    Serial #$serial"
			verbose "    Release Name \"$release_name\""

			[[ -n ${iso_pers[$index]} ]] &&
			    verbose "    RedHat Personality \"$redhat_pers\""

			if [[ "${iso_disctype[$index]}" = "CD" ]]; then
				disctype="CD #"
				cd="${iso_cdnum[$index]}"
			else
				disctype="DVD, representing CDs #"
				cd=0
			fi

			verbose "    ${disctype}${iso_cdnum[$index]}\n"

			#
			# Once we've matched a particular distro, don't check
			# this ISO to see if it's part of any other.
			#
			unset iso_filename[$index]

			iso_names[$cd]="$iso"

			#
			# A DVD-based distro consists of one and ONLY one disc,
			# so process it now.
			#
			if [[ "${iso_disctype[$index]}" = "DVD" ]]; then
				typeset dvd_discs=",${iso_cdnum[$index]}"

				cd=1 
				while ((cd <= ncds)); do
					dvd_discs=$(echo "$dvd_discs" |
					    sed "s/,$cd//")
					((cd += 1))
				done

				#
				# If no CDs are left in $dvd_discs, the DVD
				# was a complete distribution, so add it to
				# the valid distro list.
				#
				if [[ -z $dvd_discs ]]; then
					add_to_distro_list "DVD"
					unset iso_names[$cd]
				fi
			elif [[ -n ${iso_pers[$index]} ]]; then
				#
				# If this is a RedHat personality CD, save off
				# some extra information about it so we can
				# discern between mutiple personality discs
				# later, if needed.
				#
				pers[${#pers[@]}]=${iso_pers[$index]}
				pers_cd[${#pers_cd[@]}]="$iso"
			fi

			((index += 1))
		done

		#
		# Check to see if we have ISOs representing a full CD set.
		# If we don't, don't mark this as an available distro.
		#
		(( ${#iso_names[@]} != $ncds )) && continue

		relase_name="$release_name $distro_version"
		
		if [[ -z ${pers[@]} ]]; then
			#
			# If there were no personality discs, just add this
			# ISO set to the distro list.
			#
			unset redhat_pers
			add_to_distro_list "CD Set"
		else
			#
			# If a valid CD-based distro was found and there are
			# RedHat personality discs for that distro present,
			# create entries for each personality in the available
			# distro list.
			#
			pers_index=0

			while ((pers_index < ${#pers[@]})); do
				redhat_pers=${pers[$pers_index]}

				if [[ -n ${pers_cd[$pers_index]} ]]; then
					#
					# RedHat personality discs are always
					# disc 1 of a CD set, so if we found a
					# valid personality disc for this set,
					# set the disc 1 entry for this distro
					# to the ISO for the proper personality
					# disc.
					#
					iso_names[1]="${pers_cd[$pers_index]}"
					add_to_distro_list "CD Set"
				fi

				((pers_index += 1))
			done
		fi
	done
}

#
# Do a lofi add for the passed filename and set lofi_dev to the lofi
# device name lofiadm created for it (e.g. "/dev/lofi/1".)
#
# If the passed filename already has a lofi device name, simply set lofi_dir
# to the existing device name.
#
# Returns 0 on success, 1 on failure.
#
lofi_add()
{
	typeset filename="$1"

	lofi_dev=$(lofiadm "$filename" 2>/dev/null) && return 0
	lofi_dev=$(lofiadm -a "$filename") && return 0

	screenlog "$lofi_failed" "$filename"
	return 1
}

#
# Delete the lofi device name passed in.
#
# Returns 0 on success, 1 on failure.
#
lofi_del()
{
	typeset dev="$1"

	[[ "$dev" != /dev/lofi/* ]] && return 1

	if lofiadm -d "$dev" 2>/dev/null; then
		[[ -n $lofi_dev ]] && unset lofi_dev
		return 0
	fi

	return 1 
}

#
# Mount the lofi device name passed in.
#
# Set the variable mntdir to the directory on which the lofi device is
# mounted.
#
# Returns 0 on success, 1 on failure.
#
lofi_mount()
{
	typeset lofidev="$1"
	typeset mntpoint="$2"

	#
	# Check to see if the lofi device is already mounted and return
	# the existing mount point if it is.
	#
	get_mountdir "$lofidev" && { mntdir="$mount_dir" ; return 0 ; }

	unset mntdir
	if [[ ! -d  "$mntpoint" ]]; then
		if ! mkdir -p "$mntpoint"; then
			log "Could not create mountpoint \"$mntpoint\"!\n"
			return 1
		fi
		lofi_created="$mntpoint"
	fi

	verbose "Attempting mount of device \"$lofidev\""
	verbose "  on directory \"$mntpoint\"... \c"

	if ! mount -F hsfs -r "$lofidev" "$mntpoint" 2>/dev/null; then
		verbose "FAILED."
		[[ -n $lofi_created ]] && rmdir -ps "$lofi_created" &&
		    unset lofi_created
		return 1
	fi

	mntdir="$mntpoint"
	verbose "succeeded."
	return 0
}

#
# Unmount the lofi device name passed in, and remove the device mount point
# after unmounting the device.
#
# Returns 0 on success, 1 on failure.
#
lofi_umount()
{
	typeset mntdev="$1"

	#
	# If the directory name passed wasn't mounted to begin with,
	# just return success.
	#
	get_mountdir "$mntdev" || return 0

	verbose "Unmounting device \"$mntdev\"... \c"

        if ! umount "$mntdev" ; then
		verbose "FAILED."
		return 1
	fi

	verbose "succeeded."
	return 0
}

# Scan the passed list of ISOs.
scan_isos()
{
	typeset iso
	typeset index=0

	unset iso_serial
	unset iso_release
	unset iso_cdnum
	unset iso_disctype
	unset iso_filename
	unset iso_pers

	for iso in "$@"; do
		verbose "Checking possible ISO\n  \"$iso\"..."

		if lofi_add "$iso"; then
			verbose "  added as lofi device \"$lofi_dev\""
			if lofi_mount "$lofi_dev" "/tmp/lxiso"; then
				if read_discinfo "$mntdir"; then
					iso_release[$index]="$rd_release"
					iso_serial[$index]="$rd_serial"
					iso_cdnum[$index]="$rd_cdnum"
					iso_disctype[$index]="$rd_disctype"

					[[ -n $rd_pers ]] &&
					    iso_pers[$index]="$rd_pers"

					iso_filename[$index]="$iso"
					((index += 1))
				fi
				lofi_umount "$lofi_dev"
			else
				verbose "  not a usable ISO image."
				log "Unable to mount \"$lofi_dev\" (\"$iso\")"
			fi

			lofi_del "$lofi_dev"
		else
			verbose "  not a valid ISO image."
		fi
	done
}

#
# Prompt the user with the first argument, then make a menu selection
# from the balance.
#
# This is effectively similar to the ksh "select" function, except it
# outputs to stdout.
#
# Shell variables set:
#    choice    - set to the menu number selected
#    selection - set to the menu text selected
#
pick_one()
{
	typeset menu_items
	typeset menu_index
	typeset reply

	typeset prompt="$1"
	shift

	unset choice

	set -A menu_items "$@"

	until [[ -n $choice ]]; do
		menu_index=1
		
		echo "\n$prompt\n"

		for f in "${menu_items[@]}"; do
			echo "$menu_index) $f"
			((menu_index += 1))
		done

		echo "\n$(gettext "Please select") (1-$#): " "\c"
		read reply
		echo

		[[ -z $reply ]] && echo && continue

		#
		# Reprint menu selections if the answer was not a number in
		# range of the menu items available
		#
		[[ $reply != +([0-9]) ]] && continue
		((reply < 1)) || ((reply > $#)) && continue

		choice=$reply
		selection=${menu_items[((choice - 1))]}
	done
}

#
# Select a distribution to install from the arguments passed and set
# "ndsitro" to the value chosen - 1 (so it may be used as an array index.)
#
# The routine will automatically return with ndisto set to 0 if only one
# argument is passed.
#
select_distro()
{
	unset choice
	unset ndistro

	if (($# > 1)); then
		if [[ -n $silent_mode ]]; then
			typeset dist

			log "ERROR: multiple distrubutions present in ISO" \
				"directory but silent install"
			log "  mode specified.  Distros available:"
			for dist in "$@"; do
				log "    \"$dist\""
			done
			return 1
		fi

		pick_one \
		    "$(gettext "Which distro would you like to install?")" \
		    "$@"
	fi

	#
	# Covers both the cases of when only one distro name is passed
	# to the routine as well as when an EOF is sent to the distribution
	# selection prompt.
	#
	if [[ -z $choice ]]; then
		screenlog "$install_dist" "$1"
		ndistro=0
	else
		screenlog "$install_dist" "$selection"
		ndistro=$((choice - 1))
	fi

	return 0
}

#
# Install a zone from discs or manually lofi-mounted ISOs.
#
# Return 0 on success, 1 on failure
#
do_disc_install()
{
	typeset path="$1"

	typeset eject_final="N"
	typeset install_status

	#
	# Get a disc, it doesn't matter which one.
	#
	# We don't know which distro this may be yet, so we can't yet
	# ask for the first disc in the install order.
	#
	if ! get_cd "$path"; then
		if [[ -z $silent_mode ]]; then
			typeset distro_disc=\
			    $(gettext "a supported Linux distribution disc")

			screenlog "\n$distro_mediafail" "$distro_disc ($path)"
		fi
		return 1
	fi

	if [[ -n $silent_mode && "$rd_disctype" = "CD" ]]; then
		log "$silent_err_msg"
		return 1
	fi

	if ! get_disc_distro "$mntdir"; then
		msg=$(gettext "Unable to find a supported Linux release on")
		screenlog "$msg"
		screenlog "  $media_spec" "$path"
		umount "$mntdir" > /dev/null 2>&1
		return 1
	fi

	check_mbfree $zoneroot $distro_mb_required || return 1
	build_rpm_list $install_packages

	echo

	if [[ "$install_media" = "disc" ]]; then
		#
		# If we're in interactive mode, ask the user if they want the
		# disc ejected when the installation is complete.
		#
		# Silent mode installs will require the user to manually run
		# eject(1).
		#
		if [[ -n $removable && -z $silent_mode ]]; then
			typeset ans
			typeset disc
			typeset status
			typeset which=""

			disc="$rd_disctype"
			[[ "$disc" = "CD" ]] && which=$(gettext "final ")

			#
			# Ask the user if they want the install disc ejected
			# when the installation is complete.  Any answer but
			# "n" or "N" is taken to mean yes, eject it.
			#
			eject_final="Y"
			status=$(gettext "WILL")

			screenlog "$eject_final_msg" "$which" "$disc"
			screenlog "  $eject_final_prompt" "$zonename" "[y]/n"

			read ans && [[ "$ans" = [Nn]* ]] && eject_final="N" &&
			    status=$(gettext "will NOT")

			screenlog "\n$eject_final_status\n" "$which" "$disc" \
			    "$status"
		fi

		screenlog "$install_ndiscs" "$distro_ncds"

		msg=$(gettext "install %s.")
		screenlog "$msg" "$release_name"
	else
		screenlog "$install_nisos" "$distro_ncds"

		msg=$(gettext "DVD) to install %s.")
		screenlog "$msg" "$release_name"
	fi

	install_from_media "$path"
	install_status=$?

	[[ "$eject_final" = "Y" ]] && eject_removable_disc

	return $install_status
}

#
# Install a zone using the list of ISO files passed as arguments to this
# function.
#
# Return 0 on success, 1 on failure.
#
do_iso_install()
{
	typeset install_status
	typeset iso_path
	typeset ldev

	msg=$(gettext "Checking for valid Linux distribution ISO images...")
	screenlog "\n$msg"

	scan_isos "$@"

	if [[ -z ${iso_filename[@]} ]]; then
		msg=$(gettext "No valid ISO images available or mountable.")
		screenlog "\n$msg"
		return 1
	fi
	
	validate_iso_distros

	if [[ -z ${release[@]} ]]; then
		msg=$(gettext "No supported Linux distributions found.")
		screenlog "\n$msg"
		return 1
	fi

	select_distro "${select_name[@]}" || return 1
	unset select_name

	. ${distro_file[$ndistro]} > /dev/null
	distro_ncds=${#distro_cdorder[@]}

	check_mbfree $zoneroot $distro_mb_required || return 1
	build_rpm_list $install_packages

	unset lofi_devs

	verboselog ""
	for iso_path in ${iso_set[$ndistro]}; do
		if ! lofi_add "$iso_path"; then
			for ldev in $lofi_devs; do
				lofi_del "$ldev"
			done
			return 1
		fi

		verboselog "Added \"$iso_path\""
		verboselog "  as \"$lofi_dev\""
		lofi_devs="$lofi_devs $lofi_dev"
	done

	release_name="${release[$ndistro]}"

	set -A iso_pathnames "zero_pad" ${iso_set[$ndistro]}
	install_from_media $lofi_devs
	install_status=$?

	for ldev in $lofi_devs; do
		lofi_del "$ldev"
	done

	unset lofi_devs
	return $install_status
}

# Clean up on interrupt
trap_cleanup()
{
	cd "$cwd"

	msg=$(gettext "Interrupt received, cleaning up partial install...")
	screenlog "$msg"

	[[ -n $miniroot_booted ]] && zoneadm -z "$zonename" halt &&
	    unset miniroot_booted && unset newroot_mounted

	#
	# OK, why a sync here?  Because certain commands may have written data
	# to mounted file systems before the interrupt, and given just the right
	# timing there may be buffered data not yet sent to the disk or the
	# system may still be writing data to the disk.  Either way, the umount
	# will then fail because the system will still see the mounted
	# filesystems as busy.
	#
	sync

	if [[ -n $newroot_mounted ]]; then
		umount_list $newroot_mounted
		unset newroot_mounted
	fi

	if [[ -n $zone_mounted ]]; then
		umount "$zone_mounted"
		unset zone_mounted
	fi

	#
	# Normally, this isn't needed but there is a window where mntdir is set
	# before zone_mounted, so account for that case.
	#
	if [[ -n $mntdir ]]; then
		umount "$mntdir"
		unset mntdir
	fi

	[[ -n $lofi_dev ]] && lofi_del "$lofi_dev"

	if [[ -n $lofi_devs ]]; then
		typeset ldev

		for ldev in $lofi_devs
		do
			lofi_del "$ldev"
		done

		unset lofi_devs
	fi

	[[ -n $lofi_created ]] && rmdir -ps "$lofi_created" &&
	    unset lofi_created

	msg=$(gettext "Installation aborted.")
	screenlog "$msg"
	exit $ZONE_SUBPROC_FATAL
}

#
# Start of main script
#
cwd=$(dirname "$0")
distro_dir="$cwd/distros"

unset deferred_saved
unset distro_path
unset logfile
unset msg
unset newroot_mounted
unset silent_err_msg
unset silent_mode
unset verbose_mode
unset zone_mounted
unset zoneroot
unset zonename

#
# Exit values used by the script, as #defined in <sys/zone.h>
#
#	ZONE_SUBPROC_OK
#	===============
#	Installation was successful
#
#	ZONE_SUBPROC_USAGE
#	==================
#	Improper arguments were passed, so print a usage message before exiting
#
#	ZONE_SUBPROC_NOTCOMPLETE
#	========================
#	Installation did not complete, but another installation attempt can be
#	made without an uninstall
#
#	ZONE_SUBPROC_FATAL
#	==================
#	Installation failed and an uninstall will be required before another
#	install can be attempted
#
ZONE_SUBPROC_OK=0
ZONE_SUBPROC_USAGE=253
ZONE_SUBPROC_NOTCOMPLETE=254
ZONE_SUBPROC_FATAL=255

#
# Process and set up various global option variables:
#
#    distro_path - Path containing files that make up the distribution
#                  (e.g. a directory containing ISO files or a disc device)
#    logfile     - Name (if any) of the install log file
#    zoneroot    - Root directory for the zone to install
#    zonename    - Name of the zone to install
#
while getopts 'svxd:l:r:z:' opt; do
	case $opt in
		s) silent_mode=1; unset verbose_mode;;
		v) verbose_mode=1; unset silent_mode;;
		x) set -x;;
		d) distro_path="$OPTARG";;
		l) logfile="$OPTARG";;
		r) zoneroot="$OPTARG";;
		z) zonename="$OPTARG";;
	esac
done
shift OPTIND-1

distro_path=${distro_path:=/cdrom/cdrom0}

install_packages="$@"

[[ -n $silent_mode ]] && exec 1>/dev/null

if [[ -z $zonename ]]; then
	msg=$(gettext "ERROR:  Cannot install - no zone name was specified")
	screenlog "$msg"
	echo
	exit $ZONE_SUBPROC_NOTCOMPLETE
fi

if [[ -z $zoneroot ]]; then
	msg=$(gettext "ERROR:  Cannot install - no zone root directory was")
	screenlog "$msg"

	msg=$(gettext "specified.")
	screenlog "  $msg"
	echo
	exit $ZONE_SUBPROC_NOTCOMPLETE
fi

# Make sure the specified zone root directory exists
[[ -d "$zoneroot" ]] || mkdir -m 0700 -p "$zoneroot"

if [[ ! -d "$zoneroot" ]]; then
	screenlog "$zone_rootfail" "$zoneroot"
	echo
	exit $ZONE_SUBPROC_NOTCOMPLETE
fi

rootdir="$zoneroot/root"

# Make sure the specified zone root subdirectory exists
[[ -d "$rootdir" ]] || mkdir -p "$rootdir"

if [[ ! -d "$rootdir" ]]; then
	screenlog "$zone_rootsub" "$rootdir"
	echo
	exit $ZONE_SUBPROC_NOTCOMPLETE
fi

media_mntdir="$rootdir/media"

if [[ -n $logfile ]]; then
	# If a log file was specified, log information regarding the install
	log "\nInstallation started `date`" 
	log "Installing from path \"$distro_path\""
else
	# Redirect stderr to /dev/null if silent mode is specified.
	[[ -n $silent_mode ]] && exec 2>/dev/null
fi

distro_path=${distro_path:=$default_distro_path}

# From this point on, call trap_cleanup() on interrupt (^C)
trap trap_cleanup INT

verbose "Installing zone \"$zonename\" at root \"$zoneroot\""
release_name="supported Linux distribution"

#
# Based on the pathname, attempt to determine whether this will be a disc or
# lofi-based install or one using ISOs.
#
if [[ "$distro_path" = /cdrom/* || "$distro_path" = /media/* ||
    "$distro_path" = /dev/dsk/* || "$distro_path" = /dev/lofi/* ]]; then
	if [[ "$distro_path" = /dev/lofi/* ]]; then
		silent_err_msg="$silent_nolofi"
		install_media="lofi"
	else
		silent_err_msg="$silent_nodisc"
		install_media="disc"
	fi

	if [[ "$distro_path" = /cdrom/* || "$distro_path" = /media/* ]]; then
		managed_removable=1
	else
		managed_removable=0
	fi

	log "Installing zone \"$zonename\" at root \"$zoneroot\""
	verboselog "  Attempting ${install_media}-based install via:"
	verboselog "    \"$distro_path\""

	do_disc_install "$distro_path"
else
	typeset dir_start
	typeset dir_file

	dir_start=$(dirname "$distro_path" | cut -c 1)

	[[ "$dir_start" != "/" ]] && distro_path="${PWD:=$(pwd)}/$distro_path"

	if [[ ! -d "$distro_path" ]]; then
		screenlog "$no_distropath" "$distro_path"
		echo
		exit $ZONE_SUBPROC_NOTCOMPLETE
	fi

	log "Installing zone \"$zonename\" at root \"$zoneroot\""
	verboselog "  Attempting ISO-based install from directory:"
	verboselog "    \"$distro_path\""

	unset iso_files

	for dir_file in $distro_path/*; do
		#
		# Skip this file if it's not a regular file or isn't readable
		#
		[[ ! -f $dir_file || ! -r $dir_file ]] && continue

		#
		# If it's an hsfs file, it's an ISO, so add it to the possible
		# distro ISO list
		#
		filetype=$(LC_ALL=C fstyp $dir_file 2>/dev/null) &&
		    [[ "$filetype" = "hsfs" ]] &&
		    iso_files="$iso_files $dir_file"
	done

	install_media="ISO"
	do_iso_install $iso_files
fi

if [[ $? -ne 0 ]]; then
	cd "$cwd"

	[[ -n $miniroot_booted ]] && zoneadm -z "$zonename" halt &&
	    unset miniroot_booted && unset newroot_mounted

	if [[ -n $zone_mounted ]]; then
		umount "$zone_mounted"
		unset zone_mounted
	fi

	if [[ -n $newroot_mounted ]]; then
		umount_list $newroot_mounted
		unset newroot_mounted
	fi

	screenlog "\n$install_failed\n" "$release_name" "$zonename" "`date`"

	msg=$(gettext "Cleaning up after failed install...")
	screenlog "$msg"

	#
	# The extra checks are some basic paranoia due to the potentially
	# dangerous nature of these commands but are not intended to catch all
	# malicious cases.
	#
	[[ -d "$zoneroot/a" ]] && rm -rf "$zoneroot/a"

	exit $ZONE_SUBPROC_FATAL
fi

screenlog "$install_done" "$release_name" "$zonename" "`date`"

exit $ZONE_SUBPROC_OK