1 #
   2 # CDDL HEADER START
   3 #
   4 # The contents of this file are subject to the terms of the
   5 # Common Development and Distribution License (the "License").
   6 # You may not use this file except in compliance with the License.
   7 #
   8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9 # or http://www.opensolaris.org/os/licensing.
  10 # See the License for the specific language governing permissions
  11 # and limitations under the License.
  12 #
  13 # When distributing Covered Code, include this CDDL HEADER in each
  14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15 # If applicable, add the following below this CDDL HEADER, with the
  16 # fields enclosed by brackets "[]" replaced with your own identifying
  17 # information: Portions Copyright [yyyy] [name of copyright owner]
  18 #
  19 # CDDL HEADER END
  20 #
  21 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  22 # Use is subject to license terms.
  23 #
  24 
  25 #
  26 # Send the error message to the screen and to the logfile.
  27 #
  28 error()
  29 {
  30         typeset fmt="$1"
  31         shift
  32 
  33         printf "${MSG_PREFIX}ERROR: ${fmt}\n" "$@"
  34         [[ -n $LOGFILE ]] && printf "[$(date)] ERROR: ${fmt}\n" "$@" >&2
  35 }
  36 
  37 fatal()
  38 {
  39         typeset fmt="$1"
  40         shift
  41 
  42         error "$fmt" "$@"
  43         exit $EXIT_CODE
  44 }
  45 
  46 #
  47 # Send the provided printf()-style arguments to the screen and to the logfile.
  48 #
  49 log()
  50 {
  51         typeset fmt="$1"
  52         shift
  53 
  54         printf "${MSG_PREFIX}${fmt}\n" "$@"
  55         [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
  56 }
  57 
  58 #
  59 # Print provided text to the screen if the shell variable "OPT_V" is set.
  60 # The text is always sent to the logfile.
  61 #
  62 vlog()
  63 {
  64         typeset fmt="$1"
  65         shift
  66 
  67         [[ -n $OPT_V ]] && printf "${MSG_PREFIX}${fmt}\n" "$@"
  68         [[ -n $LOGFILE ]] && printf "[$(date)] ${MSG_PREFIX}${fmt}\n" "$@" >&2
  69 }
  70 
  71 # Validate that the directory is safe.
  72 safe_dir()
  73 {
  74         typeset dir="$1"
  75 
  76         if [[ -h $ZONEROOT/$dir || ! -d $ZONEROOT/$dir ]]; then
  77                 fatal "$e_baddir" "$dir"
  78         fi
  79 }
  80 
  81 # Only make a copy if we haven't already done so.
  82 safe_backup()
  83 {
  84         typeset src="$1"
  85         typeset dst="$2"
  86 
  87         if [[ ! -h $src && ! -h $dst && ! -d $dst && ! -f $dst ]]; then
  88                 /usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
  89         fi
  90 }
  91 
  92 # Make a copy even if the destination already exists.
  93 safe_copy()
  94 {
  95         typeset src="$1"
  96         typeset dst="$2"
  97 
  98         if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
  99                 /usr/bin/cp -p $src $dst || fatal "$e_badfile" "$src"
 100         fi
 101 }
 102 
 103 # Move a file
 104 safe_move()
 105 {
 106         typeset src="$1"
 107         typeset dst="$2"
 108 
 109         if [[ ! -h $src && ! -h $dst && ! -d $dst ]]; then
 110                 /usr/bin/mv $src $dst || fatal "$e_badfile" "$src"
 111         fi
 112 }
 113 
 114 #
 115 # Read zonecfg ipd and fs entries and save the relevant data, one entry per
 116 # line.
 117 # This assumes the properties from the zonecfg output, e.g.:
 118 #       inherit-pkg-dir:
 119 #               dir: /usr
 120 #       fs:
 121 #               dir: /opt
 122 #               special: /opt
 123 #               raw not specified
 124 #               type: lofs
 125 #               options: [noexec,ro,noatime]
 126 #
 127 # and it assumes the order of the fs properties as above.  This also saves the
 128 # inherit-pkg-dir patterns into the ipd.{cpio|pax} temporary files for
 129 # filtering while extracting the image into the zonepath.  We have to save the
 130 # IPD patterns in the appropriate format for filtering with the different
 131 # archivers and we don't know what format we'll get until after the flash
 132 # archive is unpacked.
 133 #
 134 get_fs_info()
 135 {
 136         zonecfg -z $zonename info inherit-pkg-dir | \
 137             nawk -v ipdcpiof=$ipdcpiofile -v ipdpaxf=$ipdpaxfile '{
 138                 if ($1 == "dir:") {
 139                         dir=$2;
 140                         printf("%s lofs %s ro\n", dir, dir);
 141 
 142                         if (substr(dir, 1, 1) == "/") {
 143                                 printf("%s\n", substr(dir, 2)) >> ipdcpiof
 144                                 printf("%s/*\n", substr(dir, 2)) >> ipdcpiof
 145                         } else {
 146                                 printf("%s\n", dir) >> ipdcpiof
 147                                 printf("%s/*\n", dir) >> ipdcpiof
 148                         }
 149 
 150                         if (substr(dir, 1, 1) == "/") {
 151                                 printf("%s ", substr(dir, 2)) >> ipdpaxf
 152                         } else {
 153                                 printf("%s ", dir) >> ipdpaxf
 154                         }
 155                 }
 156         }' >> $fstmpfile
 157 
 158         zonecfg -z $zonename info fs | nawk '{
 159                 if ($1 == "options:") {
 160                         # Remove brackets.
 161                         options=substr($2, 2, length($2) - 2);
 162                         printf("%s %s %s %s\n", dir, type, special, options);
 163                 } else if ($1 == "dir:") {
 164                         dir=$2;
 165                 } else if ($1 == "special:") {
 166                         special=$2;
 167                 } else if ($1 == "type:") {
 168                         type=$2
 169                 }
 170         }' >> $fstmpfile
 171 }
 172 
 173 #
 174 # Mount zonecfg fs entries into the zonepath.
 175 #
 176 mnt_fs()
 177 {
 178         if [ ! -s $fstmpfile ]; then
 179                 return;
 180         fi
 181 
 182         # Sort the fs entries so we can handle nested mounts.
 183         sort $fstmpfile | nawk -v zonepath=$zonepath '{
 184                 if (NF == 4)
 185                         options="-o " $4;
 186                 else
 187                         options=""
 188 
 189                 # Create the mount point.  Ignore errors since we might have
 190                 # a nested mount with a pre-existing mount point.
 191                 cmd="/usr/bin/mkdir -p " zonepath "/root" $1 " >/dev/null 2>&1"
 192                 system(cmd);
 193 
 194                 cmd="/usr/sbin/mount -F " $2 " " options " " $3 " " \
 195                     zonepath "/root" $1;
 196                 if (system(cmd) != 0) {
 197                         printf("command failed: %s\n", cmd);
 198                         exit 1;
 199                 }
 200         }' >>$LOGFILE
 201 }
 202 
 203 #
 204 # Unmount zonecfg fs entries from the zonepath.
 205 #
 206 umnt_fs()
 207 {
 208         if [ ! -s $fstmpfile ]; then
 209                 return;
 210         fi
 211 
 212         # Reverse sort the fs entries so we can handle nested unmounts.
 213         sort -r $fstmpfile | nawk -v zonepath=$zonepath '{
 214                 cmd="/usr/sbin/umount " zonepath "/root" $1
 215                 if (system(cmd) != 0) {
 216                         printf("command failed: %s\n", cmd);
 217                 }
 218         }' >>$LOGFILE
 219 }
 220 
 221 #
 222 # Determine flar compression style from identification file.
 223 #
 224 get_compression()
 225 {
 226         typeset ident=$1
 227         typeset line=$(grep "^files_compressed_method=" $ident)
 228 
 229         print ${line##*=}
 230 }
 231 
 232 #
 233 # Determine flar archive style from identification file.
 234 #
 235 get_archiver()
 236 {
 237         typeset ident=$1
 238         typeset line=$(grep "^files_archived_method=" $ident)
 239 
 240         print ${line##*=}
 241 }
 242 
 243 #
 244 # Unpack flar into current directory (which should be zoneroot).  The flash
 245 # archive is standard input.  See flash_archive(4) man page.
 246 # 
 247 # We can't use "flar split" since it will only unpack into a directory called
 248 # "archive".  We need to unpack in place in order to properly handle nested
 249 # fs mounts within the zone root.  This function does the unpacking into the
 250 # current directory.
 251 #
 252 # This code is derived from the gen_split() function in /usr/sbin/flar so
 253 # we keep the same style as the original.
 254 #
 255 install_flar()
 256 {
 257         typeset result
 258         typeset archiver_command
 259         typeset archiver_arguments
 260 
 261         vlog "cd $ZONEROOT && do_flar < \"$install_archive\""
 262 
 263         # Read cookie
 264         read -r input_line
 265         if (( $? != 0 )); then
 266                 log "$not_readable" "$install_media"
 267                 return 1
 268         fi
 269         # The cookie has format FlAsH-aRcHiVe-m.n where m and n are integers.
 270         if [[ ${input_line%%-[0-9]*.[0-9]*} != "FlAsH-aRcHiVe" ]]; then
 271                 log "$not_flar"
 272                 return 1
 273         fi
 274 
 275         while [ true ]
 276         do
 277                 # We should always be at the start of a section here
 278                 read -r input_line
 279                 if [[ ${input_line%%=*} != "section_begin" ]]; then
 280                         log "$bad_flar"
 281                         return 1
 282                 fi
 283                 section_name=${input_line##*=}
 284 
 285                 # If we're at the archive, we're done skipping sections.
 286                 if [[ "$section_name" == "archive" ]]; then
 287                         break
 288                 fi
 289                 
 290                 #
 291                 # Save identification section to a file so we can determine
 292                 # how to unpack the archive.
 293                 #
 294                 if [[ "$section_name" == "identification" ]]; then
 295                         /usr/bin/rm -f identification
 296                         while read -r input_line
 297                         do
 298                                 if [[ ${input_line%%=*} == \
 299                                     "section_begin" ]]; then
 300                                         /usr/bin/rm -f identification
 301                                         log "$bad_flar"
 302                                         return 1
 303                                 fi
 304 
 305                                 if [[ $input_line == \
 306                                     "section_end=$section_name" ]]; then
 307                                         break;
 308                                 fi
 309                                 echo $input_line >> identification
 310                         done
 311 
 312                         continue
 313                 fi
 314 
 315                 #
 316                 # Otherwise skip past this section; read lines until detecting
 317                 # section_end.  According to flash_archive(4) we can have
 318                 # an arbitrary number of sections but the archive section
 319                 # must be last.
 320                 #
 321                 success=0
 322                 while read -r input_line
 323                 do
 324                         if [[ $input_line == "section_end=$section_name" ]];
 325                         then
 326                                 success=1
 327                                 break
 328                         fi
 329                         # Fail if we miss the end of the section
 330                         if [[ ${input_line%%=*} == "section_begin" ]]; then
 331                                 /usr/bin/rm -f identification
 332                                 log "$bad_flar"
 333                                 return 1
 334                         fi
 335                 done
 336                 if (( $success == 0 )); then
 337                         #
 338                         # If we get here we read to the end of the file before
 339                         # seeing the end of the section we were reading.
 340                         #
 341                         /usr/bin/rm -f identification
 342                         log "$bad_flar"
 343                         return 1
 344                 fi
 345         done
 346 
 347         # Get the information needed to unpack the archive.
 348         archiver=$(get_archiver identification)
 349         if [[ $archiver == "pax" ]]; then
 350                 # pax archiver specified
 351                 archiver_command="/usr/bin/pax"
 352                 if [[ -s $ipdpaxfile ]]; then
 353                         archiver_arguments="-r -p e -c \
 354                             $(/usr/bin/cat $ipdpaxfile)"
 355                 else
 356                         archiver_arguments="-r -p e"
 357                 fi
 358         elif [[ $archiver == "cpio" || -z $archiver ]]; then
 359                 # cpio archived specified OR no archiver specified - use default
 360                 archiver_command="/usr/bin/cpio"
 361                 archiver_arguments="-icdumfE $ipdcpiofile"
 362         else
 363                 # unknown archiver specified
 364                 log "$unknown_archiver" $archiver
 365                 return 1
 366         fi
 367 
 368         if [[ ! -x $archiver_command ]]; then
 369                 /usr/bin/rm -f identification
 370                 log "$cmd_not_exec" $archiver_command
 371                 return 1
 372         fi 
 373 
 374         compression=$(get_compression identification)
 375 
 376         # We're done with the identification file
 377         /usr/bin/rm -f identification
 378 
 379         # Extract archive
 380         if [[ $compression == "compress" ]]; then
 381                 /usr/bin/zcat | ppriv -e -s A=all,-sys_devices \
 382                     $archiver_command $archiver_arguments 2>/dev/null
 383         else
 384                 ppriv -e -s A=all,-sys_devices \
 385                     $archiver_command $archiver_arguments 2>/dev/null
 386         fi
 387         result=$?
 388 
 389         (( $result != 0 )) && return 1
 390 
 391         return 0 
 392 }
 393 
 394 #
 395 # Unpack cpio archive into zoneroot.
 396 #
 397 install_cpio()
 398 {
 399         stage1=$1
 400         archive=$2
 401 
 402         cpioopts="-idmfE $ipdcpiofile"
 403 
 404         vlog "cd \"$ZONEROOT\" && $stage1 \"$archive\" | "
 405         vlog "ppriv -e -s A=all,-sys_devices cpio $cpioopts"
 406 
 407         ( cd "$ZONEROOT" && $stage1 "$archive" | \
 408              ppriv -e -s A=all,-sys_devices cpio $cpioopts )
 409 }
 410 
 411 #
 412 # Unpack pax archive into zoneroot.
 413 #
 414 install_pax()
 415 {
 416         archive=$1
 417 
 418         if [[ -s $ipdpaxfile ]]; then
 419                 filtopt="-c $(/usr/bin/cat $ipdpaxfile)"
 420         fi
 421 
 422         vlog "cd \"$ZONEROOT\" && "
 423         vlog "ppriv -e -s A=all,-sys_devices pax -r -f \"$archive\" $filtopt"
 424 
 425         ( cd "$ZONEROOT" && ppriv -e -s A=all,-sys_devices \
 426             pax -r -f "$archive" $filtopt )
 427 }
 428 
 429 #
 430 # Unpack UFS dump into zoneroot.
 431 #
 432 install_ufsdump()
 433 {
 434         archive=$1
 435 
 436         vlog "cd \"$ZONEROOT\" && "
 437         vlog "ppriv -e -s A=all,-sys_devices ufsrestore rf \"$archive\""
 438 
 439         #
 440         # ufsrestore goes interactive if you ^C it.  To prevent that,
 441         # we make sure its stdin is not a terminal.
 442         # Note that there is no way to filter inherit-pkg-dirs for a full
 443         # restore so there will be warnings in the log file.
 444         #
 445         ( cd "$ZONEROOT" && ppriv -e -s A=all,-sys_devices \
 446             ufsrestore rf "$archive" < /dev/null )
 447 }
 448 
 449 #
 450 # Copy directory hierarchy into zoneroot.
 451 #
 452 install_dir()
 453 {
 454         source_dir=$1
 455 
 456         cpioopts="-pdm"
 457 
 458         first=1
 459         filt=$(for i in $(cat $ipdpaxfile)
 460                 do
 461                         echo $i | egrep -s "/" && continue
 462                         if [[ $first == 1 ]]; then
 463                                 printf "^%s" $i
 464                                 first=0
 465                         else
 466                                 printf "|^%s" $i
 467                         fi
 468                 done)
 469 
 470         list=$(cd "$source_dir" && ls -d * | egrep -v "$filt")
 471         flist=$(for i in $list
 472         do
 473                 printf "%s " "$i"
 474         done)
 475         findopts="-xdev ( -type d -o -type f -o -type l ) -print"
 476 
 477         vlog "cd \"$source_dir\" && find $flist $findopts | "
 478         vlog "ppriv -e -s A=all,-sys_devices cpio $cpioopts \"$ZONEROOT\""
 479 
 480         ( cd "$source_dir" && find $flist $findopts | \
 481             ppriv -e -s A=all,-sys_devices cpio $cpioopts "$ZONEROOT" )
 482 }
 483 
 484 # Setup i18n output
 485 TEXTDOMAIN="SUNW_OST_OSCMD"
 486 export TEXTDOMAIN
 487 
 488 e_baddir=$(gettext "Invalid '%s' directory within the zone")
 489 e_badfile=$(gettext "Invalid '%s' file within the zone")
 490 
 491 #
 492 # Exit values used by the script, as #defined in <sys/zone.h>
 493 #
 494 #       ZONE_SUBPROC_OK
 495 #       ===============
 496 #       Installation was successful
 497 #
 498 #       ZONE_SUBPROC_USAGE
 499 #       ==================
 500 #       Improper arguments were passed, so print a usage message before exiting
 501 #
 502 #       ZONE_SUBPROC_NOTCOMPLETE
 503 #       ========================
 504 #       Installation did not complete, but another installation attempt can be
 505 #       made without an uninstall
 506 #
 507 #       ZONE_SUBPROC_FATAL
 508 #       ==================
 509 #       Installation failed and an uninstall will be required before another
 510 #       install can be attempted
 511 #
 512 ZONE_SUBPROC_OK=0
 513 ZONE_SUBPROC_USAGE=253
 514 ZONE_SUBPROC_NOTCOMPLETE=254
 515 ZONE_SUBPROC_FATAL=255
 516