common.ksh revision ae1049320dd687abe67d8ed2ce29f4d478f64841
#
# 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.
#
#
# Send the error message to the screen and to the logfile.
#
error()
{
typeset fmt="$1"
shift
printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@"
}
fatal()
{
typeset fmt="$1"
shift
exit $EXIT_CODE
}
printf "ERROR: "
printf "$@"
printf "\n"
exit $ZONE_SUBPROC_FATAL
}
#
# Send the provided printf()-style arguments to the screen and to the logfile.
#
log()
{
typeset fmt="$1"
shift
printf "${MSG_PREFIX}${fmt}\n" "$@"
}
#
# Print provided text to the screen if the shell variable "OPT_V" is set.
# The text is always sent to the logfile.
#
vlog()
{
typeset fmt="$1"
shift
}
#
# Validate that the directory is safe.
#
# It is possible for a malicious zone root user to modify a zone's filesystem
# so that modifications made to the zone's filesystem by administrators in the
# global zone modify the global zone's filesystem. We can prevent this by
# ensuring that all components of paths accessed by scripts are real (i.e.,
# non-symlink) directories.
#
# NOTE: The specified path should be an absolute path as would be seen from
# within the zone. Also, this function does not check parent directories.
# If, for example, you need to ensure that every component of the path
#
# safe_dir /foo
#
safe_dir()
{
typeset dir="$1"
fi
}
# Like safe_dir except the dir doesn't have to exist.
{
typeset dir="$1"
fi
}
# Only make a copy if we haven't already done so.
{
typeset src="$1"
typeset dst="$2"
fi
}
# Make a copy even if the destination already exists.
{
typeset src="$1"
typeset dst="$2"
fi
}
# Move a file
{
typeset src="$1"
typeset dst="$2"
fi
}
safe_rm()
{
fi
}
#
# Replace the file with a wrapper pointing to the native brand code.
# However, we only do the replacement if the file hasn't already been
# replaced with our wrapper. This function expects the cwd to be the
# location of the file we're replacing.
#
# Some of the files we're replacing are hardlinks to isaexec so we need to 'rm'
# the file before we setup the wrapper while others are hardlinks to rc scripts
# that we need to maintain.
#
{
typeset filename="$1"
typeset runname="$2"
typeset mode="$3"
typeset own="$4"
typeset rem="$5"
return
fi
if [ $? -eq 0 ]; then
return
fi
fi
cat <<-END >$filename || exit 1
#!/bin/sh
#
# Solaris Brand Replacement
#
# Attention. This file has been replaced with a new version for
# use in a virtualized environment. Modification of this script is not
# supported and all changes will be lost upon reboot. The
# {name}.pre_p2v version of this file is a backup copy of the
# original and should not be deleted.
#
END
}
{
typeset filename="$1"
typeset runname="$2"
typeset mode="$3"
typeset own="$4"
if [ -f $filename ]; then
exit 1
fi
cat <<-END >$filename || exit 1
#!/bin/sh
#
# Solaris Brand Wrapper
#
# Attention. This file has been created for use in a
# virtualized environment. Modification of this script
# is not supported and all changes will be lost upon reboot.
#
END
}
#
# Read zonecfg ipd and fs entries and save the relevant data, one entry per
# line.
# This assumes the properties from the zonecfg output, e.g.:
# inherit-pkg-dir:
# dir: /usr
# fs:
# dir: /opt
# special: /opt
# raw not specified
# type: lofs
# options: [noexec,ro,noatime]
#
# and it assumes the order of the fs properties as above. This also saves the
# inherit-pkg-dir patterns into the ipd.{cpio|pax} temporary files for
# filtering while extracting the image into the zonepath. We have to save the
# IPD patterns in the appropriate format for filtering with the different
# archivers and we don't know what format we'll get until after the flash
# archive is unpacked.
#
{
nawk -v ipdcpiof=$ipdcpiofile -v ipdpaxf=$ipdpaxfile '{
if ($1 == "dir:") {
dir=$2;
printf("%s lofs %s ro\n", dir, dir);
if (substr(dir, 1, 1) == "/") {
printf("%s\n", substr(dir, 2)) >> ipdcpiof
printf("%s/*\n", substr(dir, 2)) >> ipdcpiof
} else {
printf("%s\n", dir) >> ipdcpiof
printf("%s/*\n", dir) >> ipdcpiof
}
if (substr(dir, 1, 1) == "/") {
printf("%s ", substr(dir, 2)) >> ipdpaxf
} else {
printf("%s ", dir) >> ipdpaxf
}
}
}' >> $fstmpfile
if ($1 == "options:") {
# Remove brackets.
options=substr($2, 2, length($2) - 2);
printf("%s %s %s %s\n", dir, type, special, options);
} else if ($1 == "dir:") {
dir=$2;
} else if ($1 == "special:") {
special=$2;
} else if ($1 == "type:") {
type=$2
}
}' >> $fstmpfile
}
#
# Mount zonecfg fs entries into the zonepath.
#
mnt_fs()
{
if [ ! -s $fstmpfile ]; then
return;
fi
# Sort the fs entries so we can handle nested mounts.
sort $fstmpfile | nawk -v zonepath=$zonepath '{
if (NF == 4)
options="-o " $4;
else
options=""
# Create the mount point. Ignore errors since we might have
# a nested mount with a pre-existing mount point.
system(cmd);
zonepath "/root" $1;
if (system(cmd) != 0) {
printf("command failed: %s\n", cmd);
exit 1;
}
}' >>$LOGFILE
}
#
# Unmount zonecfg fs entries from the zonepath.
#
umnt_fs()
{
if [ ! -s $fstmpfile ]; then
return;
fi
# Reverse sort the fs entries so we can handle nested unmounts.
sort -r $fstmpfile | nawk -v zonepath=$zonepath '{
if (system(cmd) != 0) {
printf("command failed: %s\n", cmd);
}
}' >>$LOGFILE
}
# Find the dataset mounted on the zonepath.
ZONEPATH_DS=`/usr/sbin/zfs list -H -t filesystem -o name,mountpoint | \
if ($2 == zonepath)
print $1
}'`
fi
}
#
# Perform any cleanup in the zoneroot after unpacking the archive.
#
{
}
#
# Determine flar compression style from identification file.
#
{
typeset ident=$1
print ${line##*=}
}
#
# Determine flar archive style from identification file.
#
{
typeset ident=$1
print ${line##*=}
}
#
# Unpack flar into current directory (which should be zoneroot). The flash
# archive is standard input. See flash_archive(4) man page.
#
# We can't use "flar split" since it will only unpack into a directory called
# "archive". We need to unpack in place in order to properly handle nested
# fs mounts within the zone root. This function does the unpacking into the
# current directory.
#
# we keep the same style as the original.
#
{
typeset result
typeset archiver_command
typeset archiver_arguments
# Read cookie
read -r input_line
if (( $? != 0 )); then
return 1
fi
# The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers.
return 1
fi
while [ true ]
do
# We should always be at the start of a section here
read -r input_line
return 1
fi
section_name=${input_line##*=}
# If we're at the archive, we're done skipping sections.
break
fi
#
# Save identification section to a file so we can determine
# how to unpack the archive.
#
/usr/bin/rm -f identification
while read -r input_line
do
if [[ ${input_line%%=*} == \
"section_begin" ]]; then
/usr/bin/rm -f identification
return 1
fi
if [[ $input_line == \
break;
fi
echo $input_line >> identification
done
continue
fi
#
# Otherwise skip past this section; read lines until detecting
# section_end. According to flash_archive(4) we can have
# an arbitrary number of sections but the archive section
# must be last.
#
success=0
while read -r input_line
do
then
success=1
break
fi
# Fail if we miss the end of the section
/usr/bin/rm -f identification
return 1
fi
done
#
# If we get here we read to the end of the file before
# seeing the end of the section we were reading.
#
/usr/bin/rm -f identification
return 1
fi
done
# Check for an archive made from a ZFS root pool.
if (( $? == 0 )); then
/usr/bin/rm -f identification
return 1
fi
# Get the information needed to unpack the archive.
# pax archiver specified
if [[ -s $ipdpaxfile ]]; then
archiver_arguments="-r -p e -c \
else
archiver_arguments="-r -p e"
fi
# cpio archived specified OR no archiver specified - use default
archiver_arguments="-icdumfE $ipdcpiofile"
else
# unknown archiver specified
return 1
fi
if [[ ! -x $archiver_command ]]; then
/usr/bin/rm -f identification
return 1
fi
# We're done with the identification file
/usr/bin/rm -f identification
# Extract archive
else
fi
result=$?
return 0
}
#
# Get the archive base.
#
# We must unpack the archive in the right place within the zonepath so
# that files are installed into the various mounted filesystems that are set
# up in the zone's configuration. These are already mounted for us by the
# mntfs function.
#
# Archives can be made of either a physical host's root file system or a
# zone's zonepath. For a physical system, if the archive is made using an
# absolute path (/...) we can't use it. For a zone the admin can make the
# archive from a variety of locations;
#
# a) zonepath itself: This will be a single dir, probably named with the
# zone name, it will contain a root dir and under the root we'll see all
# the top level dirs; etc, var, usr... We must be above the ZONEPATH
# when we unpack the archive but this will only work if the the archive's
# top-level dir name matches the ZONEPATH base-level dir name. If not,
# this is an error.
#
# b) inside the zonepath: We'll see root and it will contain all the top
# level dirs; etc, var, usr.... We must be in the ZONEPATH when we unpack
# the archive.
#
# c) inside the zonepath root: We'll see all the top level dirs, ./etc,
# ./var, ./usr.... This is also the case we see when we get an archive
# of a physical sytem. We must be in ZONEROOT when we unpack the archive.
#
# directory.
#
# This function handles the above possibilities so that we reject absolute
# path archives and figure out where in the file system we need to be to
# properly unpack the archive into the zone. It sets the ARCHIVE_BASE
# variable to the location where the achive should be unpacked.
#
{
stage1=$1
archive=$2
stage2=$3
# Check for an absolute path archive
if (substr($0, 1, 1) == "/")
exit 1
if ($1 != ".")
dirs[$1] = 1
else
dirs[$2] = 1
}
END {
for (d in dirs) {
cnt++
if (d == "bin") sawbin = 1
if (d == "etc") sawetc = 1
if (d == "root") sawroot = 1
if (d == "var") sawvar = 1
}
if (cnt == 1) {
# If only one top-level dir named root, we are in the
# zonepath, otherwise this must be an archive *of*
# the zonepath so print the top-level dir name.
if (sawroot)
print "*zonepath*"
else
for (d in dirs) print d
} else {
# (or at the top level of a full system archive which
# one.
if (sawroot && !sawbin && !sawetc && !sawvar)
print "*zonepath*"
else
print "*zoneroot*"
}
}'`
if (( $? != 0 )); then
fi
else
# We need to be in the dir above the ZONEPATH but we need to
# validate that $base matches the final component of ZONEPATH.
fatal "$e_mismatch_archive" "$base" "$bname"
fi
fi
}
#
# Unpack cpio archive into zoneroot.
#
{
stage1=$1
archive=$2
get_archive_base "$stage1" "$archive" "cpio -it"
cpioopts="-idmfE $ipdcpiofile"
vlog "cd \"$ARCHIVE_BASE\" && $stage1 \"$archive\" | cpio $cpioopts"
# Ignore errors from cpio since we expect some errors depending on
# how the archive was made.
return 0
}
#
# Unpack pax archive into zoneroot.
#
{
archive=$1
get_archive_base "cat" "$archive" "pax"
if [[ -s $ipdpaxfile ]]; then
fi
vlog "cd \"$ARCHIVE_BASE\" && pax -r -f \"$archive\" $filtopt"
# Ignore errors from pax since we expect some errors depending on
# how the archive was made.
return 0
}
#
# Unpack UFS dump into zoneroot.
#
{
archive=$1
#
# ufsrestore goes interactive if you ^C it. To prevent that,
# we make sure its stdin is not a terminal.
# Note that there is no way to filter inherit-pkg-dirs for a full
# restore so there will be warnings in the log file.
#
result=$?
return $result
}
#
# Copy directory hierarchy into zoneroot.
#
{
source_dir=$1
cpioopts="-pdm"
first=1
do
if [[ $first == 1 ]]; then
first=0
else
fi
done)
do
printf "%s " "$i"
done)
findopts="-xdev ( -type d -o -type f -o -type l ) -print"
vlog "cd \"$source_dir\" && find $flist $findopts | "
# Ignore errors from cpio since we expect some errors depending on
# how the archive was made.
return 0
}
#
# This is a common function for laying down a zone image from a variety of
# different sources. This can be used to either install a fresh zone or as
# part of zone migration during attach.
#
# The first argument specifies the type of image: archive, directory or stdin.
# The second argument specifies the image itself. In the case of stdin, the
# second argument specifies the format of the stream (cpio, flar, etc.).
# Any validation or post-processing on the image is done elsewhere.
#
# This function calls a 'sanity_check' function which must be provided by
# the script which includes this code.
#
{
intype=$1
insrc=$2
return 1
fi
filetype="unknown"
filetypename="unknown"
stage1="cat"
# Indicates that the existing zonepath is prepopulated.
filetype="existing"
filetypename="existing"
else
fatal "$e_path_abs" "$insrc"
fi
log "$e_not_found" "$insrc"
fi
log "$e_not_readable" "$insrc"
fi
fi
filetype="directory"
filetypename="directory"
fi
else
# Common code for both archive and stdin stream.
fi
else
# For intype == stdin, the insrc parameter specifies
# the stream format coming on stdin.
insrc="-"
fi
# Setup vars for the archive type we have.
filetypename="cpio archive"
;;
filetypename="bzipped cpio archive"
;;
filetypename="gzipped cpio archive"
;;
filetypename="ufsdump archive"
;;
"flar")
filetype="flar"
filetypename="flash archive"
;;
"flash")
filetype="flar"
filetypename="flash archive"
;;
filetype="flar"
filetypename="flash archive"
;;
"tar")
filetype="tar"
filetypename="tar archive"
;;
filetype="tar"
filetypename="tar archive"
;;
"pax")
filetype="xustar"
filetypename="pax (xustar) archive"
;;
filetype="xustar"
filetypename="pax (xustar) archive"
;;
"zfs")
filetype="zfs"
filetypename="ZFS send stream"
;;
filetype="zfs"
filetypename="ZFS send stream"
;;
*) log "$e_unknown_archive"
;;
esac
fi
# Check for a non-empty root if no '-d -' option.
fi
fi
fi
# Make sure we always have the files holding the directories to filter
# out when extracting from a CPIO or PAX archive. We'll add the fs
# entries to these files in get_fs_info() (there may be no IPDs for
# some brands but thats ok).
ipdcpiofile=$(/usr/bin/mktemp -t -p /var/tmp ipd.cpio.XXXXXX)
rm -f $fstmpfile
fi
# In addition to the IPDs, also filter out these directories.
ipdpaxfile=$(/usr/bin/mktemp -t -p /var/tmp ipd.pax.XXXXXX)
fi
# Set up any fs mounts so the archive will install into the correct
# locations.
if (( $? != 0 )); then
fi
else
fi
#
# Install the image into the zonepath.
#
stage1="cat"
stage1="gzcat"
filetype="cpio"
stage1="bzcat"
filetype="cpio"
fi
# Ignore errors from tar since we expect some errors depending
# on how the archive was made.
#
# Given a 'zfs send' stream file, receive the snapshot into
# the zone's dataset. We're getting the original system's
# zonepath dataset. Destroy the existing dataset created
# above since this recreates it.
#
fi
if (( $? != 0 )); then
log "$f_zfsdestroy" "$DATASET"
fi
fi
# Clean up any fs mounts used during unpacking.
# Verify this is a valid image.
return 0
}
# Setup i18n output
TEXTDOMAIN="SUNW_OST_OSCMD"
export TEXTDOMAIN
e_unknown_archive=$(gettext "Error: Unknown archive format. Must be a flash archive, a cpio archive (can also be gzipped or bzipped), a pax XUSTAR archive, or a level 0 ufsdump archive.")
e_mismatch_archive=$(gettext "Error: the archive top-level directory (%s) does not match the zonepath (%s).")
e_root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.")
bad_zfs_flar=$(gettext "Flash archive contains a ZFS send stream.\n\tRecreate the flar using the -L option with cpio or pax.")
#
#
# 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
#