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 "$@"