1 #!/bin/ksh93 -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 # 28 # itu - converts packages to Driver Update format and patches Solaris install 29 # media for Install Time Update (ITU). 30 # 31 32 readonly PROG=$0 33 readonly ORIGPWD=$PWD 34 35 # Must-have utilities 36 readonly CPIO=/usr/bin/cpio 37 readonly GZIP=/usr/bin/gzip 38 readonly MKISOFS=/usr/bin/mkisofs 39 readonly PATCHADD=/usr/sbin/patchadd 40 readonly PKGTRANS=/usr/bin/pkgtrans 41 readonly PKGADD=/usr/sbin/pkgadd 42 readonly LOFIADM=/usr/sbin/lofiadm 43 readonly MKDIR=/usr/bin/mkdir 44 readonly RM=/usr/bin/rm 45 readonly CP=/usr/bin/cp 46 readonly HEAD=/usr/bin/head 47 readonly SORT=/usr/bin/sort 48 readonly MKBOOTMEDIA=/usr/bin/mkbootmedia 49 readonly PKG2DU=/usr/bin/pkg2du 50 readonly TOUCH=/usr/bin/touch 51 readonly NAWK=/usr/bin/nawk 52 readonly CHMOD=/usr/bin/chmod 53 readonly GREP=/usr/bin/grep 54 readonly LS=/usr/bin/ls 55 readonly LN=/usr/bin/ln 56 readonly SED=/usr/bin/sed 57 readonly CAT=/usr/bin/cat 58 readonly FIND=/usr/bin/find 59 readonly UNAME=/usr/bin/uname 60 readonly MACH=`$UNAME -p` 61 62 ROOT_ARCHIVE=/usr/sbin/root_archive 63 BOOTBLOCK= 64 MINIROOT= 65 # Relative to a Solaris media root. 66 if [ "$MACH" = "sparc" ]; then 67 BOOTBLOCK=boot/hsfs.bootblock 68 MINIROOT=$MEDIA_ROOT/boot/sparc.miniroot 69 else 70 # x86/x64 71 BOOTBLOCK=boot/grub/stage2_eltorito 72 MINIROOT=$MEDIA_ROOT/boot/x86.miniroot 73 fi 74 75 readonly TMP_DIR=${TMPDIR:-/tmp}/${PROG##*/}.$$ 76 readonly LOGFILE=${TMPDIR:-/tmp}/${PROG##*/}-log.$$ 77 78 # Paths we need. 79 export PATH=/usr/bin:/usr/sbin:/sbin:/boot/solaris/bin:$PATH 80 81 # for gettext 82 TEXTDOMAIN=SUNW_OST_OSCMD 83 export TEXTDOMAIN 84 85 86 function cleanup 87 { 88 $RM -rf "$TMP_DIR" 89 } 90 91 92 function usage_long 93 { 94 usage_short 95 print -u2 96 usage_options 97 } 98 99 100 function usage_short 101 { 102 gettext "Usage:\n" 103 gettext "${PROG##*/} makedu -r solaris_release [-v] [-f] [-d output_dir]\n [-o iso_file] [-l iso_label] package [package ...]\n" 104 gettext "${PROG##*/} patchmedia -R media_root [-v] [-f]\n [-o iso_file] [-l iso_label] pkg_or_patch [pkg_or_patch ...]\n" 105 gettext "${PROG##*/} makeiso -o iso_file [-v] [-f] [-l iso_label] media_root\n" 106 } 107 108 109 function usage_options { 110 gettext "Options:\n" 111 gettext " -d output_dir\n Directory where the Driver Update directory should be created.\n" 112 gettext " -f\n If output_dir/DU or iso_file already exists, remove it without\n asking first.\n" 113 gettext " -l iso_label\n Label/volume name of the ISO image (if -o option is specified).\n" 114 gettext " -o iso_file\n Path of ISO image file to create. For 115 subcommands patchmedia and\n makeiso this will be a bootable ISO image.\n This option must be specified for subcommand makeiso.\n" 116 gettext " -R media_root\n Top-level directory of on-disk image of Solaris installation media.\n This option must be specified for subcommand patchmedia.\n" 117 gettext " -r solaris_release\n Solaris release number for which the Driver Update is intended.\n It takes the form of 5.10.\n This option must be specified for subcommand makedu.\n" 118 gettext " -v\n Verbose. Multiple -v options increase verbosity.\n" 119 120 echo; 121 } 122 123 124 # 125 # Process command line options. 126 # Note: since $OPTIND is a local variable inside functions, upon return 127 # from this function global variable $MYOPTIND is set to this value. 128 # 129 function process_options # <arg> ... 130 { 131 typeset opt optlist 132 133 case "$SUBCOMMAND" in 134 makedu) optlist='d:fl:o:r:v' ;; 135 patchmedia) optlist='fl:o:R:v' ;; 136 makeiso) optlist='fl:o:v' ;; 137 esac 138 139 while getopts ":$optlist" opt 140 do 141 case $opt in 142 d) DU_OUTDIR=$OPTARG 143 ;; 144 f) FORCE=1 145 ;; 146 l) ISOLABEL=$OPTARG 147 ;; 148 o) ISO=$OPTARG 149 if [ ! -z `echo $ISO | $GREP "^/tmp"` ]; then 150 gettext "ISO images will not be created on /tmp.\n" 151 gettext "Please choose a different output location.\n" 152 exit 3 153 fi 154 ;; 155 R) MEDIA_ROOT=$OPTARG 156 ;; 157 r) RELEASE=$OPTARG 158 ;; 159 v) (( VERBOSE_LEVEL += 1 )) 160 VERBOSE_OPTS="${VERBOSE_OPTS:--}$opt" # collect options 161 ;; 162 :) gettext "Option -$OPTARG missing argument.\n" 163 usage_short 164 return 1 165 ;; 166 *) gettext "Option -$OPTARG invalid for $SUBCOMMAND.\n" 167 usage_short 168 return 1 169 ;; 170 esac 171 done 172 173 MYOPTIND=$OPTIND 174 return 0 175 } 176 177 178 # 179 # Check some prerequisites 180 # 181 function check_prereqs 182 { 183 typeset utils f 184 185 # List of must-have utilities depends on subcommand. 186 case "$SUBCOMMAND" in 187 makedu) 188 set -A utils $GZIP ${ISO:+$MKISOFS} $PKGTRANS 189 ;; 190 patchmedia) 191 set -A utils $CPIO $GZIP ${ISO:+$MKISOFS} $PATCHADD \ 192 $ROOT_ARCHIVE 193 ;; 194 makeiso) 195 set -A utils $MKISOFS 196 ;; 197 esac 198 199 for f in "${utils[@]}" 200 do 201 if [[ ! -x "$f" ]] 202 then 203 gettext "Can't find required utility $f.\n" 204 return 1 205 fi 206 done 207 208 # Subcommand packmedia uses the "root_archive unpack_media" command 209 # which calls lofiadm -a, which requires write access as 210 # determined by /dev/lofictl. See lofiadm(1m). 211 if [[ $SUBCOMMAND = patchmedia && ! -w /dev/lofictl ]] 212 then 213 gettext "You don't have enough privileges to run lofiadm -a.\n" 214 gettext "See lofiadm(1m) for more information.\n" 215 return 1 216 fi 217 218 return 0 219 } 220 221 222 # 223 # Verifies the given packages and collects them in the PACKAGES array. 224 # 225 function collect_packages # <arg> ... 226 { 227 typeset obj 228 229 for obj in "$@" 230 do 231 if [[ ! -e "$obj" ]] 232 then 233 gettext "Can't find package $obj.\n" 234 return 1 235 elif [[ ! -f "$obj/pkginfo" ]] 236 then 237 gettext "$obj is not a package.\n" 238 return 1 239 fi 240 PACKAGES[ ${#PACKAGES[*]} ]=$obj 241 done 242 return 0 243 } 244 245 246 # 247 # Verifies the given packages and patches. Packages are then collected in 248 # the array PACKAGES. Patches are stored in the PATCHES array. 249 # 250 function collect_packages_patches # <arg> ... 251 { 252 typeset obj 253 254 for obj in "$@" 255 do 256 if [[ -f "$obj/patchinfo" ]] 257 then 258 # Collect patches. 259 PATCHES[ ${#PATCHES[*]} ]=$obj 260 elif [[ -f "$obj/pkginfo" ]] 261 then 262 # Collect packages. 263 PACKAGES[ ${#PACKAGES[*]} ]=$obj 264 elif [[ -e "$obj" ]] 265 then 266 gettext "$obj is not a package or patch.\n" 267 return 1 268 else 269 gettext "$obj does not exist.\n" 270 return 1 271 fi 272 done 273 return 0 274 } 275 276 277 # 278 # Ask user whether to overwrite an object, unless -f option was given. 279 # 280 function is_overwrite 281 { 282 typeset arg=$1 283 typeset -l ans 284 285 (( FORCE )) && return 0 286 while true 287 do 288 gettext "$arg already exists. Overwrite it? (y/n) " 289 read ans 290 case $ans in 291 y*|Y*) return 0 ;; # go ahead, overwrite 292 n*|N*) return 1 ;; # don't overwrite 293 esac 294 done 295 } 296 297 298 # 299 # Check the format of the Solaris release number $RELEASE. 300 # Also set $VERSION (for DU format) based on $RELEASE. 301 # 302 function check_release 303 { 304 # Allow Major.Minor or Major.Minor.Micro format. 305 if [[ $RELEASE != +([0-9]).+([0-9])?(.+([0-9])) ]] 306 then 307 gettext "Invalid release number specified: $RELEASE.\n" 308 return 1 309 fi 310 311 # As defined by the ITU spec, a Solaris release number 5.x corresponds 312 # to version number 2x (e.g. 5.10 -> 210). Hopefully, by the time we 313 # do a 6.x Release we won't need ITUs any more. 314 VERSION=$(echo $RELEASE | $SED 's/5\./2/') 315 } 316 317 318 # 319 # If an ISO file was specified, get realpath of its parent directory ($ISODIR). 320 # If the ISO file already exists, ask user to overwrite it, unless -f option 321 # was specified. 322 # 323 function check_iso 324 { 325 if [[ "$ISO" = */* ]] 326 then 327 ISODIR=$(cd "${ISO%/*}" 2>/dev/null && pwd -P) 328 if (( $? )) 329 then 330 gettext "Can't access parent directory of ISO image.\n" 331 return 1 332 fi 333 else 334 ISODIR=$(pwd -P) 335 fi 336 337 if [[ -f "$ISO" ]] 338 then 339 is_overwrite "$ISO" || return 2 340 $RM -f "$ISO" 341 fi 342 343 return 0 344 } 345 346 347 # 348 # If specified, check the Driver Update output directory $DU_OUTDIR (-d option). 349 # Else set $DU_OUTDIR to a temporary directory. Also if $DU_OUTDIR/DU 350 # already exists, ask user whether to overwrite it, unless -f option was given. 351 # 352 function check_dudir 353 { 354 typeset realpath 355 356 if [[ -z "$DU_OUTDIR" ]] 357 then 358 DU_OUTDIR=$TMP_DIR/dudir 359 return 0 360 fi 361 362 # Verify user-specified DU output directory. 363 if [[ ! -d "$DU_OUTDIR" ]] 364 then 365 if [ `$MKDIR -p $DU_OUTDIR` ]; then 366 gettext "$DU_OUTDIR is not a directory.\n" 367 return 1 368 fi 369 elif [[ ! -w "$DU_OUTDIR" ]] 370 then 371 gettext "Directory $DU_OUTDIR is not writable.\n" 372 return 1 373 fi 374 375 # If an ISO image path is also specified, make sure it's not under 376 # $DU_OUTDIR since we might take the ISO image of $DU_OUTDIR. 377 if [[ -n "$ISODIR" ]] 378 then 379 realpath=$(cd "$DU_OUTDIR" 2>/dev/null && pwd -P) 380 if [[ "$ISODIR" = "$realpath"?(/*) ]] 381 then 382 gettext "ISO image must not be under Driver Update's output directory ($realpath).\n" 383 return 1 384 fi 385 fi 386 387 # If the DU directory already exists, ask user permission to 388 # remove it unless -f option was given. 389 if [[ -d "$DU_OUTDIR/DU" ]] 390 then 391 is_overwrite "$DU_OUTDIR/DU" || return 2 392 $RM -rf "$DU_OUTDIR/DU" || return 1 393 fi 394 395 return 0 396 } 397 398 399 # 400 # Verify $MEDIA_ROOT is indeed a Solaris install media. 401 # 402 function check_media_root 403 { 404 if [[ ! -d $(echo "$MEDIA_ROOT"/Solaris*/Tools/Boot) ]] 405 then 406 gettext "$MEDIA_ROOT is not a Solaris install media.\n" 407 return 1 408 fi 409 return 0 410 } 411 412 413 # 414 # Verify there's a miniroot file under $MEDIA_ROOT. Also set $MINIROOT 415 # to the path of the miniroot. 416 # 417 function check_miniroot 418 { 419 MINIROOT=$MEDIA_ROOT/boot/x86.miniroot 420 if [[ ! -f "$MINIROOT" ]] 421 then 422 return 0 423 fi 424 MINIROOT=$MEDIA_ROOT/boot/sparc.miniroot 425 if [[ ! -f "$MINIROOT" ]] 426 then 427 return 0 428 fi 429 gettext "Can't find $MEDIA_ROOT/boot/x86.miniroot or $MEDIA_ROOT/boot/sparc.miniroot.\n" 430 return 1 431 } 432 433 434 # 435 # Create a non-bootable ISO image of the given directory. 436 # 437 function create_nonboot_iso # <dir> 438 { 439 typeset dir vflag i 440 441 if (( $# != 1 )) 442 then 443 gettext "create_nonboot_iso missing argument.\n" 444 return 1 445 fi 446 dir=$1 447 448 # Skip if no ISO image was specified. 449 [[ -z "$ISO" ]] && return 0 450 451 # Determine mkisofs' verbose flag depending on $VERBOSE_LEVEL. 452 case $VERBOSE_LEVEL in 453 0) vflag=-quiet 454 ;; 455 1) vflag= # mkisofs' default verboseness 456 ;; 457 *) vflag= 458 i=$VERBOSE_LEVEL 459 while ((i > 0)) 460 do 461 vflag="-v $vflag" 462 (( i -= 1 )) 463 done 464 ;; 465 esac 466 467 print "Creating ISO image ..." 468 469 # Note: the "-log-file >(cat -u >&2)" and "2>/dev/null" below is a 470 # trick to filter out mkisofs's warning message about being 471 # non-conforming to ISO-9660. 472 # We do some funky architecture-specific stuff here so that we can 473 # actually create a bootable media image for UltraSPARC systems 474 475 sparc_ISOARGS="-B ... -joliet-long -U" 476 i386_ISOARGS="-d -N -r -relaxed-filenames" 477 if [[ "$MACH" = "i386" ]] 478 then 479 ISOARGS=$i386_ISOARGS 480 else 481 ISOARGS=$sparc_ISOARGS 482 fi 483 484 $MKISOFS -o "$ISO" \ 485 -allow-leading-dots \ 486 $ISOARGS \ 487 -l -ldots \ 488 -R -J \ 489 -V "$ISOLABEL" \ 490 $vflag \ 491 -log-file >(cat -u >&2) \ 492 "$dir" 2>/dev/null 493 } 494 495 496 # 497 # Create a bootable Solaris ISO image of the given Solaris install directory. 498 # 499 function create_bootable_iso # <dir> 500 { 501 typeset dir vflag saved i 502 503 if (( $# != 1 )) 504 then 505 gettext "create_bootable_iso missing argument.\n" 506 return 1 507 fi 508 dir=$1 509 510 # Skip if no ISO image was specified. 511 [[ -z "$ISO" ]] && return 0 512 513 # Determine mkisofs' verbose flag depending on $VERBOSE_LEVEL. 514 case $VERBOSE_LEVEL in 515 0) vflag=-quiet 516 ;; 517 1) vflag= # mkisofs' default verboseness 518 ;; 519 *) vflag= 520 i=$VERBOSE_LEVEL 521 while ((i > 0)) 522 do 523 vflag="-v $vflag" 524 (( i -= 1 )) 525 done 526 ;; 527 esac 528 529 # Verify the boot block exists under media root. If it does, 530 # verify it's writable since it will be modified with some boot 531 # information by mkisofs' -boot-info-table option. 532 if [[ ! -f "$dir/$BOOTBLOCK" ]] 533 then 534 gettext "Can't find $dir/$BOOTBLOCK.\n" 535 return 1 536 elif [[ ! -w "$dir/$BOOTBLOCK" ]] 537 then 538 gettext "$dir/$BOOTBLOCK is not writable.\n" 539 return 1 540 fi 541 542 gettext "Creating bootable ISO image ..." 543 544 # Since mkisofs below will modify the file $BOOTBLOCK in-place, save 545 # a copy of it first. 546 saved=$TMP_DIR/${BOOTBLOCK##*/} 547 $CP -f "$dir/$BOOTBLOCK" "$saved" || return 548 549 # Note: the "-log-file >(cat -u >&2)" and "2>/dev/null" below is a 550 # trick to filter out mkisofs's warning message about being 551 # non-conforming to ISO-9660. 552 # We do some funky architecture-specific stuff here so that we can 553 # actually create a bootable media image for UltraSPARC systems 554 sparc_ISOARGS="-G $BOOTBLOCK -B ... -joliet-long -U" 555 i386_ISOARGS="-b boot/grub/stage2_eltorito -boot-info-table " 556 i386_ISOARGS="$i386_ISOARGS -boot-load-size 4 -c .catalog -d -N " 557 i386_ISOARGS="$i386_ISOARGS -no-emul-boot -r -relaxed-filenames" 558 if [[ "$MACH" = "i386" ]] 559 then 560 ISOARGS=$i386_ISOARGS 561 else 562 ISOARGS=$sparc_ISOARGS 563 fi 564 565 cd $dir 566 $MKISOFS -o "$ISO" \ 567 -allow-leading-dots \ 568 $ISOARGS \ 569 -l -ldots \ 570 -R -J \ 571 -V "$ISOLABEL" \ 572 $vflag \ 573 -log-file >(cat -u >&2) \ 574 "$dir" 2>/dev/null 575 i=$? 576 577 # Restore saved El Torito file 578 $CP -f "$saved" "$dir/$ELTORITO" 2>/dev/null 579 580 return $i 581 } 582 583 584 # 585 # Create a Driver Update (DU) format directory from packages 586 # 587 function create_du 588 { 589 typeset distdir tmpdudir pkgs obj statusfile 590 591 # Create DU directory first. 592 distdir=$DU_OUTDIR/DU/sol_$VERSION/$MACH 593 $MKDIR -p "$distdir/Tools" "$distdir/Product" 594 595 echo "start create DU with MACH $MACH" 596 597 # If we're running this script on sun4[vu], then create a symlink 598 # to the other UltraSPARC architecture 599 if [[ "$MACH" != "i386" ]] 600 then 601 cd $DU_OUTDIR/DU/sol_$VERSION 602 $LN -s sparc sun4v 603 $LN -s sparc sun4u 604 else 605 cd $DU_OUTDIR/DU/sol_$VERSION 606 $LN -s i386 i86pc 607 fi 608 609 # Unfortunately pkgtrans insists that all packages must be in 610 # <device1> (see pkgtrans(1)). The packages can't have any path 611 # components. So we'll create a temporary directory first and then 612 # symlinks to the specified packages. Then run pkgtrans with 613 # the temporary directory as <device1>. 614 tmpdudir=$TMP_DIR/create_du 615 $RM -rf "$tmpdudir" 616 $MKDIR -p "$tmpdudir" 617 618 for obj in "${PACKAGES[@]}" 619 do 620 # Get rid of trailing /'s, if any. 621 [[ "$obj" == */ ]] && obj=${obj%%+(/)} 622 623 # Make sure it's full pathname. 624 [[ "$obj" != /* ]] && obj=$ORIGPWD/$obj 625 626 ln -s "$obj" "$tmpdudir" || return 627 628 # Remember just the file component. 629 pkgs[ ${#pkgs[*]} ]=${obj##*/} 630 done 631 632 # Package up packages as compressed data stream. 633 statusfile=$TMP_DIR/.pkgtrans.status 634 ( 635 # Use fd 9 for redirecting pkgtrans' "Transferring..." 636 # messages which normally go to stderr to current stdout 637 # (not the following pipeline's stdout). 638 exec 9>&1 639 { 640 $PKGTRANS -s "$tmpdudir" /dev/stdout "${pkgs[@]}" 2>&9 641 echo $? > $statusfile 642 $TOUCH $statusfile # make sure file is created 643 } | $GZIP -9 > "$distdir/Product/pkgs.gz" 644 ) 645 646 [[ -s $statusfile && $(<$statusfile) != 0 ]] && return 1 647 648 # Create admin file for pkgadd 649 $CAT > "$distdir/Tools/admin" <<"EOF" 650 mail= 651 instance=overwrite 652 partial=nocheck 653 runlevel=nocheck 654 idepend=nocheck 655 rdepend=nocheck 656 space=nocheck 657 setuid=nocheck 658 conflict=nocheck 659 action=nocheck 660 EOF 661 662 # Create install.sh 663 $CAT > "$distdir/Tools/install.sh" <<"EOF" 664 #!/sbin/sh 665 # install.sh -R <basedir> - install packages to basedir 666 basedir=/ 667 toolsdir=`dirname $0` 668 tmpfile=/tmp/`basename $0`.$$ 669 gzip=/usr/bin/gzip 670 while getopts "R:" arg 671 do 672 case "$arg" in 673 R) basedir=$OPTARG;; 674 esac 675 done 676 677 # /etc/driver_aliases ,/etc/driver_classes 678 # /etc/name_to_major /etc/minor_perm 679 # The four file can't append due to ACL defect. 680 # workaround this by mv and cp 681 682 /usr/bin/touch /etc/driver_aliases 683 if [ $? -ne 0 ] ; then 684 /bin/cp /etc/driver_aliases /tmp/driver_aliases 685 /bin/mv /etc/driver_aliases /tmp/driver_aliases.bak 686 /bin/mv /tmp/driver_aliases /etc 687 fi 688 /usr/bin/touch /etc/driver_classes 689 if [ $? -ne 0 ] ; then 690 /bin/cp /etc/driver_classes /tmp/driver_classes 691 /bin/mv /etc/driver_classes /tmp/driver_classes.bak 692 /bin/mv /tmp/driver_classes /etc 693 fi 694 /usr/bin/touch /etc/name_to_major 695 if [ $? -ne 0 ] ; then 696 /bin/cp /etc/name_to_major /tmp/name_to_major 697 /bin/mv /etc/name_to_major /tmp/name_to_major.bak 698 /bin/mv /tmp/name_to_major /etc 699 fi 700 /usr/bin/touch /etc/minor_perm 701 if [ $? -ne 0 ] ; then 702 /bin/cp /etc/minor_perm /tmp/minor_perm 703 /bin/mv /etc/minor_perm /tmp/minor_perm.bak 704 /bin/mv /tmp/minor_perm /etc 705 fi 706 707 # Make sure that we've got our own copy of /usr/bin/gzip 708 # in the tools directory 709 710 if [ ! -f $gzip ] ; then 711 gzip=$toolsdir/gzip 712 /usr/bin/chmod a+x "$toolsdir/gzip" 2>/dev/null 713 fi 714 715 $gzip -c -d "$toolsdir/../Product/pkgs.gz" > $tmpfile && 716 /usr/sbin/pkgadd -R "$basedir" -d "$tmpfile" -a "$toolsdir/admin" all 717 status=$? 718 rm -f "$tmpfile" 719 exit $status 720 EOF 721 $CHMOD a+rx "$distdir/Tools/install.sh" 722 723 $CP -f /usr/bin/gzip "$distdir/Tools" 2>/dev/null 724 } 725 726 727 # 728 # Unpack the miniroot of a Solaris install media. 729 # 730 function unpack_media 731 { 732 # Create temp directory to unpack the miniroot. 733 $MKDIR -p "$UNPACKED_ROOT" 734 735 # We need to use the unpackmedia option to correctly apply patches 736 gettext "Unpacking media ... " 737 $ROOT_ARCHIVE unpackmedia "$MEDIA_ROOT" "$UNPACKED_ROOT" > /dev/null 2>&1 738 if [ $? != 0 ]; then 739 if [ -d $MEDIA_ROOT/Solaris_10 -a -d $MEDIA_ROOT/Solaris_11 ]; then 740 # we _do_ care, because we're not patching a Solaris 741 # update media instance 742 gettext "There was an error unpacking the media from $MEDIA_ROOT\n" 743 exit 1 744 fi 745 fi 746 } 747 748 749 # 750 # Pack an unpacked miniroot onto a Solaris install media. 751 # 752 function repack_media 753 { 754 gettext "Repacking media ..." 755 756 # We need to ensure that we're using the appropriate version 757 # of root_archive for the media that we're packing/unpacking. 758 # The onnv version of root_archive differs from the S10 version, 759 # and this will cause problems on re-packing. So we sneakily 760 # use the version that we've just unpacked 761 if [ -d $MEDIA_ROOT/Solaris_10 ]; then 762 ROOT_ARCHIVE=$UNPACKED_ROOT/boot/solaris/bin/root_archive 763 fi 764 765 $ROOT_ARCHIVE packmedia "$MEDIA_ROOT" "$UNPACKED_ROOT" > /dev/null 2>&1 766 if [ $? != 0 ]; then 767 if [ -d $MEDIA_ROOT/Solaris_10 -a -d $MEDIA_ROOT/Solaris_11 ]; then 768 # we _do_ care, because we're not patching a Solaris 769 # update media instance 770 gettext "There was an error packing the media from $MEDIA_ROOT\n" 771 exit 1 772 fi 773 fi 774 gettext "Done.\n" 775 } 776 777 778 # 779 # Add packages to a Solaris install media. Also install these packages 780 # onto the miniroot. 781 # 782 function add_pkgs 783 { 784 typeset icmd statusfile i 785 786 (( ${#PACKAGES[*]} == 0 )) && return 787 788 statusfile=$TMP_DIR/.add_pkgs.status 789 790 DU_OUTDIR=$ITUDIR/$ITU_COUNTDIR 791 (( ITU_COUNTDIR += 1 )) 792 $MKDIR "$DU_OUTDIR" || return 793 794 # 795 # Add a Driver Update directory on the media 796 # 797 echo; 798 gettext "Adding package(s) to media root.\n" 799 create_du || return 800 801 # 802 # Using the Driver Update above install the packages onto the miniroot. 803 # 804 echo; 805 gettext "Installing package(s) onto miniroot.\n" 806 icmd=$DU_OUTDIR/DU/sol_$VERSION/$MACH/Tools/install.sh 807 if [[ ! -f "$icmd" ]] 808 then 809 # This shouldn't happen, but just in case. 810 gettext "Cannot find $icmd.\n" 811 return 1 812 fi 813 [[ ! -x "$icmd" ]] && chmod a+x "$icmd" 814 815 $RM -f "$statusfile" 816 { 817 "$icmd" -R "$UNPACKED_ROOT" 818 if (( i=$? )) 819 then 820 echo $i > "$statusfile" 821 $TOUCH "$statusfile" # make sure file is created 822 fi 823 } 2>&1 | $NAWK -v logfile="$LOGFILE" -v vlevel=$VERBOSE_LEVEL ' 824 # If not verbose, print certain lines from patchadd. 825 (vlevel == 0) && /^Installing/ {print} 826 (vlevel == 0) && /^Installation.*successful/ {print} 827 828 # If verbose, print every line to stderr. 829 (vlevel > 0) {print > "/dev/stderr"} 830 831 # Save every line to logfile. 832 {print >> logfile} 833 ' || return 834 [[ -s "$statusfile" ]] && return $(<$statusfile) 835 return 0 836 } 837 838 839 # 840 # Add patches to a Solaris install media. Also patch the miniroot with 841 # these patches 842 # 843 function add_patches 844 { 845 typeset distdir tmpdir icmd obj patches statusfile 846 847 (( ${#PATCHES[*]} == 0 )) && return 848 849 tmpdir=$TMP_DIR/patches 850 statusfile=$TMP_DIR/.add_patches.status 851 852 $RM -rf "$tmpdir" 853 854 distdir=$ITUDIR/$ITU_COUNTDIR/DU/sol_$VERSION/$MACH 855 (( ITU_COUNTDIR += 1 )) 856 857 $MKDIR -p "$distdir/Tools" "$distdir/Product" "$tmpdir" || return 858 859 # If we're running this script on sun4[vu], then create a symlink 860 # to the other UltraSPARC architecture 861 if [[ "$MACH" != "i386" ]] 862 then 863 cd $ITUDIR/$ITU_COUNTDIR/DU/sol_$VERSION 864 $LN -s sparc sun4v 865 $LN -s sparc sun4u 866 else 867 cd $ITUDIR/$ITU_COUNTDIR/DU/sol_$VERSION 868 $LN -s i386 i86pc 869 fi 870 871 # 872 # Add packages onto media root 873 # 874 echo; 875 gettext "Adding patch(es) to media root.\n" 876 877 # Symlink each patch in a temp dir so a single cpio/gzip can work. 878 for obj in "${PATCHES[@]}" 879 do 880 # Get rid of trailing /'s, if any. 881 [[ "$obj" == */ ]] && obj=${obj%%+(/)} 882 883 # Make sure it's a full pathname. 884 [[ "$obj" != /* ]] && obj=$ORIGPWD/$obj 885 886 $LN -s "$obj" "$tmpdir" || return 887 888 # Remember just the file component. 889 patches[ ${#patches[*]} ]=${obj##*/} 890 done 891 892 # Package up patches as compressed cpio archive. 893 $RM -f "$statusfile" 894 ( 895 # Save current stdout as fd 8. This doesn't point to the 896 # gzip pipeline below. 897 exec 8>&1 898 899 { 900 # Fd 9 is used later on for filtering out cpio's 901 # reporting total blocks to stderr but yet still 902 # print other error messages. fd 9 refers to the 903 # pipeline to gzip. 904 exec 9>&1 905 906 cd "$tmpdir" 907 for obj in "${patches[@]}" 908 do 909 print -u8 "Transferring patch $obj." 910 $FIND "$obj/." -follow -print 911 if (( i=$? )) 912 then 913 echo $i > "$statusfile" 914 $TOUCH "$statusfile" 915 return $i 916 fi 917 done | $CPIO -oc 2>&1 >&9 | $GREP -v '^[0-9]* blocks' >&2 918 } | $GZIP -9 > "$distdir/Product/patches.gz" 919 ) || return 920 921 [[ -s "$statusfile" ]] && return $(<$statusfile) 922 923 # Create install.sh 924 $CAT > "$distdir/Tools/install.sh" <<"EOF" 925 #!/sbin/sh 926 # install.sh -R <basedir> - install patches to basedir 927 basedir=/ 928 toolsdir=`dirname $0` 929 tmpdir=/tmp/`basename $0`.$$ 930 gzip=/usr/bin/gzip 931 trap "/bin/rm -rf $tmpdir" 0 932 while getopts "R:" arg 933 do 934 case "$arg" in 935 R) basedir=$OPTARG;; 936 esac 937 done 938 /bin/mkdir -p "$tmpdir" || exit 939 tmpfile=$tmpdir/patches 940 patchdir=$tmpdir/patchdir 941 /bin/mkdir "$patchdir" || exit 942 943 # Make sure that we've got our own copy of /usr/bin/gzip 944 # in the tools directory 945 946 if [ ! -f $gzip ] ; then 947 gzip=$toolsdir/gzip 948 /usr/bin/chmod a+x "$toolsdir/gzip" 2>/dev/null 949 fi 950 951 $gzip -c -d "$toolsdir/../Product/patches.gz" > $tmpfile || exit 952 cd "$patchdir" 953 /bin/cpio -idum < "$tmpfile" || exit 954 patchadd -R "$basedir" -nu * 955 EOF 956 $CHMOD a+rx "$distdir/Tools/install.sh" 957 958 $CP -f /usr/bin/gzip "$distdir/Tools" 2>/dev/null 959 960 # 961 # Patch the miniroot 962 # 963 echo; 964 gettext "Installing patch(es) onto miniroot.\n" 965 $RM -f "$statusfile" 966 { 967 $PATCHADD -udn -C "$UNPACKED_ROOT" "${PATCHES[@]}" 968 if (( i=$? )) 969 then 970 echo $i > "$statusfile" 971 $TOUCH "$statusfile" # make sure file is created 972 fi 973 } 2>&1 | $NAWK -v logfile="$LOGFILE" -v vlevel=$VERBOSE_LEVEL ' 974 # If not verbose, print certain lines from patchadd. 975 (vlevel == 0) && /^Patch.*successful/ {print} 976 977 # If verbose, print every line to stderr. 978 (vlevel > 0) {print > "/dev/stderr"} 979 980 # Save every line to logfile. 981 {print >> logfile} 982 ' || return 983 984 [[ -s "$statusfile" ]] && return $(<$statusfile) 985 986 # Remove patch log files to save space when miniroot is repacked. 987 $RM -rf "$UNPACKED_ROOT"/var/sadm/patch 988 } 989 990 991 # 992 # Starting point for makedu subcommand: 993 # 994 # Convert packages into Driver Update (DU) directory format. 995 # 996 function makedu # <arg> ... 997 { 998 typeset i 999 1000 process_options "$@" || return 1001 shift 'MYOPTIND - 1' 1002 1003 if (( $# == 0 )) 1004 then 1005 gettext "Please specify one or more packages.\n" 1006 usage_short 1007 return 1 1008 fi 1009 1010 # Release number must be specified. 1011 if [[ -z "$RELEASE" ]] 1012 then 1013 gettext "Please specify Solaris release number (-r option).\n" 1014 usage_short 1015 return 1 1016 fi 1017 check_release || return 1018 1019 # Either -d or -o option, or both, must be specified. 1020 if [[ -z "$DU_OUTDIR" && -z "$ISO" ]] 1021 then 1022 gettext "Please specify either -d or -o option (or both).\n" 1023 usage_short 1024 return 1 1025 fi 1026 1027 if [[ -n "$ISO" ]] 1028 then 1029 check_iso || return 1030 ${ISOLABEL:=DU sol_$VERSION} 2>/dev/null # default ISO label 1031 fi 1032 check_dudir || return # should be called after check_iso 1033 1034 # Rest of arguments must be packages. 1035 collect_packages "$@" || return 1036 1037 check_prereqs || return 1038 1039 # Create DU and the (non-bootable) ISO image (if requested). 1040 create_du && create_nonboot_iso "$DU_OUTDIR" 1041 if (( i=$? )) 1042 then 1043 $RM -rf "$DU_OUTDIR/DU" 1044 [[ -n "$ISO" ]] && rm -f "$ISO" 1045 else 1046 if [[ "$MACH" != "i386" ]] 1047 then 1048 echo "This DU must be written as either an ISO (hsfs)" 1049 echo "or a *ufs* filesystem. DO NOT USE pcfs!" 1050 fi 1051 fi 1052 return $i 1053 } 1054 1055 1056 # 1057 # Starting point for patchmedia subcommand: 1058 # 1059 # Patch a Solaris install image with the given packages and patches. 1060 # 1061 function patchmedia # <arg> ... 1062 { 1063 typeset soldir 1064 1065 process_options "$@" || return 1066 shift 'MYOPTIND - 1' 1067 1068 if (( $# == 0 )) 1069 then 1070 gettext "Please specify one or more packages or patches.\n" 1071 usage_short 1072 return 1 1073 fi 1074 1075 # -R option must be specified 1076 if [[ -z "$MEDIA_ROOT" ]] 1077 then 1078 gettext "Please specify Solaris media root (-R option).\n" 1079 usage_short 1080 return 1 1081 fi 1082 check_media_root || return 1083 1084 # Get the Solaris directory under $MEDIA_ROOT. 1085 soldir=$($LS -d $MEDIA_ROOT/Solaris* 2>/dev/null) 1086 if [[ -z "$soldir" ]] 1087 then 1088 gettext "Can't find Solaris directory in $MEDIA_ROOT.\n" 1089 return 1 1090 fi 1091 1092 # Extract the Solaris release number from the Solaris_* directory. 1093 RELEASE=5.${soldir##*Solaris_} 1094 check_release || return 1095 1096 # If user specifies an ISO image to create. 1097 if [[ -n "$ISO" ]] 1098 then 1099 check_iso || return 1100 ${ISOLABEL:=${soldir##*/}} # default ISO label 1101 fi 1102 1103 # Rest of arguments must be packages or patches. 1104 collect_packages_patches "$@" || return 1105 1106 # Verify we have some important utilities we need. 1107 check_prereqs || return 1108 1109 # Verify there's miniroot file in $MEDIA_ROOT. 1110 check_miniroot || return 1111 1112 # Create the ITU directory on the media root, if necessary 1113 ITUDIR=$MEDIA_ROOT/ITUs 1114 $MKDIR -p "$ITUDIR" || return 1115 1116 # The ITU directory might contain multiple driver updates already, 1117 # each in a separate numbered subdirectory. So look for the 1118 # subdirectory with the highest number and we'll add the packages 1119 # and patches on the next one. 1120 ITU_COUNTDIR=$($LS -d "$ITUDIR"/+([0-9]) 2>/dev/null | 1121 $SED 's;.*/;;' | $SORT -rn | $HEAD -1) 1122 if [[ $ITU_COUNTDIR == *( ) ]] # ITU_COUNTDIR is a typeset -Zn var 1123 then 1124 ITU_COUNTDIR=0 1125 else 1126 (( ITU_COUNTDIR += 1 )) 1127 fi 1128 1129 unpack_media || return 1130 add_pkgs && add_patches 1131 if (( status=$? )) && [[ -s "$LOGFILE" ]] 1132 then 1133 echo; 1134 gettext "A package or patch installation has failed.\n" 1135 gettext "Messages from pkgadd and patchadd have been saved in $LOGFILE\n" 1136 return $status 1137 else 1138 rm -f "$LOGFILE" 1139 fi 1140 print 1141 repack_media || return 1142 create_bootable_iso "$MEDIA_ROOT" 1143 gettext "$MEDIA_ROOT has been successfully patched\n" 1144 } 1145 1146 1147 # 1148 # Starting point for makeiso subcommand: 1149 # 1150 # Create a bootable ISO image of a Solaris install image. 1151 # 1152 function makeiso # <arg> .. 1153 { 1154 process_options "$@" || return 1155 shift 'MYOPTIND - 1' 1156 1157 if (( $# == 0 )) 1158 then 1159 gettext "Please specify the Solaris media root.\n" 1160 usage_short 1161 return 1 1162 elif (( $# > 1 )) 1163 then 1164 gettext "Too many arguments supplied.\n" 1165 usage_short 1166 return 1 1167 fi 1168 MEDIA_ROOT=$1 1169 check_media_root || return 1170 1171 # ISO image must be specified. 1172 if [[ -z "$ISO" ]] 1173 then 1174 gettext "Please specify ISO image file (-o option).\n" 1175 usage_short 1176 return 1 1177 fi 1178 check_iso || return 1179 1180 # If user doesn't specify ISO label, use the Solaris_* directory name 1181 # under $MEDIA_ROOT. 1182 if [[ -z "$ISOLABEL" ]] 1183 then 1184 ISOLABEL=$(echo "$MEDIA_ROOT"/Solaris*) 1185 ISOLABEL=${ISOLABEL##*/} 1186 fi 1187 1188 check_prereqs || return 1189 create_bootable_iso "$MEDIA_ROOT" 1190 } 1191 1192 1193 # 1194 # Main 1195 # 1196 trap cleanup EXIT 1197 1198 # Numbered subdirectories under ITU directory $ITUDIR. 1199 typeset -Z3 ITU_COUNTDIR=0 1200 1201 # Where to unpack a miniroot. 1202 UNPACKED_ROOT=${TMP_DIR}/miniroot 1203 1204 # Reset arrays. 1205 unset PACKAGES PATCHES 1206 1207 DU_OUTDIR= 1208 FORCE=0 1209 ISO= 1210 ISOLABEL= 1211 MEDIA_ROOT= 1212 RELEASE= 1213 SUBCOMMAND= 1214 VERBOSE_LEVEL=0 1215 VERBOSE_OPTS= 1216 1217 if (( $# == 0 )) 1218 then 1219 usage_long 1220 return 1 1221 fi 1222 typeset -l SUBCOMMAND=$1 # ignore case 1223 shift 1224 1225 if [[ $SUBCOMMAND != @(makedu|patchmedia|makeiso) ]] 1226 then 1227 # Be nice: allow some subcommands that cry out "help". 1228 case "$SUBCOMMAND" in 1229 *(-)help|*(-)usage|-h|-\?) 1230 usage_long 1231 return 0 1232 ;; 1233 *) 1234 gettext "Invalid subcommand: $SUBCOMMAND.\n" 1235 usage_short 1236 return 1 1237 ;; 1238 esac 1239 fi 1240 1241 $MKDIR -p "$TMP_DIR" || return 1242 $RM -f "$LOGFILE" 1243 1244 # Run the subcommand. 1245 $SUBCOMMAND "$@"