lx_distro_install.ksh revision 65488c97aeb108aeffd7b61db3b2b3bcb4fc9d72
#
# 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
# 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.
#
export PATH
# Setup i18n output
TEXTDOMAIN="SUNW_OST_OSCMD"
export TEXTDOMAIN
# Log passed arguments to file descriptor 2
log()
{
}
#
# Send the provided printf()-style arguments to the screen and to the
# logfile.
#
{
typeset fmt="$1"
shift
}
# Print and log provided text if the shell variable "verbose_mode" is set
verbose()
{
[[ -n $verbose_mode ]] && echo "$@"
}
#
# Print to the screen if the shell variable "verbose_mode" is set, but always
# send the output to the log.
#
{
[[ -n $verbose_mode ]] && echo "$@"
}
#
# 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.
#
{
typeset mount_dir="$1"
typeset device
unset mount_dev
#
# Obtain information on the specified mounted device.
#
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.
#
{
typeset mount_dev="$1"
typeset dir
unset mount_dir
#
# Obtain information on the specified mounted device.
#
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.
#
{
typeset dir="$1"
typeset mb_required=$2
#
# Return free space in partition containing passed argument in MB
#
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:
#
#
# 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.
#
{
typeset found=0
typeset left=0
typeset rpmdir="$1/$rd_rpmdir"
typeset arch
typeset procinfo
typeset rpmglob
typeset rpmfile
unset rpms_found
unset rpms_left
shift
cd "$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
[[ $? -eq 0 ]] &&
[[ -n $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
#
# Check for additional processor capabilities
#
#
# Linux gives "athlon" packages precedence
# over "i686" packages, so duplicate that
# here.
#
archs="athlon i686"
else
archs="i686"
fi
fi
fi
verboselog "RPM source directory:\n \"$rpmdir\"\n"
if [[ $# -eq 1 ]]; then
else
screenlog "$locate_npkgs" "$#"
fi
#
# 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:
#
#
#
# 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.
#
unset rpmfile
done
if [[ -z $rpmfile ]]; then
else
fi
done
cd "$curdir"
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.
#
{
# Default to a desktop installation
typeset cnt=0
typeset pkgs
break;;
*) screenlog "$unknown_clust" "$clust"
exit $ZONE_SUBPROC_USAGE ;;
esac
done
exit $ZONE_SUBPROC_USAGE
fi
esac
# The RPMs in the miniroot must all be installed properly as well
}
#
# 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>
#
#
{
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
else
fi
" \"$zoneroot\"..."
if [[ $? -ne 0 ]]; then
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.
#
{
#
# 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
return 1
fi
deferred_found="${rpms_found[@]}"
numdeferred=${#rpms_found[@]}
else
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
verboselog "Deferring installation of \"$rpm\""
#
# Remove the RPM from the install_rpms list
# and append it to the deferred_saved array
#
# remove trailing spaces, if any
deferred_saved[${#deferred_saved[@]}]="$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[@]}
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 ""
else
#
# 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 " 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.
#
rpmerr=$?
log ""
log ""
return 1
fi
log ""
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
#
{
typeset failures=0
typeset mounted
unset umount_failures
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.
#
{
typeset dev
typeset mounted
typeset target
unset newroot_mounted
#
# /usr and /lib get lofs mounted in the zone on /native read-only
#
# the use of native devices.
#
unset newroot_mounted
return 1
fi
unset newroot_mounted
return 1
fi
#
# This is a bit ugly; to provide device access within the chrooted
# environment RPM will use for its install, we will create the same
# /dev in the new filesystem we're installing to.
#
do
return 1
fi
#
# 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
#
# 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 }"
unset newroot_mounted
return 1
fi
else
screenlog "$lofs_failed" "$dev" "$target"
unset newroot_mounted
return 1
fi
fi
done
return 0
}
#
# Replace the root directory of a zone with the duplicate previously created
# in the zone's /a directory.
#
{
#
# 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.
#
return 1
fi
unset miniroot_booted
unset newroot_mounted
#
# 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.
#
#
# 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.
#
#
# Remove the contents of the /dev directory created by the install.
#
# We don't technically need to do this, but the zone infrastructure
# 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
#
return 0
}
{
unset miniroot_booted
return 1
fi
if ! copy_miniroot; then
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.
#
return 1
fi
#
# 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
if [[ -n $newroot_mounted ]]; then
unset newroot_mounted
fi
return 1
fi
#
# Attempt to initialize the RPM database for the new zone
#
return 1
fi
return 0
}
{
#
# 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
if [[ -d $defdir ]]; then
fi
# Run ldconfig in the new root
/sbin/ldconfig -f /etc/ld.so.conf
#
# exist
#
#
# Make sure all init.d and rc[0-6].d links are set up properly.
#
done
rmdir -ps "$media_mntdir"
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.
#
{
#
# Create the directory $zoneroot/a if it doesn't already exist
#
#
# lxsave_ files.
#
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.
#
{
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.
#
[[ $# -eq 1 ]] &&
return 1
fi
return 1
fi
typeset rd_line
typeset linenum=0
#
# If .discinfo architecture isn't "i386," fail here as
# we only support i386 distros at this time.
#
"${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.
#
rd_release=${rd_line[1]}
# CentOS names their releases "final"
#
# 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_disctype="DVD"
else
rd_disctype="CD"
fi
#
# 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.
#
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
# 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"
#
# An appropriate file was found, so
# extract the personality type from the
# filename.
#
# For example, the presence of the file:
#
#
# 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]"
#
# 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
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.
#
# 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
#
{
typeset device="$1"
typeset mount_err
unset removable
unset zone_mounted
[[ -z $mntdir ]] && return 1
unset mntdir
return 1
fi
#
# 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
#
# 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.
#
done
screenlog "\n$unknown_media" "$device"
return 2
fi
mount_err=$?
else
#
# Attempt to mount the media manually.
#
# First, make sure the passed device name really IS a device.
#
#
# Now check to see if the device is already mounted and lofi
# mount the existing mount point into the zone if it is.
#
mount -F lofs -r "$mount_dir" "$mntdir"
mount_err=$?
else
# It wasn't mounted, so go ahead and try to do so.
mount_err=$?
fi
# A mount_err of 33 means no suitable media was found
fi
screenlog "$mountfail" "$device" "$mntdir"
unset mntdir
return 1
fi
return 0
}
# Eject the disc mounted on the passed directory name
{
screenlog ""
verbose " (Attempting to eject '$removable'... \c"
if [[ -n $zone_mounted ]]; then
unset zone_mounted
fi
verbose "failed.)\n"
else
verbose "done.)\n"
fi
unset removable
}
#
# Ask for the user to provide a disc or ISO.
#
# Returns 0 on success, 1 on failure.
#
{
# No prompting is allowed in silent mode.
if [[ -n $silent_mode ]]; then
return 1
fi
if [[ "$1" != "" ]]; then
msg="$release_name, CD $1"
else
fi
[[ -n $removable ]] && eject_removable_disc
else
if [[ -n $zone_mounted ]]; then
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.
#
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
unset rd_disctype
mount_err=$?
#
# If the mount succeeded, continue on in the main
# script
#
# No medium was found, so prompt for one.
unset mntdir
return 1
else
# mount failed
unset mntdir
return 1
fi
fi
if [[ -n $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.
#
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.
#
"Serial #$rd_serial"
verboselog "Release Name \"$rd_release\""
[[ -n $rd_pers ]] &&
"\"$rd_pers\""
verboselog ""
# If we didn't care which CD it was, return success
# Return if the CD number read is a match
else
"$rd_cdnum), Serial #$rd_serial"
verboselog "Release Name \"$rd_release\"\n"
[[ -n $rd_pers ]] &&
"\"$rd_pers\""
verboselog ""
# If we didn't care which CD it was, return success
#
# Since a DVD represents multiple CDs, make sure the
# DVD inserted represents the CD we want.
#
return 0
fi
if [[ -n $prompted ]]; then
else
" 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.
#
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.
#
{
typeset distro
unset distro_file
for distro in $distro_files; do
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.
#
{
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
typeset i
# For miniroot installs, ask for CDs in numerical order
cds[0]="zero_pad"
for i in ${distro_cdorder[@]}; do
done
cdnum=1
else
#
# For full zone installs, ask for CDs in the order RPM needs
# to find the packages.
#
fi
set -A ldevs "zero_pad" "$@"
else
mountdev="$1"
err_media="$release_name, CD ${cds[$cdnum]} (or DVD)"
fi
unset rpms_left_save
#
# 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)
#
fi
#
# If the disc needed in the install order isn't the one in
# the drive, ask for the correct one.
#
screenlog "$err_msg" "$zonename" "$err_media"
return 1
fi
# set the RedHat personality type, if applicable
#
# We now know the actual type of media being used, so
# modify the "err_media" string accordingly.
#
err_media="$release_name DVD"
else
err_media="$release_name, CD ${cds[$cdnum]}"
fi
fi
#
# Save a copy of $rpms_left. Other functions clobber it.
#
rpms_left_save="${rpms_left[@]}"
if [[ -n $rpms_found ]]; then
verboselog "\nInstalling miniroot from"
"${rpms_found[@]}"; then
return 1
fi
else
${rpms_found[@]}; then
return 1
fi
fi
#
# Mark installation from this CD (or ISO representing
# this CD) as completed.
#
fi
fi
# A DVD install takes a single disc, so stop iterating
# If there are no RPMs left, we're done.
[[ -z $rpms_left_save ]] && break
#
# 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
unset zone_mounted
fi
fi
done
if [[ -n $zone_mounted ]]; then
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.
#
typeset 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
do
#
# If all of the packages in
# "rpm_left_save" appeared in this
# personality's list of "expected
# missing" packages, then the
# installation completed successfully.
#
return 0
done
fi
fi
" ${rpms_left_save%%+( )}\n"
return 1
fi
return 0
}
#
# Install a zone from installation media
#
# Returns 0 on success, 1 on failure
#
{
if ! setup_miniroot; then
return 1
fi
#
# Attempt to install deferred RPMS, if any
#
if [[ -n $deferred_rpms ]]; then
return 1
fi
fi
return $?
}
#
# Add an entry to the valid distro list.
#
# The passed argument is the ISO type ("CD Set" or "DVD")
#
{
typeset name
distro_file[${#distro_file[@]}]="$distro"
[[ -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."
#
{
typeset cd
typeset disctype
typeset index
typeset iso
typeset ncds
typeset pers
typeset pers_cd
typeset pers_index
typeset serial
typeset nisos=${#iso_filename[@]}
unset distro_file
unset iso_set
unset release
unset select_name
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
ncds=${#distro_cdorder[@]}
unset iso_names
unset pers
unset pers_cd
index=0
#
# 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
continue
fi
release_name="${iso_release[$index]}"
redhat_pers="${iso_pers[$index]}"
#
# If the serial number doesn't match that for
# this distro, check other ISOs
#
continue
fi
verbose " Release Name \"$release_name\""
verbose " RedHat Personality \"$redhat_pers\""
disctype="CD #"
cd="${iso_cdnum[$index]}"
else
disctype="DVD, representing CDs #"
cd=0
fi
#
# 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]
#
# A DVD-based distro consists of one and ONLY one disc,
# so process it now.
#
cd=1
((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"
fi
#
# 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
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
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.
#
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
done
fi
done
}
#
# Do a lofi add for the passed filename and set lofi_dev to the lofi
#
# 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"
return 1
}
#
# Delete the lofi device name passed in.
#
# Returns 0 on success, 1 on failure.
#
lofi_del()
{
typeset dev="$1"
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.
#
{
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.
#
unset mntdir
return 1
fi
fi
verbose "FAILED."
unset lofi_created
return 1
fi
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.
#
{
typeset mntdev="$1"
#
# If the directory name passed wasn't mounted to begin with,
# just return success.
#
verbose "FAILED."
return 1
fi
verbose "succeeded."
return 0
}
# Scan the passed list of ISOs.
{
typeset iso
typeset index=0
unset iso_serial
unset iso_release
unset iso_cdnum
unset iso_disctype
unset iso_filename
unset iso_pers
[[ -n $rd_pers ]] &&
fi
else
verbose " not a usable ISO image."
fi
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
echo "\n$prompt\n"
echo "$menu_index) $f"
done
read reply
echo
[[ -z $reply ]] && echo && continue
#
# Reprint menu selections if the answer was not a number in
# range of the menu items available
#
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.
#
{
unset choice
unset ndistro
if (($# > 1)); then
if [[ -n $silent_mode ]]; then
typeset dist
"directory but silent install"
log " mode specified. Distros available:"
done
return 1
fi
pick_one \
"$@"
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
fi
return 0
}
#
# Install a zone from discs or manually lofi-mounted ISOs.
#
# Return 0 on success, 1 on failure
#
{
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 [[ -z $silent_mode ]]; then
typeset distro_disc=\
screenlog "\n$distro_mediafail" "$distro_disc ($path)"
fi
return 1
fi
return 1
fi
screenlog " $media_spec" "$path"
return 1
fi
check_mbfree $zoneroot $distro_mb_required || return 1
echo
#
# 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=""
#
# 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"
screenlog "$eject_final_msg" "$which" "$disc"
screenlog " $eject_final_prompt" "$zonename" "[y]/n"
"$status"
fi
else
fi
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.
#
{
typeset install_status
typeset iso_path
typeset ldev
scan_isos "$@"
if [[ -z ${iso_filename[@]} ]]; then
return 1
fi
if [[ -z ${release[@]} ]]; then
return 1
fi
unset select_name
. ${distro_file[$ndistro]} > /dev/null
distro_ncds=${#distro_cdorder[@]}
check_mbfree $zoneroot $distro_mb_required || return 1
unset lofi_devs
verboselog ""
for ldev in $lofi_devs; do
done
return 1
fi
verboselog "Added \"$iso_path\""
verboselog " as \"$lofi_dev\""
done
release_name="${release[$ndistro]}"
for ldev in $lofi_devs; do
done
unset lofi_devs
return $install_status
}
# Clean up on interrupt
{
cd "$cwd"
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.
#
if [[ -n $newroot_mounted ]]; then
unset newroot_mounted
fi
if [[ -n $zone_mounted ]]; then
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
unset mntdir
fi
if [[ -n $lofi_devs ]]; then
typeset ldev
for ldev in $lofi_devs
do
done
unset lofi_devs
fi
unset lofi_created
exit $ZONE_SUBPROC_FATAL
}
#
# Start of main script
#
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
#
#
# 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
#
#
# 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
#
s) silent_mode=1; unset verbose_mode;;
v) verbose_mode=1; unset silent_mode;;
x) set -x;;
esac
done
shift OPTIND-1
install_packages="$@"
[[ -n $silent_mode ]] && exec 1>/dev/null
if [[ -z $zonename ]]; then
echo
fi
if [[ -z $zoneroot ]]; then
echo
fi
# Make sure the specified zone root directory exists
echo
fi
# Make sure the specified zone root subdirectory exists
echo
fi
media_mntdir="$rootdir/media"
if [[ -n $logfile ]]; then
# If a log file was specified, log information regarding the install
log "Installing from path \"$distro_path\""
else
[[ -n $silent_mode ]] && exec 2>/dev/null
fi
# From this point on, call trap_cleanup() on interrupt (^C)
trap trap_cleanup INT
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.
#
install_media="lofi"
else
install_media="disc"
fi
else
fi
verboselog " Attempting ${install_media}-based install via:"
verboselog " \"$distro_path\""
else
typeset dir_start
typeset dir_file
echo
fi
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
#
#
# If it's an hsfs file, it's an ISO, so add it to the possible
# distro ISO list
#
done
install_media="ISO"
fi
if [[ $? -ne 0 ]]; then
cd "$cwd"
unset miniroot_booted && unset newroot_mounted
if [[ -n $zone_mounted ]]; then
unset zone_mounted
fi
if [[ -n $newroot_mounted ]]; then
unset newroot_mounted
fi
#
# 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.
#
exit $ZONE_SUBPROC_FATAL
fi
exit $ZONE_SUBPROC_OK