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 # 23 # Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 # Use is subject to license terms. 25 # 26 27 # Restrict executables to /bin, /usr/bin, /usr/sbin and /usr/sfw/bin 28 PATH=/bin:/usr/bin:/usr/sbin:/usr/sfw/bin 29 30 export PATH 31 32 # Setup i18n output 33 TEXTDOMAIN="SUNW_OST_OSCMD" 34 export TEXTDOMAIN 35 36 # Log passed arguments to file descriptor 2 37 log() 38 { 39 [[ -n $logfile ]] && echo "$@" >&2 40 } 41 42 # 43 # Send the provided printf()-style arguments to the screen and to the 44 # logfile. 45 # 46 screenlog() 47 { 48 typeset fmt="$1" 49 shift 50 51 printf "$fmt\n" "$@" 52 [[ -n $logfile ]] && printf "$fmt\n" "$@" >&2 53 } 54 55 # Print and log provided text if the shell variable "verbose_mode" is set 56 verbose() 57 { 58 [[ -n $verbose_mode ]] && echo "$@" 59 [[ -n $logfile ]] && [[ -n $verbose_mode ]] && echo "$@" >&2 60 } 61 62 unsupported_cpu=\ 63 $(gettext "ERROR: Cannot install branded zone: processor must be %s-compatible") 64 65 cmd_not_found=$(gettext "Required command '%s' cannot be found!") 66 cmd_not_exec=$(gettext "Required command '%s' not executable!") 67 zone_initfail=$(gettext "Attempt to initialize zone '%s' FAILED.") 68 path_abs=$(gettext "Pathname specified to -d '%s' must be absolute.") 69 70 cmd_h=$(gettext "%s -z <zone name> %s -h") 71 cmd_full=\ 72 $(gettext "%s -z <zone name> %s [-v | -s] [-d <dir>|<device>] [<cluster> ... ]") 73 74 both_modes=$(gettext "%s: error: cannot select both silent and verbose modes") 75 76 not_found=$(gettext "%s: error: file or directory not found.") 77 78 wrong_type=\ 79 $(gettext "%s: error: must be a gzip, bzip2, .Z or uncompressed tar archive.") 80 81 not_readable=$(gettext "Cannot read file '%s'") 82 83 no_install=$(gettext "Could not create install directory '%s'") 84 no_log=$(gettext "Could not create log directory '%s'") 85 no_logfile=$(gettext "Could not create log file '%s'") 86 87 root_full=$(gettext "Zonepath root %s exists and contains data; remove or move aside prior to install.") 88 89 install_zone=$(gettext "Installing zone '%s' at root directory '%s'") 90 install_from=$(gettext "from archive '%s'") 91 92 install_fail=$(gettext "Installation of zone '%s' FAILED.") 93 see_log=$(gettext "See the log file:\n '%s'\nfor details.") 94 95 install_abort=$(gettext "Installation of zone '%s' aborted.") 96 install_good=$(gettext "Installation of zone '%s' completed successfully.") 97 98 # Check if commands passed in exist and are executable. 99 check_cmd() 100 { 101 for cmd in "$@"; do 102 if [[ ! -f $cmd ]]; then 103 screenlog "$cmd_not_found" "$cmd" 104 screenlog "$install_abort" "$zonename" 105 exit $ZONE_SUBPROC_NOTCOMPLETE 106 fi 107 108 if [[ ! -x $cmd ]]; then 109 screenlog "$cmd_not_exec" "$cmd" 110 screenlog "$install_abort" "$zonename" 111 exit $ZONE_SUBPROC_NOTCOMPLETE 112 fi 113 done 114 } 115 116 # Post process as tarball-installed zone for use by BrandZ. 117 init_tarzone() 118 { 119 typeset rootdir="$1" 120 121 if ! $branddir/lx_init_zone "$rootdir"; then 122 screenlog "$zone_initfail" "$zonename" 123 return 1 124 fi 125 } 126 127 # Clean up on interrupt 128 trap_cleanup() 129 { 130 msg=$(gettext "Installation cancelled due to interrupt.") 131 132 screenlog "$msg" 133 exit $int_code 134 } 135 136 # 137 # Output the usage message. 138 # 139 # This is done this way due to limitations in the way gettext strings are 140 # extracted from shell scripts and processed. Use of this somewhat awkward 141 # syntax allows us to produce longer lines of text than otherwise would be 142 # possible without wrapping lines across more than one line of code. 143 # 144 usage() 145 { 146 int_code=$ZONE_SUBPROC_USAGE 147 148 echo $(gettext "Usage:") 149 printf " $cmd_h\n" "zoneadm" "install" 150 printf " $cmd_full\n" "zoneadm" "install" 151 152 echo 153 154 echo $(gettext "The installer will attempt to use the default system") \ 155 $(gettext "removable disc device if <archive dir> is not") \ 156 $(gettext "specified.") | fmt -80 157 158 echo 159 160 echo $(gettext "<cluster> specifies which package cluster you wish") \ 161 $(gettext "to install.") | fmt -80 162 163 echo 164 echo $(gettext "The 'desktop' cluster will be installed by default.") 165 echo 166 echo $(gettext "The available clusters are:") 167 echo " + core" 168 echo " + server" 169 echo " + desktop" 170 echo " + development" 171 echo " + all" 172 echo 173 174 echo $(gettext "Each cluster includes all of the clusters preceding") \ 175 $(gettext "it, so the 'server' cluster includes the 'core'") \ 176 $(gettext "cluster, the 'desktop' cluster includes the 'core'") \ 177 $(gettext "and 'server' clusters, and so on.") | fmt -80 178 179 echo 180 echo $(gettext "Examples") 181 echo "========" 182 183 echo $(gettext "Example 1: Install a base Linux system from CDs or a") \ 184 $(gettext "DVD using the system default removable disc device:") | 185 fmt -80 186 187 echo 188 echo " # zoneadm -z myzone install" 189 echo 190 191 echo $(gettext "Example 2: Install the 'server' cluster from CDs or") \ 192 $(gettext "a DVD via an alternative removable disc device:") | 193 fmt -80 194 195 echo 196 echo " # zoneadm -z myzone install -d /cdrom/cdrom1 server" 197 echo 198 199 echo $(gettext "Example 3: Install the desktop Linux environment") \ 200 $(gettext "from an ISO image made available as '/dev/lofi/1' by") \ 201 $(gettext "use of lofiadm(1M):") | fmt -80 202 203 echo 204 echo " # zoneadm -z myzone install -d /dev/lofi/1 desktop" 205 echo 206 207 echo $(gettext "Example 4: Install the entire Linux environment from") \ 208 $(gettext "ISO images located in the directory") \ 209 "'/export/centos_3.8/isos':" | fmt -80 210 211 echo 212 echo " # zoneadm -z myzone install -d /export/centos_3.8/isos all" 213 echo 214 215 echo $(gettext "Example 5: Install from a compressed tar archive of") \ 216 $(gettext "an existing Linux installation (a tar ball) with") \ 217 $(gettext "verbose output regarding the progress of the") \ 218 $(gettext "installation:") | fmt -80 219 220 echo 221 echo " # zoneadm -z myzone install -v -d /tmp/linux_full.tar.gz" 222 echo 223 224 echo $(gettext "Example 6: Install from a compressed tar archive of") \ 225 $(gettext "an existing Linux installation (a tar ball) with NO") \ 226 $(gettext "output regarding the progress of the installation") \ 227 $(gettext "(silent mode.)") | fmt -80 228 229 echo 230 231 echo $(gettext "NOTE: Silent mode is only recommended for use by") \ 232 $(gettext "shell scripts and other non-interactive programs:") | 233 fmt -80 234 235 echo 236 echo " # zoneadm -z myzone install -d /tmp/linux_full.tar.gz -s" 237 echo 238 239 exit $int_code 240 } 241 242 # 243 # The main body of the script starts here. 244 # 245 # This script should never be called directly by a user but rather should 246 # only be called by zoneadm to install a BrandZ Linux zone. 247 # 248 249 # 250 # Exit values used by the script, as #defined in <sys/zone.h> 251 # 252 # ZONE_SUBPROC_OK 253 # =============== 254 # Installation was successful 255 # 256 # ZONE_SUBPROC_USAGE 257 # ================== 258 # Improper arguments were passed, so print a usage message before exiting 259 # 260 # ZONE_SUBPROC_NOTCOMPLETE 261 # ======================== 262 # Installation did not complete, but another installation attempt can be 263 # made without an uninstall 264 # 265 # ZONE_SUBPROC_FATAL 266 # ================== 267 # Installation failed and an uninstall will be required before another 268 # install can be attempted 269 # 270 ZONE_SUBPROC_OK=0 271 ZONE_SUBPROC_USAGE=253 272 ZONE_SUBPROC_NOTCOMPLETE=254 273 ZONE_SUBPROC_FATAL=255 274 275 # 276 # An unspecified exit or interrupt should exit with ZONE_SUBPROC_NOTCOMPLETE, 277 # meaning a user will not need to do an uninstall before attempting another 278 # install. 279 # 280 int_code=$ZONE_SUBPROC_NOTCOMPLETE 281 282 trap trap_cleanup INT 283 284 # If we weren't passed at least two arguments, exit now. 285 [[ $# -lt 2 ]] && usage 286 287 # 288 # This script is always started with a full path so we can extract the 289 # brand directory name here. 290 # 291 branddir=$(dirname "$0") 292 zonename="$1" 293 zoneroot="$2" 294 295 install_root="$zoneroot/root" 296 logdir="$install_root/var/log" 297 298 shift; shift # remove zonename and zoneroot from arguments array 299 300 unset gtaropts 301 unset install_opts 302 unset install_src 303 unset msg 304 unset silent_mode 305 unset verbose_mode 306 307 while getopts "d:hsvX" opt 308 do 309 case "$opt" in 310 h) usage;; 311 s) silent_mode=1;; 312 v) verbose_mode=1;; 313 d) install_src="$OPTARG" ;; 314 X) install_opts="$install_opts -x" ;; 315 *) usage;; 316 esac 317 done 318 shift OPTIND-1 319 320 # Providing more than one passed argument generates a usage message 321 if [[ $# -gt 1 ]]; then 322 msg=$(gettext "ERROR: Too many arguments provided:") 323 324 screenlog "$msg" 325 screenlog " \"%s\"" "$@" 326 screenlog "" 327 usage 328 fi 329 330 # Validate any free-form arguments 331 if [[ $# -eq 1 && "$1" != "core" && "$1" != "server" && "$1" != "desktop" && 332 "$1" != "development" && "$1" != "all" ]]; then 333 msg=$(gettext "ERROR: Unknown cluster name specified: %s") 334 335 screenlog "$msg" "\"$1\"" 336 screenlog "" 337 usage 338 fi 339 340 # The install can't be both verbose AND silent... 341 if [[ -n $silent_mode && -n $verbose_mode ]]; then 342 screenlog "$both_modes" "zoneadm install" 343 screenlog "" 344 usage 345 fi 346 347 # 348 # Validate that we're running on a i686-compatible CPU; abort the zone 349 # installation now if we're not. 350 # 351 procinfo=$(LC_ALL=C psrinfo -vp | grep family) 352 353 # 354 # All x86 processors in CPUID families 6, 15, 16 or 17 should be 355 # i686-compatible, assuming third party processor vendors follow AMD and 356 # Intel's lead. 357 # 358 if [[ "$procinfo" != *" x86 "* ]] || 359 [[ "$procinfo" != *" family 6 "* && "$procinfo" != *" family 15 "* && 360 "$procinfo" != *" family 16 "* && "$procinfo" != *" family 17 "* ]] ; then 361 screenlog "$unsupported_cpu" "i686" 362 exit $int_code 363 fi 364 365 if [[ -n $install_src ]]; then 366 # 367 # Validate $install_src. 368 # 369 # If install_src is a directory, assume it contains ISO images to 370 # install from, otherwise treat the argument as if it points to a tar 371 # ball file. 372 # 373 if [[ "`echo $install_src | cut -c 1`" != "/" ]]; then 374 screenlog "$path_abs" "$install_src" 375 exit $int_code 376 fi 377 378 if [[ ! -a "$install_src" ]]; then 379 screenlog "$not_found" "$install_src" 380 screenlog "$install_abort" "$zonename" 381 exit $int_code 382 fi 383 384 if [[ ! -r "$install_src" ]]; then 385 screenlog "$not_readable" "$install_src" 386 screenlog "$install_abort" "$zonename" 387 exit $int_code 388 fi 389 390 # 391 # If install_src is a block device, a directory, a possible device 392 # created via lofiadm(1M), or the directory used by a standard volume 393 # management daemon, pass it on to the secondary install script. 394 # 395 # Otherwise, validate the passed filename to prepare for a tar ball 396 # install. 397 # 398 if [[ ! -b "$install_src" && ! -d "$install_src" && 399 "$install_src" != /dev/lofi/* && "$install_src" != /cdrom/* && 400 "$install_src" != /media/* ]]; then 401 if [[ ! -f "$install_src" ]]; then 402 screenlog "$wrong_type" "$install_src" 403 screenlog "$install_abort" "$zonename" 404 exit $int_code 405 fi 406 407 filetype=`{ LC_ALL=C file $install_src | 408 awk '{print $2}' ; } 2>/dev/null` 409 410 if [[ "$filetype" = "gzip" ]]; then 411 verbose "\"$install_src\": \"gzip\" archive" 412 gtaropts="-xz" 413 elif [[ "$filetype" = "bzip2" ]]; then 414 verbose "\"$install_src\": \"bzip2\" archive" 415 gtaropts="-xj" 416 elif [[ "$filetype" = "compressed" ]]; then 417 verbose "\"$install_src\": Lempel-Ziv" \ 418 "compressed (\".Z\") archive." 419 gtaropts="-xZ" 420 elif [[ "$filetype" = "USTAR" ]]; then 421 verbose "\"$install_src\":" \ 422 "uncompressed (\"tar\") archive." 423 gtaropts="-x" 424 else 425 screenlog "$wrong_type" "$install_src" 426 screenlog "$install_abort" "$zonename" 427 exit $int_code 428 fi 429 fi 430 fi 431 432 # 433 # Start silent operation and pass the flag to prepare pass the flag to 434 # the ISO installer, if needed. 435 # 436 if [[ -n $silent_mode ]] 437 then 438 exec 1>/dev/null 439 install_opts="$install_opts -s" 440 fi 441 442 # 443 # If verbose mode was specified, pass the verbose flag to lx_distro_install 444 # for ISO or disc installations and to gtar for tarball-based installs. 445 # 446 if [[ -n $verbose_mode ]] 447 then 448 echo $(gettext "Verbose output mode enabled.") 449 install_opts="$install_opts -v" 450 [[ -n $gtaropts ]] && gtaropts="${gtaropts}v" 451 fi 452 453 [[ -n $gtaropts ]] && gtaropts="${gtaropts}f" 454 455 if [[ ! -d "$install_root" ]] 456 then 457 if ! mkdir -p "$install_root" 2>/dev/null; then 458 screenlog "$no_install" "$install_root" 459 exit $int_code 460 fi 461 fi 462 463 # 464 # Check for a non-empty root. 465 # 466 cnt=`ls $install_root | wc -l` 467 if [ $cnt -ne 0 ]; then 468 screenlog "$root_full" "$install_root" 469 exit $int_code 470 fi 471 472 if [[ ! -d "$logdir" ]] 473 then 474 if ! mkdir -p "$logdir" 2>/dev/null; then 475 screenlog "$no_log" "$logdir" 476 exit $int_code 477 fi 478 fi 479 480 logfile="${logdir}/$zonename.install.$$.log" 481 482 if ! > $logfile; then 483 screenlog "$no_logfile" "$logfile" 484 exit $int_code 485 fi 486 487 # Redirect stderr to the log file to automatically log any error messages 488 exec 2>>"$logfile" 489 490 # 491 # From here on out, an unspecified exit or interrupt should exit with 492 # ZONE_SUBPROC_FATAL, meaning a user will need to do an uninstall before 493 # attempting another install, as we've modified the directories we were going 494 # to install to in some way. 495 # 496 int_code=$ZONE_SUBPROC_FATAL 497 498 log "Installation started for zone \"$zonename\" `/usr/bin/date`" 499 500 if [[ -n $gtaropts ]]; then 501 check_cmd /usr/sfw/bin/gtar $branddir/lx_init_zone 502 503 screenlog "$install_zone" "$zonename" "$zoneroot" 504 screenlog "$install_from" "$install_src" 505 echo 506 echo $(gettext "This process may take several minutes.") 507 echo 508 509 if ! ( cd "$install_root" && gtar "$gtaropts" "$install_src" ) ; then 510 log "Error: extraction from tar archive failed." 511 else 512 if ! [[ -d "${install_root}/bin" && 513 -d "${install_root}/sbin" ]]; then 514 log "Error: improper or incomplete tar archive." 515 else 516 $branddir/lx_init_zone "$install_root" && 517 init_tarzone "$install_root" 518 519 # 520 # Emit the same code from here whether we're 521 # interrupted or exiting normally. 522 # 523 int_code=$? 524 fi 525 fi 526 527 if [[ $int_code -eq ZONE_SUBPROC_OK ]]; then 528 log "Tar install completed for zone '$zonename' `date`." 529 else 530 log "Tar install failed for zone \"$zonename\" `date`." 531 532 fi 533 else 534 check_cmd $branddir/lx_distro_install 535 536 $branddir/lx_distro_install -z "$zonename" -r "$zoneroot" \ 537 -d "$install_src" -l "$logfile" $install_opts "$@" 538 539 # 540 # Emit the same code from here whether we're interrupted or exiting 541 # normally. 542 # 543 int_code=$? 544 545 [[ $int_code -eq $ZONE_SUBPROC_USAGE ]] && usage 546 fi 547 548 if [[ $int_code -ne $ZONE_SUBPROC_OK ]]; then 549 screenlog "" 550 screenlog "$install_fail" "$zonename" 551 screenlog "" 552 553 # 554 # Only make a reference to the log file if one will exist after 555 # zoneadm exits. 556 # 557 [[ $int_code -ne $ZONE_SUBPROC_NOTCOMPLETE ]] && 558 screenlog "$see_log" "$logfile" 559 560 exit $int_code 561 fi 562 563 # 564 # After the install completes, we've likely moved a new copy of the logfile into 565 # place atop the logfile we WERE writing to, so if we don't reopen the logfile 566 # here the shell will continue writing to the old logfile's inode, meaning we 567 # would lose all log information from this point on. 568 # 569 exec 2>>"$logfile" 570 571 screenlog "" 572 screenlog "$install_good" "$zonename" 573 screenlog "" 574 575 echo $(gettext "Details saved to log file:") 576 echo " \"$logfile\"" 577 echo 578 579 exit $ZONE_SUBPROC_OK