--- /dev/null Wed Feb 11 09:21:15 2009 +++ new/usr/src/lib/brand/native/zone/common.ksh Wed Feb 11 09:21:14 2009 @@ -0,0 +1,516 @@ +# +# 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. +# + +# +# Send the error message to the screen and to the logfile. +# +error() +{ + typeset fmt="$1" + shift + + printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@" + [[ -n $LOGFILE ]] && printf "[$(date)] ERROR: ${fmt}\n" "$@" >&2 +} + +fatal() +{ + typeset fmt="$1" + shift + + error "$fmt" "$@" + exit $EXIT_CODE +} + +# +# Send the provided printf()-style arguments to the screen and to the logfile. +# +log() +{ + typeset fmt="$1" + shift + + printf "${MSG_PREFIX}${fmt}\n" "$@" + [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2 +} + +# +# 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 + + [[ -n $OPT_V ]] && printf "${MSG_PREFIX}${fmt}\n" "$@" + [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2 +} + +# Validate that the directory is safe. +safe_dir() +{ + typeset dir="$1" + + if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then + fatal "$e_baddir" "$dir" + fi +} + +# Only make a copy if we haven't already done so. +safe_backup() +{ + typeset src="$1" + typeset dst="$2" + + if [[ ! -h $src && ! -h $dst && ! -d $dst && ! -f $dst ]]; then + /usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src" + fi +} + +# Make a copy even if the destination already exists. +safe_copy() +{ + typeset src="$1" + typeset dst="$2" + + if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then + /usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src" + fi +} + +# Move a file +safe_move() +{ + typeset src="$1" + typeset dst="$2" + + if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then + /usr/bin/mv $src $dst || fatal "$e_badfile" "$src" + fi +} + +# +# 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. +# +get_fs_info() +{ + zonecfg -z $zonename info inherit-pkg-dir | \ + 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 + + zonecfg -z $zonename info fs | nawk '{ + 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. + cmd="/usr/bin/mkdir -p " zonepath "/root" $1 " >/dev/null 2>&1" + system(cmd); + + cmd="/usr/sbin/mount -F " $2 " " options " " $3 " " \ + 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 '{ + cmd="/usr/sbin/umount " zonepath "/root" $1 + if (system(cmd) != 0) { + printf("command failed: %s\n", cmd); + } + }' >>$LOGFILE +} + +# +# Determine flar compression style from identification file. +# +get_compression() +{ + typeset ident=$1 + typeset line=$(grep "^files_compressed_method=" $ident) + + print ${line##*=} +} + +# +# Determine flar archive style from identification file. +# +get_archiver() +{ + typeset ident=$1 + typeset line=$(grep "^files_archived_method=" $ident) + + 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. +# +# This code is derived from the gen_split() function in /usr/sbin/flar so +# we keep the same style as the original. +# +install_flar() +{ + typeset result + typeset archiver_command + typeset archiver_arguments + + vlog "cd $ZONEROOT && do_flar < \"$install_archive\"" + + # Read cookie + read -r input_line + if (( $? != 0 )); then + log "$not_readable" "$install_media" + return 1 + fi + # The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers. + if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]]; then + log "$not_flar" + return 1 + fi + + while [ true ] + do + # We should always be at the start of a section here + read -r input_line + if [[ ${input_line%%=*} != "section_begin" ]]; then + log "$bad_flar" + return 1 + fi + section_name=${input_line##*=} + + # If we're at the archive, we're done skipping sections. + if [[ "$section_name" == "archive" ]]; then + break + fi + + # + # Save identification section to a file so we can determine + # how to unpack the archive. + # + if [[ "$section_name" == "identification" ]]; then + /usr/bin/rm -f identification + while read -r input_line + do + if [[ ${input_line%%=*} == \ + "section_begin" ]]; then + /usr/bin/rm -f identification + log "$bad_flar" + return 1 + fi + + if [[ $input_line == \ + "section_end=$section_name" ]]; then + 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 + if [[ $input_line == "section_end=$section_name" ]]; + then + success=1 + break + fi + # Fail if we miss the end of the section + if [[ ${input_line%%=*} == "section_begin" ]]; then + /usr/bin/rm -f identification + log "$bad_flar" + return 1 + fi + done + if (( $success == 0 )); then + # + # 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 + log "$bad_flar" + return 1 + fi + done + + # Get the information needed to unpack the archive. + archiver=$(get_archiver identification) + if [[ $archiver == "pax" ]]; then + # pax archiver specified + archiver_command="/usr/bin/pax" + if [[ -s $ipdpaxfile ]]; then + archiver_arguments="-r -p e -c \ + $(/usr/bin/cat $ipdpaxfile)" + else + archiver_arguments="-r -p e" + fi + elif [[ $archiver == "cpio" || -z $archiver ]]; then + # cpio archived specified OR no archiver specified - use default + archiver_command="/usr/bin/cpio" + archiver_arguments="-icdumfE $ipdcpiofile" + else + # unknown archiver specified + log "$unknown_archiver" $archiver + return 1 + fi + + if [[ ! -x $archiver_command ]]; then + /usr/bin/rm -f identification + log "$cmd_not_exec" $archiver_command + return 1 + fi + + compression=$(get_compression identification) + + # We're done with the identification file + /usr/bin/rm -f identification + + # Extract archive + if [[ $compression == "compress" ]]; then + /usr/bin/zcat | ppriv -e -s A=all,-sys_devices \ + $archiver_command $archiver_arguments 2>/dev/null + else + ppriv -e -s A=all,-sys_devices \ + $archiver_command $archiver_arguments 2>/dev/null + fi + result=$? + + (( $result != 0 )) && return 1 + + return 0 +} + +# +# Unpack cpio archive into zoneroot. +# +install_cpio() +{ + stage1=$1 + archive=$2 + + cpioopts="-idmfE $ipdcpiofile" + + vlog "cd \"$ZONEROOT\" && $stage1 \"$archive\" | " + vlog "ppriv -e -s A=all,-sys_devices cpio $cpioopts" + + ( cd "$ZONEROOT" && $stage1 "$archive" | \ + ppriv -e -s A=all,-sys_devices cpio $cpioopts ) +} + +# +# Unpack pax archive into zoneroot. +# +install_pax() +{ + archive=$1 + + if [[ -s $ipdpaxfile ]]; then + filtopt="-c $(/usr/bin/cat $ipdpaxfile)" + fi + + vlog "cd \"$ZONEROOT\" && " + vlog "ppriv -e -s A=all,-sys_devices pax -r -f \"$archive\" $filtopt" + + ( cd "$ZONEROOT" && ppriv -e -s A=all,-sys_devices \ + pax -r -f "$archive" $filtopt ) +} + +# +# Unpack UFS dump into zoneroot. +# +install_ufsdump() +{ + archive=$1 + + vlog "cd \"$ZONEROOT\" && " + vlog "ppriv -e -s A=all,-sys_devices ufsrestore rf \"$archive\"" + + # + # 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. + # + ( cd "$ZONEROOT" && ppriv -e -s A=all,-sys_devices \ + ufsrestore rf "$archive" < /dev/null ) +} + +# +# Copy directory hierarchy into zoneroot. +# +install_dir() +{ + source_dir=$1 + + cpioopts="-pdm" + + first=1 + filt=$(for i in $(cat $ipdpaxfile) + do + echo $i | egrep -s "/" && continue + if [[ $first == 1 ]]; then + printf "^%s" $i + first=0 + else + printf "|^%s" $i + fi + done) + + list=$(cd "$source_dir" && ls -d * | egrep -v "$filt") + flist=$(for i in $list + do + printf "%s " "$i" + done) + findopts="-xdev ( -type d -o -type f -o -type l ) -print" + + vlog "cd \"$source_dir\" && find $flist $findopts | " + vlog "ppriv -e -s A=all,-sys_devices cpio $cpioopts \"$ZONEROOT\"" + + ( cd "$source_dir" && find $flist $findopts | \ + ppriv -e -s A=all,-sys_devices cpio $cpioopts "$ZONEROOT" ) +} + +# Setup i18n output +TEXTDOMAIN="SUNW_OST_OSCMD" +export TEXTDOMAIN + +e_baddir=$(gettext "Invalid '%s' directory within the zone") +e_badfile=$(gettext "Invalid '%s' file within the zone") + +# +# Exit values used by the script, as #defined in +# +# 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 +