1 #!/bin/ksh -p
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23 # Use is subject to license terms.
  24 #
  25 
  26 . /usr/lib/brand/shared/common.ksh
  27 
  28 # Restrict executables to /bin, /usr/bin and /usr/sfw/bin
  29 PATH=/bin:/usr/bin:/usr/sbin:/usr/sfw/bin
  30 export PATH
  31 
  32 cmd_not_found=$(gettext "Required command '%s' cannot be found!")
  33 cmd_not_exec=$(gettext "Required command '%s' not executable!")
  34 zone_initfail=$(gettext "Attempt to initialize zone '%s' FAILED.")
  35 path_abs=$(gettext "Pathname specified to -a '%s' must be absolute.")
  36 
  37 e_tmpfile=$(gettext "Unable to create temporary file")
  38 
  39 both_modes=$(gettext "%s: cannot select both silent and verbose modes")
  40 
  41 both_choices=$(gettext "%s: cannot select both preserve and unconfigure options")
  42 
  43 both_kinds=$(gettext "%s: cannot specify both archive and directory")
  44 
  45 not_found=$(gettext "%s: error: file or directory not found.")
  46 
  47 wrong_dir_type=$(gettext "error: must be a directory")
  48 
  49 not_readable=$(gettext "Cannot read file '%s'")
  50 
  51 no_install=$(gettext "Could not create install directory '%s'")
  52 no_log=$(gettext "Could not create log directory '%s'")
  53 
  54 media_taste=$(gettext   "    Media Type: %s")
  55 bad_archive=$(gettext "ERROR: must be a flash archive, a cpio archive (can also
  56 be gzipped or bzipped), a pax XUSTAR archive, or a level 0 ufsdump archive.")
  57 
  58 product_vers=$(gettext  "       Product: %s")
  59 install_vers=$(gettext  "     Installer: %s")
  60 install_zone=$(gettext  "          Zone: %s")
  61 install_path=$(gettext  "          Path: %s")
  62 install_from=$(gettext  "        Source: %s")
  63 installing=$(gettext    "    Installing: This may take several minutes...")
  64 no_installing=$(gettext "    Installing: Using pre-existing data in zonepath")
  65 install_prog=$(gettext  "    Installing: %s")
  66 
  67 install_fail=$(gettext  "        Result: *** Installation FAILED ***")
  68 install_log=$(gettext   "      Log File: %s")
  69 
  70 install_abort=$(gettext "        Result: Installation aborted.")
  71 install_good=$(gettext  "        Result: Installation completed successfully.")
  72 
  73 not_native_image=$(gettext  "  Sanity Check: %s doesn't look like a native image.")
  74 sanity_ok=$(gettext     "  Sanity Check: Passed.  Looks like a native system.")
  75 sanity_fail_detail=$(gettext  "  Sanity Check: Missing %s at %s")
  76 sanity_fail_vers=$(gettext  "  Sanity Check: image release version %s does not match system release version %s, the zone is not usable on this system.")
  77 sanity_fail=$(gettext   "  Sanity Check: FAILED (see log for details).")
  78 
  79 
  80 p2ving=$(gettext        "Postprocessing: This may take a while...")
  81 p2v_prog=$(gettext      "   Postprocess: ")
  82 p2v_done=$(gettext      "        Result: Postprocessing complete.")
  83 p2v_fail=$(gettext      "        Result: Postprocessing failed.")
  84 
  85 root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.")
  86 
  87 media_missing=\
  88 $(gettext "%s: you must specify an installation source using '-a' or '-d'.")
  89 
  90 cfgchoice_missing=\
  91 $(gettext "%s: you must specify -u (sys-unconfig) or -p (preserve identity).")
  92 
  93 mount_failed=$(gettext "ERROR: zonecfg(1M) 'fs' mount failed")
  94 
  95 not_flar=$(gettext "Input is not a flash archive")
  96 bad_flar=$(gettext "Flash archive is a corrupt")
  97 unknown_archiver=$(gettext "Archiver %s is not supported")
  98 
  99 e_baddir=$(gettext "Invalid '%s' directory within the zone")
 100 
 101 # Clean up on interrupt
 102 trap_cleanup()
 103 {
 104         msg=$(gettext "Installation cancelled due to interrupt.")
 105         log "$msg"
 106 
 107         # umount IPDs
 108         umnt_fs
 109 
 110         exit $EXIT_CODE
 111 }
 112 
 113 sanity_check()
 114 {
 115         typeset dir="$1"
 116         shift
 117         ret=0
 118 
 119         # These checks must work with a sparse zone.
 120         checks="etc etc/svc usr sbin lib var var/svc"
 121         for x in $checks; do
 122                 if [[ ! -e $dir/$x ]]; then
 123                         vlog "$sanity_fail_detail" "$x" "$dir"
 124                         ret=1
 125                 fi
 126         done
 127 
 128         #
 129         # Check image release against system release.  We only work on the
 130         # same minor release as the system is running.
 131         #
 132         sys_vers=0
 133         image_vers=-1
 134         if [[ -f /var/sadm/system/admin/INST_RELEASE ]]; then
 135                 sys_vers=$(nawk -F= '{if ($1 == "VERSION") print $2}' \
 136                     /var/sadm/system/admin/INST_RELEASE)
 137         fi
 138 
 139         if [[ -f $dir/var/sadm/system/admin/INST_RELEASE ]]; then
 140                 image_vers=$(nawk -F= '{if ($1 == "VERSION") print $2}' \
 141                     $dir/var/sadm/system/admin/INST_RELEASE)
 142         fi
 143 
 144         if (( $sys_vers != $image_vers )); then
 145                 vlog "$sanity_fail_vers" "$image_vers" "$sys_vers"
 146                 ret=1
 147         fi
 148         
 149         return $ret
 150 }
 151 
 152 #
 153 # The main body of the script starts here.
 154 #
 155 # This script should never be called directly by a user but rather should
 156 # only be called by zoneadm to install a native system image into a zone.
 157 #
 158 
 159 #
 160 # Exit code to return if install is interrupted or exit code is otherwise
 161 # unspecified.
 162 #
 163 EXIT_CODE=$ZONE_SUBPROC_USAGE
 164 
 165 trap trap_cleanup INT
 166 
 167 # If we weren't passed at least two arguments, exit now.
 168 (( $# < 2 )) && exit $ZONE_SUBPROC_USAGE
 169 
 170 zonename="$1"
 171 zonepath="$2"
 172 
 173 ZONEROOT="$zonepath/root"
 174 logdir="$ZONEROOT/var/log"
 175 
 176 shift; shift    # remove zonename and zonepath from arguments array
 177 
 178 unset backout
 179 unset install_archive
 180 unset source_dir
 181 unset msg
 182 unset silent_mode
 183 unset OPT_V
 184 
 185 #
 186 # It is worth noting here that we require the end user to pick one of
 187 # -u (sys-unconfig) or -p (preserve config).  This is because we can't
 188 # really know in advance which option makes a better default.  Forcing
 189 # the user to pick one or the other means that they will consider their
 190 # choice and hopefully not be surprised or disappointed with the result.
 191 #
 192 unset unconfig_zone
 193 unset preserve_zone
 194 
 195 while getopts "a:b:d:psuv" opt
 196 do
 197         case "$opt" in
 198                 a)      install_archive="$OPTARG" ; install_media="$OPTARG";;
 199                 b)      if [[ -n "$backout" ]]; then
 200                                 backout="$backout -b $OPTARG"
 201                         else
 202                                 backout="-b $OPTARG"
 203                         fi
 204                         ;;
 205                 d)      source_dir="$OPTARG" ; install_media="$OPTARG";;
 206                 p)      preserve_zone="-p";;
 207                 s)      silent_mode=1;;
 208                 u)      unconfig_zone="-u";;
 209                 v)      OPT_V="-v";;
 210                 *)      exit $ZONE_SUBPROC_USAGE;;
 211         esac
 212 done
 213 shift OPTIND-1
 214 
 215 # The install can't be both verbose AND silent...
 216 if [[ -n $silent_mode && -n $OPT_V ]]; then
 217         fatal "$both_modes" "zoneadm install"
 218 fi
 219 
 220 if [[ -z $install_media ]]; then
 221         fatal "$media_missing" "zoneadm install"
 222 fi
 223 
 224 if [[ -n $install_archive && -n $source_dir ]]; then
 225         fatal "$both_kinds" "zoneadm install"
 226 fi
 227 
 228 # The install can't both preserve and unconfigure
 229 if [[ -n $unconfig_zone && -n $preserve_zone ]]; then
 230         fatal "$both_choices" "zoneadm install"
 231 fi
 232 
 233 # Must pick one or the other.
 234 if [[ -z $unconfig_zone && -z $preserve_zone ]]; then
 235         fatal "$cfgchoice_missing" "zoneadm install"
 236 fi
 237 
 238 #
 239 # Handle "-d -" option to use whatever is already installed into the zonepath.
 240 #
 241 if [ "$source_dir" != "-" ]; then
 242         #
 243         # Validate $install_media (things common to archive/dir)
 244         #
 245         if [[ "$(echo $install_media | cut -c 1)" != "/" ]]; then
 246                 fatal "$path_abs" "$install_media"
 247         fi
 248 
 249         if [[ ! -e "$install_media" ]]; then
 250                 log "$not_found" "$install_media"
 251                 fatal "$install_abort" "$zonename"
 252         fi
 253 
 254         if [[ ! -r "$install_media" ]]; then
 255                 log "$not_readable" "$install_media"
 256                 fatal "$install_abort" "$zonename"
 257         fi
 258 
 259         if [[ -n $install_archive ]]; then
 260                 if [[ ! -f "$install_archive" ]]; then
 261                         log "$media_taste" "$bad_archive"
 262                         fatal "$install_abort" "$zonename"
 263                 fi
 264         fi
 265 
 266         if [[ -n $source_dir ]]; then
 267                 if [[ ! -d "$source_dir" ]]; then
 268                         log "$media_taste" "$wrong_dir_type"
 269                         fatal "$install_abort" "$zonename"
 270                 fi
 271         fi
 272 fi
 273 
 274 LOGFILE=$(/usr/bin/mktemp -t -p /var/tmp $zonename.install_log.XXXXXX)
 275 if [[ -z "$LOGFILE" ]]; then
 276         fatal "$e_tmpfile"
 277 fi
 278 zone_logfile="${logdir}/$zonename.install$$.log"
 279 exec 2>>"$LOGFILE"
 280 log "$install_log" "$LOGFILE"
 281 
 282 vlog "Starting pre-installation tasks."
 283 
 284 if [[ -z $install_archive && -n $source_dir ]]; then
 285         #
 286         # Minimal check to make sure that the user is passing
 287         # us something that at least seems to be a native image.
 288         #
 289         if [[ "$source_dir" == "-" ]]; then
 290                 filetype="existing"
 291                 filetypename="existing"
 292         else
 293                 sanity_check $source_dir
 294                 if (( $? != 0 )); then
 295                         fatal "$not_native_image" "$source_dir"
 296                 fi
 297 
 298                 filetype="directory"
 299                 filetypename="directory"
 300         fi
 301 else
 302         ftype="$(LC_ALL=C file $install_archive | cut -d: -f 2)"
 303         case "$ftype" in
 304         *cpio*)         filetype="cpio"
 305                         filetypename="cpio archive"
 306                 ;;
 307         *bzip2*)        filetype="bzip2"
 308                         filetypename="bzipped cpio archive"
 309                 ;;
 310         *gzip*)         filetype="gzip"
 311                         filetypename="gzipped cpio archive"
 312                 ;;
 313         *ufsdump*)      filetype="ufsdump"
 314                         filetypename="ufsdump archive"
 315                 ;;
 316         *Flash\ Archive*)       filetype="flar"
 317                         filetypename="flash archive"
 318                 ;;
 319         *USTAR\ tar\ archive\ extended\ format*)        filetype="xustar"
 320                         filetypename="pax (xustar) archive"
 321                 ;;
 322         *)              log "$media_taste" "$bad_archive"
 323                         fatal "$install_abort" "$zonename"
 324                 ;;
 325         esac
 326 fi
 327 
 328 #
 329 # From here on out, an unspecified exit or interrupt should exit with
 330 # ZONE_SUBPROC_NOTCOMPLETE, meaning a user will need to do an uninstall before
 331 # attempting another install, as we've modified the directories we were going
 332 # to install to in some way.
 333 #
 334 EXIT_CODE=$ZONE_SUBPROC_NOTCOMPLETE
 335 
 336 if [[ ! -d "$ZONEROOT" ]]
 337 then
 338         if ! mkdir -p "$ZONEROOT" 2>/dev/null; then
 339                 fatal "$no_install" "$ZONEROOT"
 340         fi
 341 fi
 342 
 343 #
 344 # Check for a non-empty root if no '-d -' option. 
 345 #
 346 if [[ "$filetype" != "existing" ]]; then
 347         cnt=$(ls $ZONEROOT | wc -l)
 348         if (( $cnt != 0 )); then
 349                 fatal "$root_full" "$ZONEROOT"
 350         fi
 351 fi
 352 
 353 vlog "Installation started for zone \"$zonename\""
 354 
 355 log "$install_from" "$install_media"
 356 vlog "$media_taste" "$filetypename"
 357 
 358 fstmpfile=$(/usr/bin/mktemp -t -p /var/tmp)
 359 if [[ -z "$fstmpfile" ]]; then
 360         fatal "$e_tmpfile"
 361 fi
 362 
 363 # Make sure we always have the files holding the directories to filter
 364 # out when extracting from a CPIO or PAX archive.  We'll add the IPDs to these
 365 # files in get_fs_info().
 366 ipdcpiofile=$(/usr/bin/mktemp -t -p /var/tmp ipd.cpio.XXXXXX)
 367 if [[ -z "$ipdcpiofile" ]]; then
 368         rm -f $fstmpfile
 369         fatal "$e_tmpfile"
 370 fi
 371 
 372 # In addition to the IPDs, also filter out these directories.
 373 echo 'dev/*' >>$ipdcpiofile
 374 echo 'devices/*' >>$ipdcpiofile
 375 echo 'devices' >>$ipdcpiofile
 376 echo 'proc/*' >>$ipdcpiofile
 377 echo 'tmp/*' >>$ipdcpiofile
 378 echo 'var/run/*' >>$ipdcpiofile
 379 echo 'system/contract/*' >>$ipdcpiofile
 380 echo 'system/object/*' >>$ipdcpiofile
 381 
 382 ipdpaxfile=$(/usr/bin/mktemp -t -p /var/tmp ipd.pax.XXXXXX)
 383 if [[ -z "$ipdpaxfile" ]]; then
 384         rm -f $fstmpfile $ipdcpiofile
 385         fatal "$e_tmpfile"
 386 fi
 387 
 388 printf "%s " "dev devices proc tmp var/run system/contract system/object" \
 389     >>$ipdpaxfile
 390 
 391 # Set up any fs mounts so the archive will install into the correct locations.
 392 get_fs_info
 393 mnt_fs
 394 if (( $? != 0 )); then
 395         umnt_fs >/dev/null 2>&1
 396         rm -f $fstmpfile $ipdcpiofile $ipdpaxfile
 397         fatal "$mount_failed"
 398 fi
 399 
 400 if [[ "$filetype" == "existing" ]]; then
 401         log "$no_installing"
 402 else
 403         log "$installing"
 404 fi
 405 
 406 unpack_result=0
 407 stage1="cat"
 408 if [[ "$filetype" == "gzip" ]]; then
 409         stage1="gzcat"
 410         filetype="cpio"
 411 fi
 412 
 413 if [[ "$filetype" == "bzip2" ]]; then
 414         stage1="bzcat"
 415         filetype="cpio"
 416 fi
 417 
 418 if [[ "$filetype" == "cpio" ]]; then
 419         install_cpio "$stage1" "$install_archive"
 420         unpack_result=$?
 421 
 422 elif [[ "$filetype" == "flar" ]]; then
 423         ( cd "$ZONEROOT" && install_flar < "$install_archive" )
 424         unpack_result=$?
 425 
 426 elif [[ "$filetype" == "xustar" ]]; then
 427         install_pax "$install_archive"
 428         unpack_result=$?
 429 
 430 elif [[ "$filetype" == "ufsdump" ]]; then
 431         install_ufsdump "$install_archive"
 432         unpack_result=$?
 433 
 434 elif [[ "$filetype" == "directory" ]]; then
 435         install_dir "$source_dir"
 436         unpack_result=$?
 437 fi
 438 
 439 # Clean up any fs mounts used during unpacking.
 440 umnt_fs
 441 rm -f $fstmpfile $ipdcpiofile $ipdpaxfile
 442 
 443 #
 444 # Do a sanity check to see if various things we think should be present
 445 # are present.  If not, the user might have supplied a cpio archive which was
 446 # not created properly.
 447 #
 448 if (( $unpack_result == 0 )); then
 449         sanity_check $ZONEROOT
 450         if (( $? != 0 )); then
 451                 log "$sanity_fail"
 452                 log ""
 453                 log "$install_log" "$LOGFILE"
 454                 fatal "$install_fail" "$zonename"
 455         else
 456                 vlog "$sanity_ok"
 457         fi
 458 fi
 459         
 460 chmod 700 $zonepath
 461 
 462 log "$p2ving"
 463 vlog "running: p2v $OPT_V $unconfig_zone $backout $zonename $zonepath"
 464 
 465 #
 466 # Run p2v.
 467 #
 468 # Getting the output to the right places is a little tricky because what
 469 # we want is for p2v to output in the same way the installer does: verbose
 470 # messages to the log file always, and verbose messages printed to the
 471 # user if the user passes -v.  This rules out simple redirection.  And
 472 # we can't use tee or other tricks because they cause us to lose the
 473 # return value from the p2v script due to the way shell pipelines work.
 474 #
 475 # The simplest way to do this seems to be to hand off the management of
 476 # the log file to the p2v script.  So we run p2v with -l to tell it where
 477 # to find the log file and then reopen the log (O_APPEND) when p2v is done.
 478 #
 479 /usr/lib/brand/native/p2v -l "$LOGFILE" -m "$p2v_prog" \
 480      $OPT_V $unconfig_zone $backout $zonename $zonepath
 481 p2v_result=$?
 482 exec 2>>$LOGFILE
 483 
 484 if (( $p2v_result == 0 )); then
 485         vlog "$p2v_done"
 486 else
 487         log "$p2v_fail"
 488         log ""
 489         log "$install_fail"
 490         log "$install_log" "$LOGFILE"
 491         exit $ZONE_SUBPROC_FATAL
 492 fi
 493 
 494 EXIT_CODE=$ZONE_SUBPROC_OK
 495 
 496 log ""
 497 log "$install_good" "$zonename"
 498 
 499 if [[ -h $ZONEROOT/var || ! -d $ZONEROOT/var || -h $ZONEROOT/var/log ]]; then
 500         log "$e_baddir" "/var/log"
 501         exit $ZONE_SUBPROC_FATAL
 502 fi
 503 
 504 # Just in case the log directory isn't present...
 505 if [[ ! -d "$logdir" ]]; then
 506         if ! mkdir -p "$logdir" 2>/dev/null; then
 507                 log "$no_log" "$logdir"
 508         fi
 509 fi
 510 
 511 if [[ ! -h $zone_logfile && ! -d $zone_logfile ]]; then
 512         cp $LOGFILE $zone_logfile
 513 fi
 514 log "$install_log" "$zone_logfile"
 515 rm -f $LOGFILE
 516 
 517 exit 0