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 #
  27 # This script is called from /usr/lib/brand/lx/lx_install.
  28 # 
  29 # options passed down from lx_install:
  30 #       -z $ZONENAME
  31 #       -r $LINUX_ROOT
  32 #
  33 # options passed down from zoneadm -z <zone-name> install
  34 #       -d <Linux-archives-dir>
  35 #       [core | server | desktop | development | all]
  36 #
  37 # The desktop cluster will be installed by default.
  38 #
  39 
  40 # Restrict executables to /bin, /usr/bin and /usr/sbin
  41 PATH=/bin:/usr/bin:/usr/sbin
  42 export PATH
  43 
  44 
  45 # Setup i18n output
  46 TEXTDOMAIN="SUNW_OST_OSCMD"
  47 export TEXTDOMAIN
  48 
  49 # Log passed arguments to file descriptor 2
  50 log()
  51 {
  52         [[ -n $logfile ]] && echo "$@" >&2
  53 }
  54 
  55 #
  56 # Send the provided printf()-style arguments to the screen and to the
  57 # logfile.
  58 #
  59 screenlog()
  60 {
  61         typeset fmt="$1"
  62         shift
  63 
  64         printf "$fmt\n" "$@"
  65         [[ -n $logfile ]] && printf "$fmt\n" "$@" >&2
  66 }
  67 
  68 # Print and log provided text if the shell variable "verbose_mode" is set
  69 verbose()
  70 {
  71         [[ -n $verbose_mode ]] && echo "$@"
  72         [[ -n $logfile ]] && [[ -n $verbose_mode ]] && echo "$@" >&2
  73 }
  74 
  75 #
  76 # Print to the screen if the shell variable "verbose_mode" is set, but always
  77 # send the output to the log.
  78 #
  79 verboselog()
  80 {
  81         [[ -n $verbose_mode ]] && echo "$@"
  82         [[ -n $logfile ]] && echo "$@" >&2
  83 }
  84 
  85 bad_rpmdir=$(gettext "'%s' is not a valid RPM directory!")
  86 
  87 mb_req=$(gettext "(%s MB required, %s MB available)")
  88 no_space=$(gettext "Not enough free space available in '%s'")
  89 
  90 inst_clust=$(gettext "Installing cluster '%s'")
  91 unknown_clust=$(gettext "ERROR: Unknown cluster name: '%s'")
  92 
  93 unknown_media=$(gettext "Unknown or unreadable media loaded in %s")
  94 
  95 eject_fail=$(gettext "Attempt to eject '%s' failed.")
  96 
  97 lofi_failed=$(gettext "Attempt to add '%s' as lofi device FAILED.")
  98 lofs_failed=$(gettext "Attempt to lofs mount '%s' on '%s' FAILED.")
  99 
 100 media_spec=$(gettext "the provided media (%s)")
 101 
 102 distro_mediafail=\
 103 $(gettext "Attempt to determine Linux distribution from\n  %s FAILED.")
 104 
 105 mini_bootfail=$(gettext "Attempt to boot miniroot for zone '%s' FAILED.")
 106 mini_copyfail=$(gettext "Attempt to copy miniroot for zone '%s' FAILED.")
 107 mini_initfail=$(gettext "Attempt to initialize miniroot for zone '%s' FAILED.")
 108 mini_instfail=$(gettext "Attempt to install RPM '%s' to miniroot FAILED.")
 109 mini_mediafail=$(gettext "Install of zone '%s' miniroot from\n  %s FAILED.")
 110 mini_setfail=$(gettext "Attempt to setup miniroot for zone '%s' FAILED.")
 111 
 112 mini_mntfsfail=\
 113 $(gettext "Attempt to mount miniroot filesystems for zone '%s' FAILED.")
 114 
 115 rpm_initfail=\
 116 $(gettext "Attempt to initialize RPM database for zone '%s' FAILED.")
 117 
 118 symlink_failed=$(gettext "Attempt to symbolically link '%s' to '%s' FAILED.")
 119 
 120 discinfo_nofile=$(gettext "ERROR: Discinfo file '%s' not found!")
 121 discinfo_notreadable=$(gettext "ERROR: Discinfo file '%s': not readable!")
 122 discinfo_wrongarch=\
 123 $(gettext "ERROR: '%s': disc architecture is '%s'; install requires 'i386'!")
 124 
 125 wrong_serial=$(gettext "Incorrect serial number found on provided %s.")
 126 wrong_ser_expect=$(gettext "  (found #%s, expected #%s)")
 127 
 128 wrong_cd=$(gettext "Incorrect CD inserted (found %s, wanted %s)")
 129 
 130 zone_initrootfail=\
 131 $(gettext "Attempt to initialize root filesystem for zone '%s' FAILED.")
 132 
 133 zone_haltfail=$(gettext "Unable to halt zone '%s'!")
 134 zone_instfail=$(gettext "Install of zone '%s' from '%s' FAILED '%s'.")
 135 zone_mediafail=$(gettext "Install of zone '%s' from\n  %s FAILED.")
 136 
 137 zone_rootfail=\
 138 $(gettext "ERROR: The specified zone root directory '%s' could not be created.")
 139 zone_rootsub=\
 140 $(gettext "ERROR: The specified zone root subdirectory '%s' does not exist.")
 141 
 142 mk_mntfail=$(gettext "Could not create the mount directory '%s'")
 143 mountfail=$(gettext "Mount of '%s' on '%s' FAILED.")
 144 
 145 insert_discmsg=\
 146 $(gettext "Please insert %s, or a\n  %s DVD in the removable media")
 147 
 148 mount_proper_iso1=$(gettext "Please mount the ISO for %s or a")
 149 mount_proper_iso2=$(gettext "%s DVD on device '%s'")
 150 
 151 silent_nodisc=$(gettext "ERROR: Cannot install from CDs in silent mode.")
 152 silent_nolofi=\
 153 $(gettext "ERROR: Cannot install from lofi-based CD ISOs in silent mode.")
 154 
 155 install_msg=$(gettext "Installing zone '%s' from\n  %s.")
 156 install_ndiscs=\
 157 $(gettext "You will need CDs 1 - %s (or the equivalent DVD) to")
 158 install_nisos=\
 159 $(gettext "You will need ISO images representing CDs 1 - %s (or the equivalent")
 160 
 161 locate_npkgs=$(gettext "Attempting to locate %s packages...")
 162 
 163 install_one_rpm=$(gettext "Installing 1 %spackage.")
 164 install_nrpms_few=\
 165 $(gettext "Installing %s %spackages; this may take a few minutes...")
 166 install_nrpms_several=\
 167 $(gettext "Installing %s %spackages; this may take several minutes...")
 168 
 169 install_longwait=\
 170 $(gettext "NOTE: There may be a long delay before you see further output.")
 171 
 172 install_defmkfail=$(gettext "Could not create the temporary directory '%s'")
 173 install_defcpfail=$(gettext "Could not make a local copy of deferred RPM '%s'")
 174 install_dist=$(gettext "Installing distribution '%s'...")
 175 install_zonefail=$(gettext "Attempt to install zone '%s' FAILED.")
 176 
 177 no_distropath=$(gettext "ERROR: Distribution path '%s' doesn't exist.")
 178 
 179 install_done=$(gettext "Installation of %s to zone\n  '%s' completed %s.")
 180 install_failed=$(gettext "Installation of %s to zone\n  '%s' FAILED %s.")
 181 
 182 eject_final_msg=\
 183 $(gettext "Would you like the system to eject the %sinstall %s when")
 184 eject_final_prompt=$(gettext "installation of '%s' is complete? (%s)")
 185 eject_final_status=$(gettext "The %sinstall %s %s be ejected.")
 186 
 187 #
 188 # Get the device underlying a specified mounted file system and return it in
 189 # the shell variable "mount_dev"
 190 #
 191 # Returns 0 on success, 1 on failure.
 192 #
 193 get_mountdev()
 194 {
 195         typeset mount_dir="$1"
 196         typeset device
 197         unset mount_dev
 198 
 199         #
 200         # Obtain information on the specified mounted device.
 201         #
 202         device=`{ df -k "$mount_dir" | egrep "^/" ; } 2>/dev/null` || return 1
 203         mount_dev=$(echo $device | awk -e '{print $1}' 2>/dev/null)
 204 
 205         [[ "`echo $mount_dev | cut -c 1`" = "/" ]] && return 0
 206 
 207         unset mount_dev
 208         return 1
 209 }
 210 
 211 #
 212 # Get the directory name a specified device is mounted as and return it in
 213 # the shell variable "mount_dir"
 214 #
 215 # Returns 0 on success, 1 on failre.
 216 #
 217 get_mountdir()
 218 {
 219         typeset mount_dev="$1"
 220         typeset dir
 221         unset mount_dir
 222 
 223         [[ -b "$mount_dev" ]] || return 1  
 224 
 225         #
 226         # Obtain information on the specified mounted device.
 227         #
 228         dir=`{ df -k "$mount_dev" | egrep "^/" ; } 2>/dev/null` || return 1
 229         mount_dir=$(echo $dir | awk -e '{print $6}' 2>/dev/null)
 230 
 231         [[ "`echo $mount_dir | cut -c 1`" = "/" ]] && return 0
 232 
 233         unset mount_dir
 234         return 1
 235 }
 236 
 237 #
 238 # Check the free disk space of the passed filesystem against the passed
 239 # argument.
 240 #
 241 # Returns 0 on success, 1 on failure.
 242 #
 243 check_mbfree()
 244 {
 245         typeset dir="$1"
 246         typeset mb_required=$2
 247 
 248         #
 249         # Return free space in partition containing passed argument in MB
 250         #
 251         typeset mbfree=`{ LC_ALL=C df -k "$dir" | \
 252             egrep -v Filesystem ; } 2>/dev/null` || return 1
 253         mbfree=$(echo $mbfree | awk -e '{print $4}' 2>/dev/null)
 254 
 255         ((mbfree /= 1024))
 256         if ((mbfree < mb_required)); then
 257                 screenlog "$no_space" "$zoneroot"
 258                 screenlog "$mb_req" "$mb_required" "$mb_free"
 259                 return 1
 260         fi
 261         return 0
 262 }
 263 
 264 #
 265 # Find packages by attempting to expand passed RPM names to their full filenames
 266 # in the passed RPM directory.
 267 #
 268 # Arguments:
 269 #
 270 #       Argument 1:  Path to mounted install media
 271 #       Arguments [2 - n]:  RPM names to process
 272 #
 273 # The expanded filenames are returned in the shell array "rpm_names."
 274 #
 275 # For example:
 276 #
 277 #       find_packages /mnt/iso dev kernel tetex redhat-menus
 278 #
 279 # would return something like:
 280 #
 281 #       rpms_found[0]:  dev-3.3.12.3-1.centos.0.i386.rpm
 282 #       rpms_found[1]:  kernel-2.4.21-32.EL.i586.rpm
 283 #       rpms_found[2]:  tetex-1.0.7-67.7.i386.rpm
 284 #       rpms_found[3]:  redhat-menus-0.39-1.noarch.rpm
 285 #
 286 # The routine returns 0 on success, 1 on an error.
 287 #
 288 find_packages()
 289 {
 290         typeset found=0
 291         typeset left=0
 292 
 293         typeset rpmdir="$1/$rd_rpmdir"
 294         typeset curdir=${PWD:=$(pwd)}
 295 
 296         typeset arch
 297         typeset procinfo
 298         typeset rpmglob
 299         typeset rpmfile
 300 
 301         unset rpms_found
 302         unset rpms_left
 303 
 304         shift
 305         cd "$rpmdir"
 306 
 307         typeset rpmcheck="$(echo *.rpm)"
 308 
 309         if [[ "$rpmcheck" = "*.rpm" ]]; then
 310                 screenlog "$bad_rpmdir" "$rpmdir"
 311                 cd "$curdir"
 312                 return 1
 313         fi
 314 
 315         #
 316         # If the miniroot is booted, and the archs list isn't already set,
 317         # ask the zone's rpm command for the list of compatible architectures.
 318         #
 319         if [[ -n $miniroot_booted && -z $archs ]]; then
 320                 procinfo=$(zlogin "$zonename" /bin/rpm --showrc | \
 321                     grep "^compatible archs")
 322 
 323                 [[ $? -eq 0 ]] &&
 324                     archs=$(echo $procinfo | sed 's/^compatible archs : //')
 325 
 326                 [[ -n $archs ]] &&
 327                     log "RPM-reported compatible architectures: $archs"
 328         fi
 329 
 330         #
 331         # Either the miniroot isn't booted or asking rpm for the information
 332         # failed for some reason, so make some reasonable assumptions.
 333         #
 334         if [[ -z $archs ]]; then
 335                 procinfo=$(LC_ALL=C psrinfo -vp | grep family)
 336 
 337                 #
 338                 # Check for additional processor capabilities
 339                 #
 340                 if [[ "$procinfo" = *" family 6 "* ||
 341                     "$procinfo" = *" family 15 "* ||
 342                     "$procinfo" = *" family 16 "* ||
 343                     "$procinfo" = *" family 17 "* ]]; then
 344                         if [[ "$procinfo" = *AuthenticAMD* ]]; then
 345                                 #
 346                                 # Linux gives "athlon" packages precedence
 347                                 # over "i686" packages, so duplicate that
 348                                 # here.
 349                                 #
 350                                 archs="athlon i686"
 351                         else
 352                                 archs="i686"
 353                         fi
 354                 fi
 355 
 356                 archs="$archs i586 i486 i386 noarch"
 357 
 358                 log "Derived compatible architectures: $archs"
 359         fi
 360 
 361         verboselog "RPM source directory:\n  \"$rpmdir\"\n"
 362 
 363         if [[ $# -eq 1 ]]; then
 364                 msg=$(gettext "Attempting to locate 1 package...")
 365                 screenlog "$msg"
 366         else
 367                 screenlog "$locate_npkgs" "$#"
 368         fi
 369 
 370         for rpm in "$@"; do
 371                 #
 372                 # Search for the appropriate RPM, using the compatible
 373                 # architecture list contained in "archs" to look for the best
 374                 # match.
 375                 #
 376                 # For example, if the processor is an i686, and the rpm is
 377                 # "glibc", the script will look for the files (in order):
 378                 #
 379                 #    glibc[.-][0-9]*.i686.rpm
 380                 #    glibc[.-][0-9]*.i586.rpm
 381                 #    glibc[.-][0-9]*.i486.rpm
 382                 #    glibc[.-][0-9]*.i386.rpm
 383                 #    glibc[.-][0-9]*.noarch.rpm
 384                 #    glibc[.-][0-9]*.fat.rpm
 385                 #
 386                 # and will stop when it finds the first match.
 387                 #
 388                 # TODO: Once the miniroot is booted, we should verify that
 389                 #       the rpm name has been expanded to "$rpmfile" properly
 390                 #       by comparing "$rpm" and the output of:
 391                 #
 392                 #       zlogin -z <zone> /bin/rpm --qf '%{NAME}' -qp $rpmfile
 393                 #
 394                 for arch in $archs; do
 395                         #
 396                         # Use the filename globbing functionality of ksh's
 397                         # echo command to search for the file we want.
 398                         #
 399                         # If no matching file is found, echo will simply
 400                         # return the passed string.
 401                         #
 402                         rpmglob="$rpm[.-][0-9]*.$arch.rpm"
 403                         rpmfile="$(echo $rpmglob)"
 404 
 405                         [[ "$rpmfile" != "$rpmglob" ]] && break
 406 
 407                         unset rpmfile
 408                 done
 409 
 410                 if [[ -z $rpmfile ]]; then
 411                         rpms_left[$left]="$rpm"
 412                         ((left += 1))
 413                 else
 414                         rpms_found[$found]="$rpmfile"
 415                         ((found += 1))
 416                 fi
 417         done
 418 
 419         cd "$curdir"
 420         log "\"$rpmdir\": matched $found of $# packages."
 421         log "\"$rpmdir\": $left RPMs remaining."
 422         return 0
 423 }
 424 
 425 #
 426 # Build the rpm lists used to install a machine.
 427 #
 428 # The first argument is the number of discs in the distribution.  The
 429 # second, optional, argument is the metacluster to install.
 430 #
 431 # The array "distro_rpm[]" is built from the individual package RPM arrays
 432 # read in from an individual distribution definition file.
 433 #
 434 build_rpm_list()
 435 {
 436         # Default to a desktop installation
 437         typeset cluster=desktop
 438         typeset cnt=0
 439         typeset pkgs
 440 
 441         for clust in "$@"; do
 442                 ((cnt += 1))
 443                 case $clust in
 444                         core)   cluster=core ;;
 445                         desk*)  cluster=desktop ;;
 446                         serv*)  cluster=server ;;
 447                         dev*)   cluster=developer ;;
 448                         all)    cluster=all
 449                                 break;;
 450                         *)      screenlog "$unknown_clust" "$clust"
 451                                 exit $ZONE_SUBPROC_USAGE ;;
 452                 esac
 453         done
 454 
 455         if [ $cnt -gt 1 ]; then
 456                 msg=$(gettext "Too many install clusters specified")
 457                 screenlog "$msg"
 458                 exit $ZONE_SUBPROC_USAGE
 459         fi
 460 
 461         screenlog "$inst_clust" $cluster
 462 
 463         case $cluster in
 464                 core)           distro_rpms=$distro_core_rpms ;;
 465                 desktop)        distro_rpms=$distro_desktop_rpms ;;
 466                 server)         distro_rpms=$distro_server_rpms ;;
 467                 developer)      distro_rpms=$distro_developer_rpms ;;
 468                 all)            distro_rpms=$distro_all_rpms ;;
 469         esac
 470 
 471         # The RPMs in the miniroot must all be installed properly as well
 472         distro_rpms="$distro_miniroot_rpms $distro_rpms"
 473 }
 474 
 475 #
 476 # Install the "miniroot" minimal Linux environment that is booted single-user
 477 # to complete the install.
 478 #
 479 # This works by doing feeding the RPM list needed for the installation one
 480 # by one to rpm2cpio(1).
 481 #
 482 # Usage:
 483 #    install_miniroot <mounted media dir> <names of RPMS to install>
 484 #      
 485 #
 486 install_miniroot()
 487 {
 488         typeset mediadir="$1"
 489         typeset rpm
 490 
 491         shift
 492 
 493         #
 494         # There's a quirk in our version of ksh that sometimes resets the
 495         # trap handler for the shell.  Since RPM operations will be the
 496         # longest part of any given install, make sure that an interrupt while
 497         # the command is running will bring the miniroot down and clean up
 498         # the interrupted install.
 499         #
 500         trap trap_cleanup INT
 501 
 502         if [[ $# -eq 1 ]]; then
 503                 msg=$(gettext "Installing %s miniroot package...")
 504         else
 505                 msg=$(gettext "Installing %s miniroot packages...")
 506         fi
 507 
 508         screenlog "\n$msg" "$#"
 509 
 510         for rpm in "$@"; do
 511                 verboselog "\nInstalling \"$rpm\" to miniroot at\n" \
 512                         "  \"$zoneroot\"..."
 513 
 514                 rpm2cpio "$mediadir/$rd_rpmdir/$rpm" | \
 515                     ( cd "$rootdir" && cpio -idu ) 1>&2
 516 
 517                 if [[ $? -ne 0 ]]; then
 518                         screenlog "$mini_instfail" "$rpm"
 519                         return 1
 520                 fi
 521         done
 522 
 523         screenlog ""
 524         return 0
 525 }
 526 
 527 #
 528 # Install the zone from the mounted disc image by feeding a list of RPMs to
 529 # install from this image to RPM running on the zone via zlogin(1).
 530 #
 531 # Usage:
 532 #    install_zone <path to mounted install media> [<names of RPMS to install>]
 533 #
 534 # If the caller doesn't supply a list of RPMs to install, we install any
 535 # we previously stashed away in the deferred RPMs directory.
 536 #
 537 install_zone()
 538 {
 539         #
 540         # Convert the passed install media pathname to a zone-relative path
 541         # by stripping $rootpath from the head of the path.
 542         #
 543         typeset zonerpmdir="${1##$rootdir}/$rd_rpmdir"
 544 
 545         typeset defdir="$rootdir/var/lx_install/deferred_rpms"
 546         typeset mounted_root="$1"
 547         typeset rpmopts="-i"
 548 
 549         typeset defer
 550         typeset deferred_found
 551         typeset install_rpms
 552         typeset nrpms
 553         typeset rpm
 554         typeset rpmerr
 555 
 556         shift
 557 
 558         #
 559         # If the caller provided a list of RPMs, determine which of them
 560         # should be installed now, and which should be deferred until
 561         # later.
 562         #
 563         if [[ $# -gt 0 ]]; then
 564                 if [[ -n $deferred_rpms ]]; then
 565                         [[ -d $defdir ]] || if ! mkdir -p $defdir; then
 566                                 screenlog "$install_defmkfail" "$mntdir"
 567                                 return 1
 568                         fi
 569 
 570                         msg=$(gettext "Checking for deferred packages...")
 571                         screenlog "$msg"
 572 
 573                         find_packages "$mounted_root" $deferred_rpms
 574                         deferred_found="${rpms_found[@]}"
 575                         numdeferred=${#rpms_found[@]}
 576                 else
 577                         deferred_found=""
 578                 fi
 579 
 580                 install_rpms="$@"
 581                 nrpms=$#
 582 
 583                 #
 584                 # If this distro has any deferred RPMs, we want to simply
 585                 # copy them into the zone instead of installing them.  We
 586                 # then remove them from the list of RPMs to be installed on
 587                 # this pass.
 588                 #
 589                 for rpm in $deferred_found; do
 590                         if echo "$install_rpms" | egrep -s "$rpm"; then
 591                                 verboselog "Deferring installation of \"$rpm\""
 592 
 593                                 #
 594                                 # Remove the RPM from the install_rpms list
 595                                 # and append it to the deferred_saved array
 596                                 #
 597                                 install_rpms=$(echo "$install_rpms " |
 598                                     sed "s/ $rpm / /g")
 599 
 600                                 # remove trailing spaces, if any
 601                                 install_rpms=${install_rpms%%+( )}
 602 
 603                                 deferred_saved[${#deferred_saved[@]}]="$rpm"
 604 
 605                                 if ! cp "$mounted_root/$rd_rpmdir/$rpm" \
 606                                     "$defdir"; then
 607                                         screenlog "$install_defcpfail" "$rpm"
 608                                         return 1
 609                                 fi
 610                         fi
 611 
 612                         #
 613                         # If we've deferred the installation of EVERYTHING,
 614                         # simply return success
 615                         #
 616                         [[ -z $install_rpms ]] && return 0
 617                 done
 618 
 619                 [[ -n $deferred_found ]] & verbose ""
 620         elif [[ -z $deferred_saved ]]; then
 621                 # There are no deferred RPMs to install, so we're done.
 622                 return 0
 623         else
 624                 # Install the RPMs listed in the deferred_saved array
 625                 install_rpms=${deferred_saved[@]}
 626                 nrpms=${#deferred_saved[@]}
 627                 zonerpmdir=/var/lx_install/deferred_rpms
 628                 defer="deferred "
 629         fi
 630 
 631         #
 632         # There's a quirk in our version of ksh that sometimes resets the
 633         # trap handler for the shell.  Since RPM operations will be the
 634         # longest part of any given install, make sure that an interrupt while
 635         # the command is running will bring the miniroot down and clean up
 636         # the interrupted install.
 637         #
 638         trap trap_cleanup INT
 639 
 640         #
 641         # Print a message depending on how many RPMS we have to install.
 642         #
 643         # 25 RPMS seems like a reasonable boundary between when an install may
 644         # take a "few" or "several" minutes; this may be tuned if needed.
 645         #
 646         screenlog ""
 647 
 648         if [[ $nrpms -eq 1 ]]; then
 649                 screenlog "$install_one_rpm" "$defer"
 650         elif [[ $nrpms -lt 25 ]]; then
 651                 screenlog "$install_nrpms_few" "$nrpms" "$defer"
 652         else
 653                 screenlog "$install_nrpms_several" "$nrpms" "$defer"
 654 
 655                 #
 656                 # For installs of over 600 packages or so, it can take rpm a
 657                 # really, REALLY long time to output anything, even when
 658                 # running in verbose mode.
 659                 #
 660                 # For example, when doing an "all" install from a DVD or DVD
 661                 # ISO, depending on the speed of the optical drive and the
 662                 # speed of the machine's CPU(s), it may be up to TEN MINUTES or
 663                 # MORE before rpm prints out its "Processing..." message even
 664                 # though it is, in fact, processing the entire package list,
 665                 # checking for dependencies (something it is unfortunately
 666                 # entirely silent about.)
 667                 #
 668                 # Since the user might otherwise think the install was hung
 669                 # when running in verbose mode, warn them that it could be
 670                 # quite a while before they see any further output from the
 671                 # installer.
 672                 #
 673                 #
 674                 [[ $nrpms -gt 600 ]] && verbose "$install_longwait"
 675         fi
 676 
 677         log ""
 678         log "Installing: $install_rpms"
 679         log ""
 680         log "NOTE:  Any messages appearing below prefixed with \"warning:\""
 681         log "       and/or that do not cause the installer to abort the"
 682         log "       installation process may safely be ignored."
 683         log ""
 684 
 685         echo
 686 
 687         # If verbose mode is selected, run rpm in verbose mode as well.
 688         [[ -n $verbose_mode ]] && rpmopts="-ivh"
 689 
 690         #
 691         # LX_INSTALL must be defined when running this command in order to
 692         # enable switches built into various emulated system calls to allow
 693         # the dev package (which may not actually write to /dev) to function.
 694         #
 695         zlogin "$zonename" "( cd "$zonerpmdir" ; LX_INSTALL=1 \
 696             /bin/rpm $rpmopts --force --aid --nosignature --root /a \
 697             $install_rpms )"
 698 
 699         rpmerr=$?
 700 
 701         if [[ $rpmerr -ne 0 ]]; then
 702                 log ""
 703                 log "Zone rpm install command exited abnormally, code $rpmerr"
 704                 log ""
 705 
 706                 screenlog "$zone_instfail" "$zonename" "$zonerpmdir" "$rpmerr"
 707                 return 1
 708         fi
 709 
 710         log ""
 711         log "$nrpms package(s) installed."
 712 
 713         return 0
 714 }
 715 
 716 #
 717 # Attempt to unmount all file systems passed on the command line
 718 #
 719 # Returns 0 if all umounts succeeded, otherwise the number of umount failures
 720 #
 721 umount_list()
 722 {
 723         typeset failures=0
 724         typeset mounted
 725 
 726         unset umount_failures
 727 
 728         for mounted in "$@"; do
 729                 if ! umount "$mounted"; then
 730                         umount_failures="$umount_failures $mounted"
 731                         ((failures += 1))
 732                 fi
 733         done
 734 
 735         return $failures
 736 }
 737 
 738 #
 739 #
 740 # Set up lofi mounts required for chroot(1M) to work on a new root directory
 741 # located in /a within a zone.
 742 #
 743 newroot_lofimnt()
 744 {
 745         typeset dev
 746         typeset mounted
 747         typeset target
 748 
 749         unset newroot_mounted
 750 
 751         #
 752         # /usr and /lib get lofs mounted in the zone on /native read-only
 753         #
 754         # $zoneroot/dev gets lofs mounted on /native/dev read/write to allow
 755         # the use of native devices.
 756         #
 757         mount -F lofs -r /lib "$rootdir/a/native/lib" || return 1
 758         newroot_mounted="$rootdir/a/native/lib"
 759 
 760         if ! mount -F lofs -r /usr "$rootdir/a/native/usr"; then
 761                 umount "$rootdir/a/native/lib"
 762                 unset newroot_mounted
 763                 return 1
 764         fi
 765 
 766         newroot_mounted="$newroot_mounted $rootdir/a/native/usr"
 767 
 768         if ! mount -F lofs "$zoneroot/root/native/dev" \
 769             "$rootdir/a/native/dev"; then
 770                 umount_list $newroot_mounted
 771                 unset newroot_mounted
 772                 return 1
 773         fi
 774 
 775         newroot_mounted="$newroot_mounted $rootdir/a/native/dev"
 776 
 777         #
 778         # This is a bit ugly; to provide device access within the chrooted
 779         # environment RPM will use for its install, we will create the same
 780         # symlinks "$rootdir/dev" contains in the new dev directory, and will
 781         # lofs mount the balance of "$rootdir/dev" into the same locations in
 782         # /dev in the new filesystem we're installing to.
 783         #
 784         for dev in "$zoneroot"/root/dev/*
 785         do
 786                 if [[ "$dev" = "$zoneroot/root/dev/*" ]]; then
 787                         log "ERROR: No files found in $zoneroot/root/dev"
 788                         umount_list $newroot_mounted
 789                         return 1
 790                 fi
 791 
 792                 target="$rootdir/a/dev/$(basename $dev)"
 793 
 794                 #
 795                 # If the device file is a symbolic link, create a new link
 796                 # in the target directory with the same source.
 797                 #
 798                 # If the device file is any other file or directory, lofs
 799                 # mount it from the device directory into the target directory.
 800                 #
 801                 if [[ -h $dev ]]; then
 802                         typeset source=$(LC_ALL=C file -h "$dev")
 803 
 804                         #
 805                         # Remove extraneous text from the output of file(1) so
 806                         # we're left only with the target path of the symbolic
 807                         # link.
 808                         #
 809                         source="${source##*link to }"
 810 
 811                         [[ -a "$target" ]] && /bin/rm -f "$target"
 812 
 813                         if ! ln -s "$source" "$target"; then
 814                                 screenlog "$symlink_failed" "$source" "$target"
 815                                 umount_list $newroot_mounted
 816                                 unset newroot_mounted
 817                                 return 1
 818                         fi
 819                 else
 820                         [[ ! -a "$target" ]] && touch "$target"
 821 
 822                         if ! mount -F lofs "$dev" "$target"; then
 823                                 screenlog "$lofs_failed" "$dev" "$target"
 824                                 umount_list $newroot_mounted
 825                                 unset newroot_mounted
 826                                 return 1
 827                         fi
 828 
 829                         newroot_mounted="$newroot_mounted $target"
 830                 fi
 831 
 832         done
 833 
 834         return 0
 835 }
 836 
 837 #
 838 # Replace the root directory of a zone with the duplicate previously created
 839 # in the zone's /a directory.
 840 #
 841 replace_miniroot()
 842 {
 843         #
 844         # The zoneadm halt will automatically unmount any file systems
 845         # mounted via lofs in the zone, so that saves us from having to
 846         # methodically unmount each one.
 847         #
 848         if ! zoneadm -z "$zonename" halt; then
 849                 screenlog "$zone_haltfail" "$zonename"
 850                 return 1
 851         fi
 852 
 853         unset miniroot_booted
 854         unset newroot_mounted
 855 
 856         [[ -d "$zoneroot/a" ]] && rm -rf "$zoneroot/a"
 857         [[ -d "$zoneroot/oldroot" ]] && rm -rf "$zoneroot/oldroot"
 858 
 859         #
 860         # Copy the logfile or we'll lose all details of the install into the
 861         # new root directory, so strip "$zoneroot" off the pathname of the
 862         # current logfile and use it to generate the pathname of the log file
 863         # in the new root directory.
 864         #
 865         [[ -n $logfile && -f "$logfile" ]] &&
 866             cp "$logfile" "$rootdir/a${logfile##$rootdir}"
 867 
 868         mv -f "$rootdir/a" "$zoneroot/a" || return 1
 869         mv -f "$rootdir" "$zoneroot/oldroot" || return 1
 870         mv -f "$zoneroot/a" "$rootdir" || return 1
 871 
 872         #
 873         # After the directory munging above, we've moved the new copy of the
 874         # logfile atop the logfile we WERE writing to, so if we don't reopen
 875         # the logfile here the shell will continue writing to the old logfile's
 876         # inode, meaning we would lose all log information from this point on.
 877         #
 878         [[ -n $logfile ]] && exec 2>>"$logfile"
 879 
 880         rm -rf "$zoneroot/oldroot"
 881 
 882         #
 883         # Remove the contents of the /dev directory created by the install.
 884         #
 885         # We don't technically need to do this, but the zone infrastructure
 886         # will mount $zoneroot/dev atop $rootdir/dev anyway, hiding its
 887         # contents so we may as well clean up after ourselves.
 888         #
 889         # The extra checks are some basic paranoia due to the potentially
 890         # dangerous nature of this command but are not intended to catch all
 891         # malicious cases
 892         #
 893         [[ "$rootdir" != "" && "$rootdir" != "/" ]] && rm -rf "$rootdir"/dev/*
 894 
 895         return 0
 896 }
 897 
 898 setup_miniroot()
 899 {
 900         unset miniroot_booted
 901 
 902         if ! "$cwd/lx_init_zone" "$rootdir" mini; then
 903                 screenlog "$mini_initfail" "$zonename"
 904                 return 1
 905         fi
 906 
 907         if ! copy_miniroot; then
 908                 screenlog "$mini_copyfail" "$zonename"
 909                 return 1
 910         fi
 911 
 912         #
 913         # zoneadm gets upset if the zone root directory is group or world
 914         # readable or executable, so make sure it isn't before proceeding.
 915         #
 916         chmod 0700 "$zoneroot"
 917 
 918         msg=$(gettext "Booting zone miniroot...")
 919         screenlog "$msg"
 920 
 921         if ! zoneadm -z "$zonename" boot -f; then
 922                 screenlog "$mini_bootfail" "$zonename"
 923                 return 1
 924         fi
 925 
 926         miniroot_booted=1
 927 
 928         #
 929         # Now that the miniroot is booted, unset the compatible architecture
 930         # list that find_packages was using for the miniroot so that it will
 931         # get the list from rpm for the full install.
 932         #
 933         unset archs
 934 
 935         #
 936         # Mount all the filesystems needed to install the new root
 937         # directory.
 938         #
 939         if ! newroot_lofimnt; then
 940                 screenlog "$mini_mntfsfail" "$zonename"
 941 
 942                 if [[ -n $newroot_mounted ]]; then
 943                         umount_list $newroot_mounted
 944                         unset newroot_mounted
 945                 fi
 946                 return 1
 947         fi
 948 
 949         #
 950         # Attempt to initialize the RPM database for the new zone
 951         #
 952         if ! zlogin "$zonename" /bin/rpm --initdb --root /a; then
 953                 screenlog "$rpm_initfail" "$zonename"
 954                 return 1
 955         fi
 956 
 957         msg=$(gettext "Miniroot zone setup complete.")
 958         screenlog "$msg"
 959         return 0
 960 }
 961 
 962 finish_install()
 963 {
 964         #
 965         # Perform some last cleanup tasks on the newly installed zone.
 966         #
 967         # Note that the zlogin commands aren't checked for errors, as the
 968         # newly installed zone will still boot even if the commands fail.
 969         #
 970         typeset file
 971 
 972         typeset defdir=$rootdir/var/lx_install/deferred_rpms
 973 
 974         msg=$(gettext "Completing installation; this may take a few minutes.")
 975         screenlog "$msg"
 976 
 977         if [[ -d $defdir ]]; then
 978                 rm -f $defdir/*.rpm
 979                 rmdir $defdir
 980         fi
 981 
 982         # Run ldconfig in the new root
 983         zlogin "$zonename" /usr/sbin/chroot /a \
 984             /sbin/ldconfig -f /etc/ld.so.conf
 985 
 986         #
 987         # Create the /etc/shadow and /etc/gshadow files if they don't already
 988         # exist
 989         #
 990         [[ -a "$rootdir/a/etc/shadow" ]] ||
 991             zlogin "$zonename" /usr/sbin/chroot /a /usr/sbin/pwconv
 992 
 993         [[ -a "$rootdir/a/etc/gshadow" ]] ||
 994             zlogin "$zonename" /usr/sbin/chroot /a /usr/sbin/grpconv
 995 
 996         #
 997         # Make sure all init.d and rc[0-6].d links are set up properly.
 998         #
 999         for file in `ls "$rootdir/a/etc/init.d"`; do
1000                 zlogin "$zonename" /usr/sbin/chroot /a \
1001                     /sbin/chkconfig --del $file > /dev/null 2>&1
1002 
1003                 zlogin "$zonename" /usr/sbin/chroot /a \
1004                     /sbin/chkconfig --add $file > /dev/null 2>&1
1005         done
1006 
1007         replace_miniroot
1008 
1009         rmdir -ps "$media_mntdir"
1010 
1011         if ! "$cwd/lx_init_zone" "$rootdir"; then
1012                 screenlog "$zone_initrootfail" "$zonename"
1013                 return 1
1014         fi
1015 
1016         return 0
1017 }
1018 
1019 #
1020 # Duplicate the installed "miniroot" image in a subdirectory of the base
1021 # directory of the zone.
1022 #
1023 # This is done so that a new root directory can be created that will be used
1024 # as the root of a chrooted directory that RPM running on the zone will install
1025 # into.
1026 #
1027 copy_miniroot()
1028 {
1029         #
1030         # Create the directory $zoneroot/a if it doesn't already exist
1031         #
1032         [[ -d "$zoneroot/a" ]] ||
1033                 { mkdir -p "$zoneroot/a" || return 1 ; }
1034 
1035         msg=$(gettext "Duplicating miniroot; this may take a few minutes...")
1036         screenlog "$msg"
1037 
1038         #
1039         # Duplicate the miniroot to /a, but don't copy over any /etc/rc.d or
1040         # lxsave_ files.
1041         #
1042         ( cd "$rootdir"; find . -print | egrep -v "/etc/rc\.d|lxsave_" | \
1043             cpio -pdm ../a )
1044 
1045         [[ -d "$rootdir/a" ]] && rm -rf "$rootdir/a" 2>/dev/null
1046         mv -f "$zoneroot/a" "$rootdir/a" || return 1
1047 
1048         return 0
1049 }
1050 
1051 #
1052 # Read the first six lines of the  .discinfo file from the root of the passed
1053 # disc directory (which should either be a mounted disc or ISO file.)
1054 #
1055 # The read lines will be used to set appropriate shell variables on success:
1056 #
1057 #     rd_line[0]:  Disc Set Serial Number (sets rd_serial)
1058 #     rd_line[1]:  Distribution Release Name (sets rd_release)
1059 #     rd_line[2]:  Distribution Architecture (sets rd_arch)
1060 #     rd_line[3]:  Disc Number$[s] in Distribution (sets rd_cdnum)
1061 #     rd_line[4]:  "base" directory for disc (currently unused)
1062 #     rd_line[5]:  RPM directory for disc (sets rd_rpmdir)
1063 #
1064 # Returns 0 on success, 1 on failure.
1065 #
1066 read_discinfo()
1067 {
1068         typeset rd_file="$1/.discinfo"
1069 
1070         unset rd_arch
1071         unset rd_cdnum
1072         unset rd_disctype
1073         unset rd_pers
1074         unset rd_release
1075         unset rd_rpmdir
1076         unset rd_serial
1077 
1078         #
1079         # If more than one argument was passed to read_discinfo, the second
1080         # is a flag meaning that we should NOT print a warning message if
1081         # we don't find a .discinfo file, as this is just a test to see if
1082         # a distribution ISO is already mounted on the passed mount point.
1083         #
1084         if [[ ! -f "$rd_file" ]]; then
1085                 [[ $# -eq 1 ]] &&
1086                     screenlog "$discinfo_nofile" "$rd_file"
1087                 return 1
1088         fi
1089 
1090         verbose "Attempting to read \"$rd_file\"..."
1091 
1092         if [[ ! -r "$rd_file" ]]; then
1093                 screenlog "$discinfo_notreadable" "$rd_file"
1094                 return 1
1095         fi
1096 
1097         typeset rd_line
1098         typeset linenum=0
1099 
1100         while read -r rd_line[$linenum]; do
1101                 #
1102                 # If .discinfo architecture isn't "i386," fail here as
1103                 # we only support i386 distros at this time.
1104                 #
1105                 if [[ $linenum = 2 && "${rd_line[2]}" != "i386" ]]; then
1106                         screenlog "$discinfo_wrongarch" "$rd_file" \
1107                             "${rd_line[2]}"
1108                         return 1
1109                 fi
1110 
1111                 #
1112                 # We've successfully read the first six lines of .discinfo
1113                 # into $rd_line, so do the appropriate shell variable munging.
1114                 #
1115                 if ((linenum == 5)); then
1116                         rd_serial=${rd_line[0]}
1117                         rd_release=${rd_line[1]}
1118 
1119                         # CentOS names their releases "final"
1120                         [[ "$rd_release" = "final" ]] && rd_release="CentOS"
1121 
1122                         #
1123                         # Line four of the .discinfo file contains either a
1124                         # single disc number for a CD or a comma delimited list
1125                         # representing the CDs contained on a particular DVD.
1126                         #
1127                         rd_cdnum=${rd_line[3]}
1128 
1129                         if [[ "$rd_cdnum" = *,* ]]; then
1130                                 rd_disctype="DVD"
1131                         else
1132                                 rd_disctype="CD"
1133                         fi
1134 
1135                         rd_rpmdir=${rd_line[5]}
1136 
1137                         #
1138                         # If the specified RPM directory doesn't exist, this is
1139                         # not a valid binary RPM disc (it's most likely a
1140                         # source RPM disc), so don't add it to the list of
1141                         # valid ISO files.
1142                         #
1143                         [[ ! -d "$1/$rd_rpmdir" ]] && return 1
1144 
1145                         if [[ "$rd_cdnum" = "1" &&
1146                            "$rd_release" = "Red Hat"* ]]; then
1147                                 typeset rh_glob
1148 
1149                                 #
1150                                 # If this is a Red Hat release, get its
1151                                 # personality name from the name of the
1152                                 # redhat-release RPM package.
1153                                 #
1154                                 # Start by looking for the file
1155                                 # "redhat-release-*.rpm" in the directory
1156                                 # RedHat/RPMS of the ISO we're examining by
1157                                 # using ksh's "echo" command to handle
1158                                 # filename globbing.
1159                                 #
1160                                 # If no matching file is found, echo will
1161                                 # simply return the passed string.
1162                                 #
1163                                 rh_glob="$1/RedHat/RPMS/redhat-release-*.rpm"
1164                                 rd_pers="$(echo $rh_glob)"
1165 
1166                                 if [[ "$rd_pers" != "$rh_glob" ]]; then
1167                                         #
1168                                         # An appropriate file was found, so
1169                                         # extract the personality type from the
1170                                         # filename.
1171                                         #
1172                                         # For example, the presence of the file:
1173                                         #
1174                                         #   redhat-release-3WS-13.5.1.i386.rpm
1175                                         #
1176                                         # would indicate the ISO either
1177                                         # represents a "WS" personality CD or
1178                                         # a "WS" installation DVD.
1179                                         #
1180                                         # Start the extraction by deleting the
1181                                         # pathname up to the personality type.
1182                                         #
1183                                         rh_glob="*/redhat-release-[0-9]"
1184                                         rd_pers="${rd_pers##$rh_glob}"
1185 
1186                                         #
1187                                         # Now remove the trailing portion of the
1188                                         # pathname to leave only the personality
1189                                         # type, such as "WS" or "ES."
1190                                         #
1191                                         rd_pers="${rd_pers%%-*\.rpm}"
1192                                 else
1193                                         unset rd_pers
1194                                 fi
1195                         fi
1196 
1197                         return 0
1198                 fi
1199 
1200                 ((linenum += 1))
1201         done < "$rd_file"
1202 
1203         #
1204         # The file didn't have at least six lines, so indicate that parsing
1205         # failed.
1206         #
1207         return 1
1208 }
1209 
1210 #
1211 # Mount install media within the zone.
1212 #
1213 # The media will be mounted at $zoneroot/root/media, either via a loopback
1214 # mount (if it's a managed removable disc) or directly (if the media is an ISO
1215 # file or if the specified filename is a block device.)
1216 #
1217 # Returns 0 on success, 1 on failure, 2 if no disc was available
1218 #
1219 mount_install_media()
1220 {
1221         typeset device="$1"
1222         typeset mount_err
1223 
1224         unset removable
1225         unset zone_mounted
1226 
1227         [[ -z $mntdir ]] && return 1
1228 
1229         [[ -d $mntdir ]] || if ! mkdir -p $mntdir; then
1230                 screenlog "$mk_mntfail" "$mntdir"
1231                 unset mntdir
1232                 return 1
1233         fi
1234 
1235         if [[ "$install_media" = "disc" && "$managed_removable" = "1" ]]; then
1236                 #
1237                 # The removable disc device is an automatically managed one,
1238                 # so just wait for the device mounter to notice a disc has been
1239                 # inserted into the drive and for the disc to appear at the
1240                 # mount point.
1241                 #
1242                 typeset mount_interval=2
1243                 typeset mount_timeout=10
1244                 typeset mount_timer=0
1245 
1246                 typeset nickname=$(basename $device)
1247 
1248                 eject -q "$nickname" > /dev/null 2>&1 || return 2
1249                 removable="$nickname"
1250 
1251                 #
1252                 # Double check that the device was mounted.  If it wasn't, that
1253                 # usually means the disc in the drive isn't in a format we can
1254                 # read or the physical disc is unreadable in some way.
1255                 #
1256                 # The mount_timer loop is needed because the "eject -q" above
1257                 # may report a disc is available before the mounter associated
1258                 # with the drive actually gets around to mounting the device,
1259                 # so we need to give it a chance to do so.  The mount_interval
1260                 # allows us to short-circuit the timer loop as soon as the
1261                 # device is mounted.
1262                 #
1263                 while ((mount_timer < mount_timeout)); do
1264                         [[ -d "$device" ]] && break
1265 
1266                         sleep $mount_interval
1267                         ((mount_timer += mount_interval))
1268                 done
1269 
1270                 if [[ ! -d "$device" ]]; then
1271                         screenlog "\n$unknown_media" "$device"
1272                         return 2
1273                 fi
1274 
1275                 mount -F lofs -r "$device" "$mntdir"
1276                 mount_err=$?
1277         else
1278                 #
1279                 # Attempt to mount the media manually.
1280                 #
1281                 # First, make sure the passed device name really IS a device.
1282                 #
1283                 [[ -b "$device" ]] || return 2
1284 
1285                 #
1286                 # Now check to see if the device is already mounted and lofi
1287                 # mount the existing mount point into the zone if it is.
1288                 #
1289                 if get_mountdir "$device"; then
1290                         mount -F lofs -r "$mount_dir" "$mntdir"
1291                         mount_err=$?
1292                 else
1293                         [[ "$install_media" = "disc" ]] && removable="$device"
1294 
1295                         # It wasn't mounted, so go ahead and try to do so.
1296                         mount -F hsfs -r "$device" "$mntdir" 
1297                         mount_err=$?
1298                 fi
1299 
1300                 # A mount_err of 33 means no suitable media was found
1301                 ((mount_err == 33)) && return 2
1302         fi
1303 
1304         if ((mount_err != 0)); then
1305                 screenlog "$mountfail" "$device" "$mntdir"
1306                 unset mntdir
1307                 return 1
1308         fi
1309 
1310         zone_mounted="$mntdir"
1311         verbose "Mount of \"$device\" on \"$mntdir\" succeeded."
1312         return 0
1313 }
1314 
1315 # Eject the disc mounted on the passed directory name
1316 eject_removable_disc()
1317 {
1318         screenlog ""
1319         verbose "  (Attempting to eject '$removable'... \c"
1320 
1321         if [[ -n $zone_mounted ]]; then
1322                 umount "$zone_mounted"
1323                 unset zone_mounted
1324         fi
1325 
1326         if ! eject "$removable"; then
1327                 verbose "failed.)\n"
1328                 screenlog "$eject_fail" "$removable"
1329 
1330                 msg=$(gettext "Please eject the disc manually.")
1331                 screenlog "$msg"
1332         else
1333                 verbose "done.)\n"
1334         fi
1335 
1336         unset removable
1337 }
1338 
1339 #
1340 # Ask for the user to provide a disc or ISO.
1341 #
1342 # Returns 0 on success, 1 on failure.
1343 #
1344 prompt_for_media()
1345 {
1346         # No prompting is allowed in silent mode.
1347         if [[ -n $silent_mode ]]; then
1348                 log "$silent_err_msg"
1349                 return 1
1350         fi
1351 
1352         if [[ "$1" != "" ]]; then
1353                 msg="$release_name, CD $1"
1354         else
1355                 typeset disc=$(gettext "disc")
1356 
1357                 msg=$(gettext "any")
1358                 msg="$msg $release_name $disc"
1359         fi
1360 
1361         if [[ "$install_media" = "disc" ]]; then
1362                 screenlog "$insert_discmsg" "$msg" "$release_name"
1363 
1364                 msg=$(gettext "drive and press <RETURN>.")
1365                 screenlog "  $msg"
1366 
1367                 [[ -n $removable ]] && eject_removable_disc
1368         else
1369                 if [[ -n $zone_mounted ]]; then
1370                         umount "$mntdir"
1371                         unset zone_mounted
1372                 fi
1373 
1374                 #
1375                 # This is only be printed in the case of a user
1376                 # specifying a device name as an install medium.
1377                 # This is handy for testing the installer or if the user
1378                 # has ISOs stored in some strange way that somehow
1379                 # breaks the "install from ISO" mechanism, as ISOs
1380                 # can be manually added using lofiadm(1M) command and
1381                 # the resulting lofi device name passed to the
1382                 # installer.
1383                 #
1384                 screenlog "$mount_proper_iso1" "$msg"
1385                 screenlog "  $mount_proper_iso2" "$release_name" "$mntdev"
1386 
1387                 msg=$(gettext "and press <RETURN>.")
1388                 screenlog "  $msg"
1389         fi
1390 
1391         read && return 0
1392         
1393         return 1
1394 }
1395 
1396 #
1397 # Get a particular CD of a multi-disc set.
1398 #
1399 # This basically works by doing the following:
1400 #
1401 #     1) Mount the disc
1402 #     2) Read the disc's .discinfo file to see which CD it is or represents
1403 #     3) If it doesn't contain the desired CD, ask the user for a disc
1404 #        containing the CD we wanted.
1405 #
1406 # Returns 0 on success, 1 on failure.
1407 #
1408 get_cd()
1409 {
1410         typeset mntdev="$1"
1411 
1412         typeset cdnum
1413         typeset discname
1414         typeset enter
1415         typeset mount_err
1416         typeset prompted
1417 
1418 
1419         if [[ $# -eq 2 ]]; then
1420                 # Caller specified a particular CD to look for
1421                 cdnum="$2"
1422                 discname="$release_name, CD $cdnum"
1423         else
1424                 # Caller wanted any disc
1425                 discname="a $release_name disc"
1426         fi
1427 
1428         verboselog "\nChecking for $discname on device"
1429         verboselog "  \"$mntdev\"\n"
1430 
1431         while :; do
1432                 # Check to see if a distro disc is already mounted
1433                 mntdir="$media_mntdir"
1434 
1435                 unset rd_disctype
1436                 if ! read_discinfo "$mntdir" "test"; then
1437                         mount_install_media "$mntdev"
1438                         mount_err=$?
1439 
1440                         #
1441                         # If the mount succeeded, continue on in the main
1442                         # script
1443                         #
1444                         if ((mount_err == 0)); then
1445                                 read_discinfo "$mntdir"
1446                         elif ((mount_err == 2)); then
1447                                 # No medium was found, so prompt for one.
1448                                 prompt_for_media "$cdnum" && prompted=1 continue
1449 
1450                                 unset mntdir
1451                                 return 1
1452                         else
1453                                 # mount failed
1454                                 unset mntdir
1455                                 return 1
1456                         fi
1457                 fi
1458 
1459                 if [[ -n $distro_serial && 
1460                     "$rd_serial" != "$distro_serial" ]]; then
1461                         screenlog "$wrong_serial" "$install_disctype"
1462                         screenlog "  $wrong_ser_expect" "$rd_serial" \
1463                             "$distro_serial"
1464 
1465                         #
1466                         # If we're installing from ISOs, don't prompt the user
1467                         # if the wrong serial number is present, as there's
1468                         # nothing they can do about it.
1469                         #
1470                         [[ "$install_media" = "ISO" ]] && return 1
1471 
1472                         prompt_for_media "$cdnum" && continue
1473 
1474                         umount "$mntdir"
1475                         unset zone_mountdir
1476                         return 1
1477                 fi
1478 
1479                 #
1480                 # Make sure that the mounted media is CD $cdnum.
1481                 #
1482                 # If it is, return to the caller, otherwise eject the
1483                 # disc and try again. 
1484                 #
1485                 if [[ "$rd_disctype" = "CD" ]]; then
1486                         verboselog "Found CD #$rd_cdnum," \
1487                             "Serial #$rd_serial"
1488                         verboselog "Release Name \"$rd_release\""
1489 
1490                         [[ -n $rd_pers ]] &&
1491                             verboselog "Detected RedHat Personality" \
1492                                 "\"$rd_pers\""
1493 
1494                         verboselog ""
1495 
1496                         # If we didn't care which CD it was, return success
1497                         [[ "$cdnum" = "" ]] && return 0
1498 
1499                         # Return if the CD number read is a match
1500                         [[ "$rd_cdnum" = "$cdnum" ]] && return 0
1501                 else
1502                         verboselog "\nFound DVD (representing CDs" \
1503                             "$rd_cdnum), Serial #$rd_serial"
1504                         verboselog "Release Name \"$rd_release\"\n"
1505 
1506                         [[ -n $rd_pers ]] &&
1507                             verboselog "Detected RedHat Personality" \
1508                                 "\"$rd_pers\""
1509 
1510                         verboselog ""
1511 
1512                         # If we didn't care which CD it was, return success
1513                         [[ "$cdnum" = "" ]] && return 0
1514 
1515                         #
1516                         # Since a DVD represents multiple CDs, make sure the
1517                         # DVD inserted represents the CD we want.
1518                         #
1519                         { echo "$rd_cdnum," | egrep -s "$cdnum," ; } &&
1520                             return 0
1521                 fi
1522 
1523                 if [[ -n $prompted ]]; then
1524                         if [[ "$rd_disctype" = "CD" ]]; then
1525                                 screenlog "$wrong_cd" "$rd_cdnum" "$cdnum"
1526                         else
1527                                 msg=$(gettext "Incorrect DVD inserted.")
1528                                 screenlog "$msg"
1529 
1530                                 log "(DVD represented CDs $rd_cdnum," \
1531                                     " wanted CD $cdnum)"
1532                         fi
1533                 fi
1534 
1535                 #
1536                 # If we're installing from ISOs, don't prompt the user if the
1537                 # wrong CD is mounted, as there's nothing they can do about it.
1538                 #
1539                 [[ "$install_media" = "ISO" ]] && return 1
1540 
1541                 prompt_for_media "$cdnum" && prompted=1 && continue
1542 
1543                 umount "$mntdir"
1544                 unset zone_mountdir
1545                 return 1
1546         done
1547 }
1548 
1549 #
1550 # Find out which distro the mounted disc belongs to by comparing the
1551 # mounted disc's serial number against those contained in the various
1552 # distro files.
1553 #
1554 # When a match is found, the shell variable "distro_file" will be set to
1555 # the name of the matching file.  Since that will have been the last file
1556 # sourced by the shell, there's no need for the caller to do it again; the
1557 # variable is only set in case it's of some use later.
1558 #
1559 # Returns 0 on success, 1 on failure.
1560 #
1561 get_disc_distro()
1562 {
1563         typeset distro
1564         typeset distro_files="$(echo $distro_dir/*.distro)"
1565 
1566         unset distro_file
1567         
1568         [[ "$distro_files" = "$distro_dir/*.distro" ]] && return 1
1569 
1570         for distro in $distro_files; do
1571                 [[ ! -f "$distro" ]] && continue
1572                 
1573                 verbose "Checking for disc distro \"$distro\"..."
1574 
1575                 . "$distro" > /dev/null
1576 
1577                 [[ "$rd_serial" != "$distro_serial" ]] && continue
1578 
1579                 distro_file="$distro"
1580                 release_name="$rd_release $distro_version"
1581                 distro_ncds=${#distro_cdorder[@]}
1582 
1583                 return 0
1584         done
1585 
1586         return 1
1587 }
1588 
1589 #
1590 # Iterate through the install media to install the miniroot and full zone
1591 #
1592 # The install media may be physical discs, a lofi mounted ISO file, or
1593 # iso files located in a directory specified by the user.
1594 #
1595 # All installations, regardless of media type, use a CD as their basic media
1596 # unit.  DVDs or ISOs representing DVDs actually contain multiple "CDs" of
1597 # installation packages.
1598 #
1599 # The variable "distro_ncds," as set elsewhere, represents the number
1600 # of CDs required to install the distribution.  Whether the installation
1601 # actually requires multiple physical discs or ISOs depends upon their content.
1602 #
1603 # Returns 0 on success, 1 on failure.
1604 #
1605 iterate_media()
1606 {
1607         typeset cdnum=1
1608         typeset cds
1609         typeset disc_rpms
1610         typeset err_media
1611         typeset err_msg
1612         typeset install_type="$1"
1613         typeset ldevs
1614         typeset mountdev
1615         typeset rh_pers
1616 
1617         shift
1618 
1619         if [[ "$install_type" = "miniroot" ]]; then
1620                 typeset i
1621 
1622                 disc_rpms=$distro_miniroot_rpms
1623                 err_msg="$mini_mediafail"
1624 
1625                 # For miniroot installs, ask for CDs in numerical order
1626                 cds[0]="zero_pad"
1627 
1628                 for i in ${distro_cdorder[@]}; do
1629                         cds[$cdnum]=$cdnum
1630                         ((cdnum += 1))
1631                 done
1632 
1633                 cdnum=1
1634         else
1635                 disc_rpms=$distro_rpms
1636                 err_msg="$zone_mediafail"
1637 
1638                 #
1639                 # For full zone installs, ask for CDs in the order RPM needs
1640                 # to find the packages.
1641                 #
1642                 set -A cds "zero_pad" ${distro_cdorder[@]}
1643         fi
1644 
1645         if [[ "$install_media" = "ISO" ]]; then
1646                 set -A ldevs "zero_pad" "$@"
1647         else
1648                 mountdev="$1"
1649                 err_media="$release_name, CD ${cds[$cdnum]} (or DVD)"
1650         fi
1651 
1652         unset rpms_left_save
1653 
1654         while ((cdnum <= distro_ncds)); do
1655                 [[ -z ${cds[$cdnum]} ]] && ((cdnum += 1)) && continue
1656 
1657                 if [[ "$install_media" = "ISO" ]]; then
1658                         typeset isonum="${cds[$cdnum]}"
1659 
1660                         #
1661                         # If this routine was called with a single ISO device
1662                         # name, it must be a DVD, so refer to that one lofi
1663                         # device (and associated ISO pathname)
1664                         #
1665                         [[ $# -eq 1 ]] && isonum=1
1666 
1667                         err_media="ISO \"${iso_pathnames[$isonum]}\""
1668                         mountdev="${ldevs[$isonum]}"
1669                 fi
1670 
1671                 #
1672                 # If the disc needed in the install order isn't the one in
1673                 # the drive, ask for the correct one.
1674                 #
1675                 if ! get_cd "$mountdev" "${cds[$cdnum]}"; then
1676                         screenlog "$err_msg" "$zonename" "$err_media"
1677                         return 1
1678                 fi
1679 
1680                 # set the RedHat personality type, if applicable
1681                 [[ -n $rd_pers && -z $rh_pers ]] && rh_pers=$rd_pers
1682 
1683                 #
1684                 # We now know the actual type of media being used, so
1685                 # modify the "err_media" string accordingly.
1686                 #
1687                 if [[ "$install_media" = "disc" ]]; then
1688                         if [[ "$rd_disctype" = "DVD" ]]; then
1689                                 err_media="$release_name DVD"
1690                         else
1691                                 err_media="$release_name, CD ${cds[$cdnum]}"
1692                         fi
1693                 fi
1694 
1695                 find_packages "$mntdir" $disc_rpms
1696 
1697                 #
1698                 # Save a copy of $rpms_left.  Other functions clobber it.
1699                 #
1700                 rpms_left_save="${rpms_left[@]}"
1701 
1702                 if [[ -n $rpms_found ]]; then
1703                         if [[ "$install_type" = "miniroot" ]]; then
1704                                 verboselog "\nInstalling miniroot from"
1705                                 verboselog "  $err_media...\n" 
1706 
1707                                 if ! install_miniroot "$mntdir" \
1708                                     "${rpms_found[@]}"; then
1709                                         screenlog "$err_msg" "$zonename" \
1710                                                 "$err_media"
1711                                         return 1
1712                                 fi
1713                         else
1714                                 screenlog "\n$install_msg\n" "$zonename" \
1715                                     "$err_media"
1716 
1717                                 if ! install_zone "$mntdir" \
1718                                     ${rpms_found[@]}; then
1719                                         screenlog "$err_msg" "$zonename" \
1720                                             "$err_media"
1721                                         return 1
1722                                 fi
1723                         fi
1724 
1725                         #
1726                         # Mark installation from this CD (or ISO representing
1727                         # this CD) as completed.
1728                         #
1729                         if [[ "$rd_disctype" = "CD" ]]; then
1730                                 unset cds[$cdnum]
1731                         fi
1732                 fi
1733 
1734                 # A DVD install takes a single disc, so stop iterating
1735                 [[ "$rd_disctype" = "DVD" ]] && break
1736 
1737                 # If there are no RPMs left, we're done.
1738                 [[ -z $rpms_left_save ]] && break
1739 
1740                 disc_rpms="$rpms_left_save"
1741                 ((cdnum += 1))
1742 
1743                 if [[ "$install_media" != "ISO" ]]; then
1744                         #
1745                         # modify the err_media variable to reflect the next
1746                         # CD in the sequence
1747                         #
1748                         err_media="$release_name, CD ${cds[$cdnum]}"
1749                 else
1750                         # Unmount the last used ISO if appropriate
1751                         if [[ -n $zone_mounted ]]; then
1752                                 umount "$zone_mounted"
1753                                 unset zone_mounted
1754                         fi
1755                 fi
1756         done
1757 
1758         if [[ -n $zone_mounted ]]; then
1759                 umount "$zone_mounted"
1760                 unset zone_mounted
1761         fi
1762 
1763         if [[ -n $rpms_left_save ]]; then
1764                 #
1765                 # Uh oh - there were RPMS we couldn't locate.  This COULD
1766                 # indicate a failed installation, but we need to check for
1767                 # a RedHat personality "missing" list first.
1768                 #
1769                 if [[ -n $rh_pers && "$rh_pers" != "AS" ]]; then
1770                         typeset missing
1771 
1772                         if [[ $rh_pers = "WS" ]]; then
1773                                 missing="$distro_WS_missing"
1774                         elif [[ $rh_pers = "ES" ]]; then
1775                                 missing="$distro_ES_missing"
1776                         fi
1777 
1778                         #
1779                         # If any packages left in "rpm_left_save" appear in the
1780                         # list of packages expected to be missing from this
1781                         # personality, remove them from the "rpm_left_save"
1782                         # list.
1783                         #
1784                         if [[ -n $missing ]]; then
1785                                 typeset pkg
1786 
1787                                 for pkg in $missing
1788                                 do
1789                                         rpm_left_save=$(echo "$rpm_left_save " |
1790                                             sed "s/$pkg //g")
1791 
1792                                         #
1793                                         # If all of the packages in
1794                                         # "rpm_left_save" appeared in this
1795                                         # personality's list of "expected
1796                                         # missing" packages, then the
1797                                         # installation completed successfully.
1798                                         #
1799                                         [[ -z ${rpm_left_save%%+( )} ]] &&
1800                                             return 0
1801                                 done
1802                         fi
1803                 fi
1804 
1805                 log "\nERROR: Unable to locate some needed packages:\n" \
1806                     "  ${rpms_left_save%%+( )}\n"
1807                 screenlog "$err_msg" "$zonename"
1808                 return 1
1809         fi
1810 
1811         return 0
1812 }
1813 
1814 #
1815 # Install a zone from installation media
1816 #
1817 # Returns 0 on success, 1 on failure
1818 #
1819 install_from_media()
1820 {
1821         msg=$(gettext "Installing miniroot for zone '%s'.")
1822         screenlog "$msg" "$zonename"
1823 
1824         iterate_media "miniroot" $@ || return 1
1825 
1826         if ! setup_miniroot; then
1827                 screenlog "$mini_setfail" "$zonename"
1828                 return 1
1829         fi
1830 
1831         msg=$(gettext "Performing full install for zone '%s'.")
1832 
1833         screenlog "\n$msg" "$zonename"
1834 
1835         iterate_media "full" $@ || return 1
1836 
1837         #
1838         # Attempt to install deferred RPMS, if any
1839         #
1840         if [[ -n $deferred_rpms ]]; then
1841                 if ! install_zone ""; then
1842                         return 1
1843                 fi
1844         fi
1845 
1846         finish_install
1847         return $?
1848 }
1849 
1850 #
1851 # Add an entry to the valid distro list.
1852 #
1853 # The passed argument is the ISO type ("CD Set" or "DVD")
1854 #
1855 add_to_distro_list()
1856 {
1857         typeset name
1858 
1859         distro_file[${#distro_file[@]}]="$distro"
1860 
1861         name="$release_name"
1862         [[ -n $redhat_pers ]] && name="$name $redhat_pers"
1863 
1864         select_name[${#select_name[@]}]="$name ($1)"
1865         release[${#release[@]}]="$release_name"
1866         iso_set[${#iso_set[@]}]="${iso_names[@]}"
1867         verboselog "Distro \"$name\" ($1) found."
1868 }
1869 
1870 #
1871 # Find out which distros we have ISO files to support
1872 #
1873 # Do this by cycling through the distro directory and reading each distro
1874 # file in turn looking for:
1875 #
1876 #     1) The number of discs in a distribution
1877 #     2) The serial number of the distribution
1878 #     3) The name of the distribution
1879 #
1880 # Based on this, we can determine based on the ISO files available which
1881 # distributions, if any, we have a complete set of files to support.
1882 #
1883 # The function returns the supported isos in the array "iso_set."
1884 #
1885 validate_iso_distros()
1886 {
1887         typeset cd
1888         typeset disctype
1889         typeset index
1890         typeset iso
1891         typeset ncds
1892         typeset pers
1893         typeset pers_cd
1894         typeset pers_index
1895         typeset serial
1896 
1897         typeset distro_files="$(echo $distro_dir/*.distro)"
1898         typeset nisos=${#iso_filename[@]}
1899 
1900         unset distro_file
1901         unset iso_set
1902         unset release
1903         unset select_name
1904 
1905         if [[ "$distro_files" = "$distro_dir/*.distro" ]]; then
1906                 msg=$(gettext "Unable to find any distro files!")
1907                 screenlog "$msg"
1908                 return
1909         fi
1910 
1911         for distro in $distro_files; do
1912                 #
1913                 # We're done if we've already processed all available ISO files
1914                 # or if there were none in the first place.
1915                 #
1916                 ((${#iso_filename[@]} == 0)) && break
1917 
1918                 [[ ! -f $distro ]] && continue
1919 
1920                 . "$distro" > /dev/null
1921                 ncds=${#distro_cdorder[@]}
1922 
1923                 unset iso_names
1924                 unset pers
1925                 unset pers_cd
1926 
1927                 verbose "\nChecking ISOs against distro file \"$distro\"..."
1928 
1929                 index=0
1930 
1931                 while ((index < nisos)); do
1932                         #
1933                         # If the filename has been nulled out, it's already
1934                         # been found as part of a distro, so continue to the
1935                         # next one.
1936                         #
1937                         if [[ -z ${iso_filename[$index]} ]]; then
1938                                 ((index += 1))
1939                                 continue
1940                         fi
1941 
1942                         iso="${iso_filename[$index]}"
1943                         serial="${iso_serial[$index]}"
1944                         release_name="${iso_release[$index]}"
1945                         redhat_pers="${iso_pers[$index]}"
1946 
1947                         verbose "  ISO \"$iso\":"
1948 
1949                         #
1950                         # If the serial number doesn't match that for
1951                         # this distro, check other ISOs
1952                         #
1953                         if [[ "$serial" != "$distro_serial" ]]; then
1954                                 ((index += 1))
1955                                 continue
1956                         fi
1957 
1958                         verbose "    Serial #$serial"
1959                         verbose "    Release Name \"$release_name\""
1960 
1961                         [[ -n ${iso_pers[$index]} ]] &&
1962                             verbose "    RedHat Personality \"$redhat_pers\""
1963 
1964                         if [[ "${iso_disctype[$index]}" = "CD" ]]; then
1965                                 disctype="CD #"
1966                                 cd="${iso_cdnum[$index]}"
1967                         else
1968                                 disctype="DVD, representing CDs #"
1969                                 cd=0
1970                         fi
1971 
1972                         verbose "    ${disctype}${iso_cdnum[$index]}\n"
1973 
1974                         #
1975                         # Once we've matched a particular distro, don't check
1976                         # this ISO to see if it's part of any other.
1977                         #
1978                         unset iso_filename[$index]
1979 
1980                         iso_names[$cd]="$iso"
1981 
1982                         #
1983                         # A DVD-based distro consists of one and ONLY one disc,
1984                         # so process it now.
1985                         #
1986                         if [[ "${iso_disctype[$index]}" = "DVD" ]]; then
1987                                 typeset dvd_discs=",${iso_cdnum[$index]}"
1988 
1989                                 cd=1 
1990                                 while ((cd <= ncds)); do
1991                                         dvd_discs=$(echo "$dvd_discs" |
1992                                             sed "s/,$cd//")
1993                                         ((cd += 1))
1994                                 done
1995 
1996                                 #
1997                                 # If no CDs are left in $dvd_discs, the DVD
1998                                 # was a complete distribution, so add it to
1999                                 # the valid distro list.
2000                                 #
2001                                 if [[ -z $dvd_discs ]]; then
2002                                         add_to_distro_list "DVD"
2003                                         unset iso_names[$cd]
2004                                 fi
2005                         elif [[ -n ${iso_pers[$index]} ]]; then
2006                                 #
2007                                 # If this is a RedHat personality CD, save off
2008                                 # some extra information about it so we can
2009                                 # discern between mutiple personality discs
2010                                 # later, if needed.
2011                                 #
2012                                 pers[${#pers[@]}]=${iso_pers[$index]}
2013                                 pers_cd[${#pers_cd[@]}]="$iso"
2014                         fi
2015 
2016                         ((index += 1))
2017                 done
2018 
2019                 #
2020                 # Check to see if we have ISOs representing a full CD set.
2021                 # If we don't, don't mark this as an available distro.
2022                 #
2023                 (( ${#iso_names[@]} != $ncds )) && continue
2024 
2025                 relase_name="$release_name $distro_version"
2026                 
2027                 if [[ -z ${pers[@]} ]]; then
2028                         #
2029                         # If there were no personality discs, just add this
2030                         # ISO set to the distro list.
2031                         #
2032                         unset redhat_pers
2033                         add_to_distro_list "CD Set"
2034                 else
2035                         #
2036                         # If a valid CD-based distro was found and there are
2037                         # RedHat personality discs for that distro present,
2038                         # create entries for each personality in the available
2039                         # distro list.
2040                         #
2041                         pers_index=0
2042 
2043                         while ((pers_index < ${#pers[@]})); do
2044                                 redhat_pers=${pers[$pers_index]}
2045 
2046                                 if [[ -n ${pers_cd[$pers_index]} ]]; then
2047                                         #
2048                                         # RedHat personality discs are always
2049                                         # disc 1 of a CD set, so if we found a
2050                                         # valid personality disc for this set,
2051                                         # set the disc 1 entry for this distro
2052                                         # to the ISO for the proper personality
2053                                         # disc.
2054                                         #
2055                                         iso_names[1]="${pers_cd[$pers_index]}"
2056                                         add_to_distro_list "CD Set"
2057                                 fi
2058 
2059                                 ((pers_index += 1))
2060                         done
2061                 fi
2062         done
2063 }
2064 
2065 #
2066 # Do a lofi add for the passed filename and set lofi_dev to the lofi
2067 # device name lofiadm created for it (e.g. "/dev/lofi/1".)
2068 #
2069 # If the passed filename already has a lofi device name, simply set lofi_dir
2070 # to the existing device name.
2071 #
2072 # Returns 0 on success, 1 on failure.
2073 #
2074 lofi_add()
2075 {
2076         typeset filename="$1"
2077 
2078         lofi_dev=$(lofiadm "$filename" 2>/dev/null) && return 0
2079         lofi_dev=$(lofiadm -a "$filename") && return 0
2080 
2081         screenlog "$lofi_failed" "$filename"
2082         return 1
2083 }
2084 
2085 #
2086 # Delete the lofi device name passed in.
2087 #
2088 # Returns 0 on success, 1 on failure.
2089 #
2090 lofi_del()
2091 {
2092         typeset dev="$1"
2093 
2094         [[ "$dev" != /dev/lofi/* ]] && return 1
2095 
2096         if lofiadm -d "$dev" 2>/dev/null; then
2097                 [[ -n $lofi_dev ]] && unset lofi_dev
2098                 return 0
2099         fi
2100 
2101         return 1 
2102 }
2103 
2104 #
2105 # Mount the lofi device name passed in.
2106 #
2107 # Set the variable mntdir to the directory on which the lofi device is
2108 # mounted.
2109 #
2110 # Returns 0 on success, 1 on failure.
2111 #
2112 lofi_mount()
2113 {
2114         typeset lofidev="$1"
2115         typeset mntpoint="$2"
2116 
2117         #
2118         # Check to see if the lofi device is already mounted and return
2119         # the existing mount point if it is.
2120         #
2121         get_mountdir "$lofidev" && { mntdir="$mount_dir" ; return 0 ; }
2122 
2123         unset mntdir
2124         if [[ ! -d  "$mntpoint" ]]; then
2125                 if ! mkdir -p "$mntpoint"; then
2126                         log "Could not create mountpoint \"$mntpoint\"!\n"
2127                         return 1
2128                 fi
2129                 lofi_created="$mntpoint"
2130         fi
2131 
2132         verbose "Attempting mount of device \"$lofidev\""
2133         verbose "  on directory \"$mntpoint\"... \c"
2134 
2135         if ! mount -F hsfs -r "$lofidev" "$mntpoint" 2>/dev/null; then
2136                 verbose "FAILED."
2137                 [[ -n $lofi_created ]] && rmdir -ps "$lofi_created" &&
2138                     unset lofi_created
2139                 return 1
2140         fi
2141 
2142         mntdir="$mntpoint"
2143         verbose "succeeded."
2144         return 0
2145 }
2146 
2147 #
2148 # Unmount the lofi device name passed in, and remove the device mount point
2149 # after unmounting the device.
2150 #
2151 # Returns 0 on success, 1 on failure.
2152 #
2153 lofi_umount()
2154 {
2155         typeset mntdev="$1"
2156 
2157         #
2158         # If the directory name passed wasn't mounted to begin with,
2159         # just return success.
2160         #
2161         get_mountdir "$mntdev" || return 0
2162 
2163         verbose "Unmounting device \"$mntdev\"... \c"
2164 
2165         if ! umount "$mntdev" ; then
2166                 verbose "FAILED."
2167                 return 1
2168         fi
2169 
2170         verbose "succeeded."
2171         return 0
2172 }
2173 
2174 # Scan the passed list of ISOs.
2175 scan_isos()
2176 {
2177         typeset iso
2178         typeset index=0
2179 
2180         unset iso_serial
2181         unset iso_release
2182         unset iso_cdnum
2183         unset iso_disctype
2184         unset iso_filename
2185         unset iso_pers
2186 
2187         for iso in "$@"; do
2188                 verbose "Checking possible ISO\n  \"$iso\"..."
2189 
2190                 if lofi_add "$iso"; then
2191                         verbose "  added as lofi device \"$lofi_dev\""
2192                         if lofi_mount "$lofi_dev" "/tmp/lxiso"; then
2193                                 if read_discinfo "$mntdir"; then
2194                                         iso_release[$index]="$rd_release"
2195                                         iso_serial[$index]="$rd_serial"
2196                                         iso_cdnum[$index]="$rd_cdnum"
2197                                         iso_disctype[$index]="$rd_disctype"
2198 
2199                                         [[ -n $rd_pers ]] &&
2200                                             iso_pers[$index]="$rd_pers"
2201 
2202                                         iso_filename[$index]="$iso"
2203                                         ((index += 1))
2204                                 fi
2205                                 lofi_umount "$lofi_dev"
2206                         else
2207                                 verbose "  not a usable ISO image."
2208                                 log "Unable to mount \"$lofi_dev\" (\"$iso\")"
2209                         fi
2210 
2211                         lofi_del "$lofi_dev"
2212                 else
2213                         verbose "  not a valid ISO image."
2214                 fi
2215         done
2216 }
2217 
2218 #
2219 # Prompt the user with the first argument, then make a menu selection
2220 # from the balance.
2221 #
2222 # This is effectively similar to the ksh "select" function, except it
2223 # outputs to stdout.
2224 #
2225 # Shell variables set:
2226 #    choice    - set to the menu number selected
2227 #    selection - set to the menu text selected
2228 #
2229 pick_one()
2230 {
2231         typeset menu_items
2232         typeset menu_index
2233         typeset reply
2234 
2235         typeset prompt="$1"
2236         shift
2237 
2238         unset choice
2239 
2240         set -A menu_items "$@"
2241 
2242         until [[ -n $choice ]]; do
2243                 menu_index=1
2244                 
2245                 echo "\n$prompt\n"
2246 
2247                 for f in "${menu_items[@]}"; do
2248                         echo "$menu_index) $f"
2249                         ((menu_index += 1))
2250                 done
2251 
2252                 echo "\n$(gettext "Please select") (1-$#): " "\c"
2253                 read reply
2254                 echo
2255 
2256                 [[ -z $reply ]] && echo && continue
2257 
2258                 #
2259                 # Reprint menu selections if the answer was not a number in
2260                 # range of the menu items available
2261                 #
2262                 [[ $reply != +([0-9]) ]] && continue
2263                 ((reply < 1)) || ((reply > $#)) && continue
2264 
2265                 choice=$reply
2266                 selection=${menu_items[((choice - 1))]}
2267         done
2268 }
2269 
2270 #
2271 # Select a distribution to install from the arguments passed and set
2272 # "ndsitro" to the value chosen - 1 (so it may be used as an array index.)
2273 #
2274 # The routine will automatically return with ndisto set to 0 if only one
2275 # argument is passed.
2276 #
2277 select_distro()
2278 {
2279         unset choice
2280         unset ndistro
2281 
2282         if (($# > 1)); then
2283                 if [[ -n $silent_mode ]]; then
2284                         typeset dist
2285 
2286                         log "ERROR: multiple distrubutions present in ISO" \
2287                                 "directory but silent install"
2288                         log "  mode specified.  Distros available:"
2289                         for dist in "$@"; do
2290                                 log "    \"$dist\""
2291                         done
2292                         return 1
2293                 fi
2294 
2295                 pick_one \
2296                     "$(gettext "Which distro would you like to install?")" \
2297                     "$@"
2298         fi
2299 
2300         #
2301         # Covers both the cases of when only one distro name is passed
2302         # to the routine as well as when an EOF is sent to the distribution
2303         # selection prompt.
2304         #
2305         if [[ -z $choice ]]; then
2306                 screenlog "$install_dist" "$1"
2307                 ndistro=0
2308         else
2309                 screenlog "$install_dist" "$selection"
2310                 ndistro=$((choice - 1))
2311         fi
2312 
2313         return 0
2314 }
2315 
2316 #
2317 # Install a zone from discs or manually lofi-mounted ISOs.
2318 #
2319 # Return 0 on success, 1 on failure
2320 #
2321 do_disc_install()
2322 {
2323         typeset path="$1"
2324 
2325         typeset eject_final="N"
2326         typeset install_status
2327 
2328         #
2329         # Get a disc, it doesn't matter which one.
2330         #
2331         # We don't know which distro this may be yet, so we can't yet
2332         # ask for the first disc in the install order.
2333         #
2334         if ! get_cd "$path"; then
2335                 if [[ -z $silent_mode ]]; then
2336                         typeset distro_disc=\
2337                             $(gettext "a supported Linux distribution disc")
2338 
2339                         screenlog "\n$distro_mediafail" "$distro_disc ($path)"
2340                 fi
2341                 return 1
2342         fi
2343 
2344         if [[ -n $silent_mode && "$rd_disctype" = "CD" ]]; then
2345                 log "$silent_err_msg"
2346                 return 1
2347         fi
2348 
2349         if ! get_disc_distro "$mntdir"; then
2350                 msg=$(gettext "Unable to find a supported Linux release on")
2351                 screenlog "$msg"
2352                 screenlog "  $media_spec" "$path"
2353                 umount "$mntdir" > /dev/null 2>&1
2354                 return 1
2355         fi
2356 
2357         check_mbfree $zoneroot $distro_mb_required || return 1
2358         build_rpm_list $install_packages
2359 
2360         echo
2361 
2362         if [[ "$install_media" = "disc" ]]; then
2363                 #
2364                 # If we're in interactive mode, ask the user if they want the
2365                 # disc ejected when the installation is complete.
2366                 #
2367                 # Silent mode installs will require the user to manually run
2368                 # eject(1).
2369                 #
2370                 if [[ -n $removable && -z $silent_mode ]]; then
2371                         typeset ans
2372                         typeset disc
2373                         typeset status
2374                         typeset which=""
2375 
2376                         disc="$rd_disctype"
2377                         [[ "$disc" = "CD" ]] && which=$(gettext "final ")
2378 
2379                         #
2380                         # Ask the user if they want the install disc ejected
2381                         # when the installation is complete.  Any answer but
2382                         # "n" or "N" is taken to mean yes, eject it.
2383                         #
2384                         eject_final="Y"
2385                         status=$(gettext "WILL")
2386 
2387                         screenlog "$eject_final_msg" "$which" "$disc"
2388                         screenlog "  $eject_final_prompt" "$zonename" "[y]/n"
2389 
2390                         read ans && [[ "$ans" = [Nn]* ]] && eject_final="N" &&
2391                             status=$(gettext "will NOT")
2392 
2393                         screenlog "\n$eject_final_status\n" "$which" "$disc" \
2394                             "$status"
2395                 fi
2396 
2397                 screenlog "$install_ndiscs" "$distro_ncds"
2398 
2399                 msg=$(gettext "install %s.")
2400                 screenlog "$msg" "$release_name"
2401         else
2402                 screenlog "$install_nisos" "$distro_ncds"
2403 
2404                 msg=$(gettext "DVD) to install %s.")
2405                 screenlog "$msg" "$release_name"
2406         fi
2407 
2408         install_from_media "$path"
2409         install_status=$?
2410 
2411         [[ "$eject_final" = "Y" ]] && eject_removable_disc
2412 
2413         return $install_status
2414 }
2415 
2416 #
2417 # Install a zone using the list of ISO files passed as arguments to this
2418 # function.
2419 #
2420 # Return 0 on success, 1 on failure.
2421 #
2422 do_iso_install()
2423 {
2424         typeset install_status
2425         typeset iso_path
2426         typeset ldev
2427 
2428         msg=$(gettext "Checking for valid Linux distribution ISO images...")
2429         screenlog "\n$msg"
2430 
2431         scan_isos "$@"
2432 
2433         if [[ -z ${iso_filename[@]} ]]; then
2434                 msg=$(gettext "No valid ISO images available or mountable.")
2435                 screenlog "\n$msg"
2436                 return 1
2437         fi
2438         
2439         validate_iso_distros
2440 
2441         if [[ -z ${release[@]} ]]; then
2442                 msg=$(gettext "No supported Linux distributions found.")
2443                 screenlog "\n$msg"
2444                 return 1
2445         fi
2446 
2447         select_distro "${select_name[@]}" || return 1
2448         unset select_name
2449 
2450         . ${distro_file[$ndistro]} > /dev/null
2451         distro_ncds=${#distro_cdorder[@]}
2452 
2453         check_mbfree $zoneroot $distro_mb_required || return 1
2454         build_rpm_list $install_packages
2455 
2456         unset lofi_devs
2457 
2458         verboselog ""
2459         for iso_path in ${iso_set[$ndistro]}; do
2460                 if ! lofi_add "$iso_path"; then
2461                         for ldev in $lofi_devs; do
2462                                 lofi_del "$ldev"
2463                         done
2464                         return 1
2465                 fi
2466 
2467                 verboselog "Added \"$iso_path\""
2468                 verboselog "  as \"$lofi_dev\""
2469                 lofi_devs="$lofi_devs $lofi_dev"
2470         done
2471 
2472         release_name="${release[$ndistro]}"
2473 
2474         set -A iso_pathnames "zero_pad" ${iso_set[$ndistro]}
2475         install_from_media $lofi_devs
2476         install_status=$?
2477 
2478         for ldev in $lofi_devs; do
2479                 lofi_del "$ldev"
2480         done
2481 
2482         unset lofi_devs
2483         return $install_status
2484 }
2485 
2486 # Clean up on interrupt
2487 trap_cleanup()
2488 {
2489         cd "$cwd"
2490 
2491         msg=$(gettext "Interrupt received, cleaning up partial install...")
2492         screenlog "$msg"
2493 
2494         [[ -n $miniroot_booted ]] && zoneadm -z "$zonename" halt &&
2495             unset miniroot_booted && unset newroot_mounted
2496 
2497         #
2498         # OK, why a sync here?  Because certain commands may have written data
2499         # to mounted file systems before the interrupt, and given just the right
2500         # timing there may be buffered data not yet sent to the disk or the
2501         # system may still be writing data to the disk.  Either way, the umount
2502         # will then fail because the system will still see the mounted
2503         # filesystems as busy.
2504         #
2505         sync
2506 
2507         if [[ -n $newroot_mounted ]]; then
2508                 umount_list $newroot_mounted
2509                 unset newroot_mounted
2510         fi
2511 
2512         if [[ -n $zone_mounted ]]; then
2513                 umount "$zone_mounted"
2514                 unset zone_mounted
2515         fi
2516 
2517         #
2518         # Normally, this isn't needed but there is a window where mntdir is set
2519         # before zone_mounted, so account for that case.
2520         #
2521         if [[ -n $mntdir ]]; then
2522                 umount "$mntdir"
2523                 unset mntdir
2524         fi
2525 
2526         [[ -n $lofi_dev ]] && lofi_del "$lofi_dev"
2527 
2528         if [[ -n $lofi_devs ]]; then
2529                 typeset ldev
2530 
2531                 for ldev in $lofi_devs
2532                 do
2533                         lofi_del "$ldev"
2534                 done
2535 
2536                 unset lofi_devs
2537         fi
2538 
2539         [[ -n $lofi_created ]] && rmdir -ps "$lofi_created" &&
2540             unset lofi_created
2541 
2542         msg=$(gettext "Installation aborted.")
2543         screenlog "$msg"
2544         exit $ZONE_SUBPROC_FATAL
2545 }
2546 
2547 #
2548 # Start of main script
2549 #
2550 cwd=$(dirname "$0")
2551 distro_dir="$cwd/distros"
2552 
2553 unset deferred_saved
2554 unset distro_path
2555 unset logfile
2556 unset msg
2557 unset newroot_mounted
2558 unset silent_err_msg
2559 unset silent_mode
2560 unset verbose_mode
2561 unset zone_mounted
2562 unset zoneroot
2563 unset zonename
2564 
2565 #
2566 # Exit values used by the script, as #defined in <sys/zone.h>
2567 #
2568 #       ZONE_SUBPROC_OK
2569 #       ===============
2570 #       Installation was successful
2571 #
2572 #       ZONE_SUBPROC_USAGE
2573 #       ==================
2574 #       Improper arguments were passed, so print a usage message before exiting
2575 #
2576 #       ZONE_SUBPROC_NOTCOMPLETE
2577 #       ========================
2578 #       Installation did not complete, but another installation attempt can be
2579 #       made without an uninstall
2580 #
2581 #       ZONE_SUBPROC_FATAL
2582 #       ==================
2583 #       Installation failed and an uninstall will be required before another
2584 #       install can be attempted
2585 #
2586 ZONE_SUBPROC_OK=0
2587 ZONE_SUBPROC_USAGE=253
2588 ZONE_SUBPROC_NOTCOMPLETE=254
2589 ZONE_SUBPROC_FATAL=255
2590 
2591 #
2592 # Process and set up various global option variables:
2593 #
2594 #    distro_path - Path containing files that make up the distribution
2595 #                  (e.g. a directory containing ISO files or a disc device)
2596 #    logfile     - Name (if any) of the install log file
2597 #    zoneroot    - Root directory for the zone to install
2598 #    zonename    - Name of the zone to install
2599 #
2600 while getopts 'svxd:l:r:z:' opt; do
2601         case $opt in
2602                 s) silent_mode=1; unset verbose_mode;;
2603                 v) verbose_mode=1; unset silent_mode;;
2604                 x) set -x;;
2605                 d) distro_path="$OPTARG";;
2606                 l) logfile="$OPTARG";;
2607                 r) zoneroot="$OPTARG";;
2608                 z) zonename="$OPTARG";;
2609         esac
2610 done
2611 shift OPTIND-1
2612 
2613 distro_path=${distro_path:=/cdrom/cdrom0}
2614 
2615 install_packages="$@"
2616 
2617 [[ -n $silent_mode ]] && exec 1>/dev/null
2618 
2619 if [[ -z $zonename ]]; then
2620         msg=$(gettext "ERROR:  Cannot install - no zone name was specified")
2621         screenlog "$msg"
2622         echo
2623         exit $ZONE_SUBPROC_NOTCOMPLETE
2624 fi
2625 
2626 if [[ -z $zoneroot ]]; then
2627         msg=$(gettext "ERROR:  Cannot install - no zone root directory was")
2628         screenlog "$msg"
2629 
2630         msg=$(gettext "specified.")
2631         screenlog "  $msg"
2632         echo
2633         exit $ZONE_SUBPROC_NOTCOMPLETE
2634 fi
2635 
2636 # Make sure the specified zone root directory exists
2637 [[ -d "$zoneroot" ]] || mkdir -m 0700 -p "$zoneroot"
2638 
2639 if [[ ! -d "$zoneroot" ]]; then
2640         screenlog "$zone_rootfail" "$zoneroot"
2641         echo
2642         exit $ZONE_SUBPROC_NOTCOMPLETE
2643 fi
2644 
2645 rootdir="$zoneroot/root"
2646 
2647 # Make sure the specified zone root subdirectory exists
2648 [[ -d "$rootdir" ]] || mkdir -p "$rootdir"
2649 
2650 if [[ ! -d "$rootdir" ]]; then
2651         screenlog "$zone_rootsub" "$rootdir"
2652         echo
2653         exit $ZONE_SUBPROC_NOTCOMPLETE
2654 fi
2655 
2656 media_mntdir="$rootdir/media"
2657 
2658 if [[ -n $logfile ]]; then
2659         # If a log file was specified, log information regarding the install
2660         log "\nInstallation started `date`" 
2661         log "Installing from path \"$distro_path\""
2662 else
2663         # Redirect stderr to /dev/null if silent mode is specified.
2664         [[ -n $silent_mode ]] && exec 2>/dev/null
2665 fi
2666 
2667 distro_path=${distro_path:=$default_distro_path}
2668 
2669 # From this point on, call trap_cleanup() on interrupt (^C)
2670 trap trap_cleanup INT
2671 
2672 verbose "Installing zone \"$zonename\" at root \"$zoneroot\""
2673 release_name="supported Linux distribution"
2674 
2675 #
2676 # Based on the pathname, attempt to determine whether this will be a disc or
2677 # lofi-based install or one using ISOs.
2678 #
2679 if [[ "$distro_path" = /cdrom/* || "$distro_path" = /media/* ||
2680     "$distro_path" = /dev/dsk/* || "$distro_path" = /dev/lofi/* ]]; then
2681         if [[ "$distro_path" = /dev/lofi/* ]]; then
2682                 silent_err_msg="$silent_nolofi"
2683                 install_media="lofi"
2684         else
2685                 silent_err_msg="$silent_nodisc"
2686                 install_media="disc"
2687         fi
2688 
2689         if [[ "$distro_path" = /cdrom/* || "$distro_path" = /media/* ]]; then
2690                 managed_removable=1
2691         else
2692                 managed_removable=0
2693         fi
2694 
2695         log "Installing zone \"$zonename\" at root \"$zoneroot\""
2696         verboselog "  Attempting ${install_media}-based install via:"
2697         verboselog "    \"$distro_path\""
2698 
2699         do_disc_install "$distro_path"
2700 else
2701         typeset dir_start
2702         typeset dir_file
2703 
2704         dir_start=$(dirname "$distro_path" | cut -c 1)
2705 
2706         [[ "$dir_start" != "/" ]] && distro_path="${PWD:=$(pwd)}/$distro_path"
2707 
2708         if [[ ! -d "$distro_path" ]]; then
2709                 screenlog "$no_distropath" "$distro_path"
2710                 echo
2711                 exit $ZONE_SUBPROC_NOTCOMPLETE
2712         fi
2713 
2714         log "Installing zone \"$zonename\" at root \"$zoneroot\""
2715         verboselog "  Attempting ISO-based install from directory:"
2716         verboselog "    \"$distro_path\""
2717 
2718         unset iso_files
2719 
2720         for dir_file in $distro_path/*; do
2721                 #
2722                 # Skip this file if it's not a regular file or isn't readable
2723                 #
2724                 [[ ! -f $dir_file || ! -r $dir_file ]] && continue
2725 
2726                 #
2727                 # If it's an hsfs file, it's an ISO, so add it to the possible
2728                 # distro ISO list
2729                 #
2730                 filetype=$(LC_ALL=C fstyp $dir_file 2>/dev/null) &&
2731                     [[ "$filetype" = "hsfs" ]] &&
2732                     iso_files="$iso_files $dir_file"
2733         done
2734 
2735         install_media="ISO"
2736         do_iso_install $iso_files
2737 fi
2738 
2739 if [[ $? -ne 0 ]]; then
2740         cd "$cwd"
2741 
2742         [[ -n $miniroot_booted ]] && zoneadm -z "$zonename" halt &&
2743             unset miniroot_booted && unset newroot_mounted
2744 
2745         if [[ -n $zone_mounted ]]; then
2746                 umount "$zone_mounted"
2747                 unset zone_mounted
2748         fi
2749 
2750         if [[ -n $newroot_mounted ]]; then
2751                 umount_list $newroot_mounted
2752                 unset newroot_mounted
2753         fi
2754 
2755         screenlog "\n$install_failed\n" "$release_name" "$zonename" "`date`"
2756 
2757         msg=$(gettext "Cleaning up after failed install...")
2758         screenlog "$msg"
2759 
2760         #
2761         # The extra checks are some basic paranoia due to the potentially
2762         # dangerous nature of these commands but are not intended to catch all
2763         # malicious cases.
2764         #
2765         [[ -d "$zoneroot/a" ]] && rm -rf "$zoneroot/a"
2766 
2767         exit $ZONE_SUBPROC_FATAL
2768 fi
2769 
2770 screenlog "$install_done" "$release_name" "$zonename" "`date`"
2771 
2772 exit $ZONE_SUBPROC_OK