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