Home | History | Annotate | Download | only in itutools
      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 "$@"
   1246