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 # updatemedia - modify Solaris media with patches and packages
     29 #
     30 
     31 readonly PROG=$0
     32 readonly TMP_DIR=${TMPDIR:-/tmp}/${PROG##*/}.$$
     33 readonly LOGFILE=${TMPDIR:-/tmp}/${PROG##*/}-log.$$
     34 
     35 # Must-have utilities
     36 readonly CPIO=/bin/cpio
     37 readonly GZIP=/bin/gzip
     38 readonly MKISOFS=/usr/bin/mkisofs
     39 readonly PATCHADD=/usr/sbin/patchadd
     40 readonly LOFIADM=/usr/sbin/lofiadm
     41 readonly MKDIR=/usr/bin/mkdir
     42 readonly RM=/usr/bin/rm
     43 readonly CP=/usr/bin/cp
     44 readonly MKBOOTMEDIA=/usr/bin/mkbootmedia
     45 readonly PKG2DU=/usr/bin/pkg2du
     46 readonly TOUCH=/usr/bin/touch
     47 readonly NAWK=/usr/bin/nawk
     48 readonly CHMOD=/usr/bin/chmod
     49 readonly GREP=/usr/bin/grep
     50 readonly LS=/usr/bin/ls
     51 readonly LN=/usr/bin/ln
     52 readonly SED=/usr/bin/sed
     53 readonly CAT=/usr/bin/cat
     54 readonly FIND=/usr/bin/find
     55 readonly HEAD=/usr/bin/head
     56 readonly SORT=/usr/bin/sort
     57 readonly UNAME=/usr/bin/uname
     58 readonly MACH=`$UNAME -p`
     59 
     60 ROOT_ARCHIVE=/usr/sbin/root_archive
     61 # for gettext
     62 TEXTDOMAIN=SUNW_OST_OSCMD
     63 export TEXTDOMAIN
     64 
     65 
     66 function usage
     67 {
     68 	gettext "Usage:\n${PROG##*/} -d <media-root> [-v] [-l <label>] [-o <iso>]\n        <pkg_or_patch> [<pkg_or_patch> ...]\n"
     69 	gettext "Options:\n  -d <media-root>\n        Top-level directory of on-disk image of Solaris installation media.\n        This is option must be specified.\n"
     70 	gettext "  -l <label>\n        Label/volume name of the ISO image (if -o option is specified).\n"
     71 	gettext "  -o <iso>\n        Create a Solaris ISO image of <media-root>.\n"
     72 	gettext "  -v\n        Verbose.  Multiple -v options increase verbosity.\n"
     73 }
     74 
     75 
     76 function check_prereqs
     77 {
     78 	typeset f
     79 
     80 	# We must have these utilities.
     81 	for f in $CPIO $GZIP ${ISO:+$MKISOFS} $PATCHADD $ROOT_ARCHIVE
     82 	do 
     83 		if [[ ! -x "$f" ]]
     84 		then
     85 			gettext "Cannot find required utility $f\n"
     86 			exit 1
     87 		fi
     88 	done
     89 
     90 	# root_archive unpack_media calls lofiadm -a, which requires
     91 	# write access as determined by /dev/lofictl.  See lofiadm(1m).
     92 	if [[ ! -w /dev/lofictl ]]
     93 	then
     94 		gettext "You do not have enough privileges to run lofiadm -a).\nSee lofiadm(1m) for more information.\n"
     95 		exit 1
     96 	fi
     97 }
     98 
     99 
    100 function cleanup
    101 {
    102 	$RM -rf "$TMP_DIR"
    103 }
    104 
    105 
    106 function unpack_media
    107 {
    108 	# Create temp directory to unpack the miniroot.
    109 	$MKDIR -p "$UNPACKED_ROOT"
    110 
    111 	# We need to use the unpackmedia option to correctly apply patches
    112 	gettext "Unpacking media ..."
    113 	$ROOT_ARCHIVE unpackmedia "$MEDIA_ROOT" "$UNPACKED_ROOT" > /dev/null 2>&1 
    114 	if [ $? != 0 -a ! -d $MEDIA_ROOT/Solaris_10 ]; then
    115 		# we _do_ care, because we're not patching a Solaris 10
    116 		# update media instance
    117 		gettext "\nThere was an error unpacking the media from $MEDIA_ROOT\n"
    118 		exit 1
    119 	fi
    120 	echo;
    121 }
    122 
    123 
    124 function repack_media
    125 {
    126 	gettext "Repacking media ..."
    127 
    128 	# We need to ensure that we're using the appropriate version
    129 	# of root_archive for the media that we're packing/unpacking.
    130 	# The onnv version of root_archive differs from the S10 version,
    131 	# and this will cause problems on re-packing. So we sneakily
    132 	# use the version that we've just unpacked
    133 	if [ -d $MEDIA_ROOT/Solaris_10 ]; then
    134 		ROOT_ARCHIVE=$UNPACKED_ROOT/boot/solaris/bin/root_archive
    135 	fi
    136 
    137 	$ROOT_ARCHIVE packmedia "$MEDIA_ROOT" "$UNPACKED_ROOT" > /dev/null 2>&1
    138 	if [ $? != 0 -a ! -d $MEDIA_ROOT/Solaris_10 ]; then
    139 		# we _do_ care, because we're not patching a Solaris 10
    140 		# update media instance
    141 		gettext "\nThere was an error unpacking the media from $MEDIA_ROOT\n"
    142 		exit 1
    143 	fi
    144 	echo;
    145 }
    146 
    147 
    148 function mkiso
    149 {
    150 	typeset vflag
    151 
    152 	# Skip if no ISO image was specified.
    153 	[[ -z "$ISO" ]] && return 0
    154 
    155 	gettext "Creating ISO image ..."
    156 	$MKBOOTMEDIA $VERBOSE_OPTS -l "$ISOLABEL" "$MEDIA_ROOT" "$ISO"
    157 	echo;
    158 }
    159 
    160 
    161 function collect_objs # <pkg_or_patch> ...
    162 {
    163 	typeset obj fail=0
    164 
    165 	for obj
    166 	do
    167 		if [[ -f "$obj"/patchinfo ]]
    168 		then
    169 			PATCHES[ ${#PATCHES[*]} ]=$obj
    170 		elif [[ -f "$obj"/pkginfo ]]
    171 		then
    172 			PACKAGES[ ${#PACKAGES[*]} ]=$obj
    173 		else
    174 			gettext "$obj is not in package or patch format\n"
    175 			(( fail += 1 ))
    176 		fi
    177 	done
    178 	(( fail )) && return 1
    179 	return 0
    180 }
    181 
    182 
    183 function add_pkgs
    184 {
    185 	typeset dudir icmd statusfile
    186 
    187 	(( ${#PACKAGES[*]} == 0 )) && return
    188 
    189 	statusfile=$TMP_DIR/.add_pkgs.status
    190 
    191 	trap '$RM -f $statusfile' EXIT
    192 
    193 	dudir=$ITUDIR/$COUNTDIR
    194 	(( COUNTDIR += 1 ))
    195 	$MKDIR "$dudir" || return
    196 
    197 	# Add a Driver Update directory on the media
    198 	echo;
    199 	gettext "Adding package(s) to media root."
    200 	$PKG2DU -r "$RELEASE" -f -d "$dudir" $VERBOSE_OPTS \
    201 	    "${PACKAGES[@]}" || return
    202 
    203 	# Using the Driver Update above install the packages onto the miniroot.
    204 	echo;
    205 	gettext "Installing package(s) onto miniroot."
    206 	icmd=$dudir/DU/sol_$VERSION/$MACH/Tools/install.sh
    207 	if [[ ! -f "$icmd" ]]
    208 	then
    209 		# This shouldn't happen, but just in case.
    210 		gettext "Cannot find $icmd\n"
    211 		return 1
    212 	fi
    213 	[[ ! -x "$icmd" ]] && $CHMOD a+x "$icmd"
    214 
    215 	$RM -f "$statusfile"
    216         {
    217 		"$icmd" -R "$UNPACKED_ROOT"
    218 		if (( i=$? ))
    219 		then
    220 			echo $i > "$statusfile"
    221 			$TOUCH "$statusfile"  # make sure file is created
    222 		fi
    223         } 2>&1 | $NAWK -v logfile="$LOGFILE" '
    224 		# Print certain lines from $icmd, save all in logfile.
    225 		/^Installing/ {print}
    226 		/^Installation.*successful/ {print}
    227 		{print >> logfile}
    228 	' || return
    229 	[[ -s "$statusfile" ]] && return $(<$statusfile)
    230 	return 0
    231 }
    232 
    233 
    234 function add_patches
    235 {
    236 	typeset distdir tmpdir icmd obj patches statusfile
    237 
    238 	(( ${#PATCHES[*]} == 0 )) && return
    239 
    240 	tmpdir=$TMP_DIR/patches
    241 	statusfile=$TMP_DIR/.add_patches.status
    242 
    243 	trap '$RM -rf $tmpdir $statusfile' EXIT
    244 
    245 	distdir=$ITUDIR/$COUNTDIR/DU/sol_$VERSION/$MACH
    246 	(( COUNTDIR += 1 ))
    247 
    248 	$MKDIR -p "$distdir/Tools" "$distdir/Product" "$tmpdir" || return
    249 
    250 	# If we're running this script on sun4[vu], then create a symlink
    251 	# to the other UltraSPARC architecture
    252 	if [[ "$MACH" != "i386" ]]
    253 	then
    254 		cd $ITUDIR/$COUNTDIR/DU/sol_$VERSION/
    255 		$LN -s sparc sun4v
    256 		$LN -s sparc sun4u
    257 	else
    258 		cd $ITUDIR/$COUNTDIR/DU/sol_$VERSION/
    259 		$LN -s i386 i86pc
    260 	fi	
    261 
    262 	# Patch the miniroot
    263 	echo;
    264 	gettext "Installing patch(es) onto miniroot."
    265 	$RM -f "$statusfile"
    266 	{
    267 		$PATCHADD -udn -C "$UNPACKED_ROOT" "${PATCHES[@]}"
    268 		if (( i=$? ))
    269 		then
    270 			echo $i > "$statusfile"
    271 			$TOUCH "$statusfile" # make sure file is created
    272 		fi
    273         } 2>&1 | $NAWK -v logfile="$LOGFILE" '
    274 		# Print certain lines from patchadd, save all in logfile.
    275 		/^Patch.*successful/ {print}
    276 		{print >> logfile}
    277 	' || return
    278 
    279 	[[ -s "$statusfile" ]] && return $(<$statusfile)
    280 
    281 	# Remove patch log files to save space when miniroot is repacked.
    282 	$RM -rf "$UNPACKED_ROOT"/var/sadm/patch
    283 
    284 	# Symlink each patch in a temp dir so a single cpio/gzip can work.
    285 	for obj in "${PATCHES[@]}"
    286 	do
    287 		# Get rid of trailing /'s, if any.
    288 		[[ "$obj" == */ ]] && obj=${obj%%+(/)}
    289 
    290 		# Make sure it's full pathname.
    291 		[[ "$obj" != /* ]] && obj=$ORIGPWD/$obj
    292 
    293 		$LN -s "$obj" "$tmpdir" || return
    294 
    295 		# Remember just the file component.
    296 		patches[ ${#patches[*]} ]=${obj##*/}
    297 	done
    298 
    299 	# Package up patches as compressed cpio archive.
    300 	echo;
    301 	gettext "Adding patch(es) to media root.\n"
    302 	$RM -f "$statusfile"
    303 	(
    304 		cd "$tmpdir"
    305 		# fd 9 is used later on for filtering out cpio's
    306 		# reporting total blocks to stderr but yet still
    307 		# print other error messages.
    308 		exec 9>&1
    309 		for obj in "${patches[@]}"
    310 		do
    311 			gettext "Transferring patch $obj\n"
    312 			$FIND "$obj/." -follow -print
    313 			if (( i=$? ))
    314 			then
    315 				echo $i > "$statusfile"
    316 				$TOUCH "$statusfile"
    317 				return $i
    318 			fi
    319 		done | $CPIO -oc 2>&1 >&9 | $GREP -v '^[0-9]* blocks' >&2
    320 	) | $GZIP -9 > "$distdir/Product/patches.gz" || return
    321 
    322 	[[ -s "$statusfile" ]] && return $(<$statusfile)
    323 
    324 	# Create install.sh
    325 	$CAT > "$distdir/Tools/install.sh" <<"EOF"
    326 #!/sbin/sh
    327 # install.sh -R <basedir> - install patches to basedir
    328 basedir=/
    329 toolsdir=`dirname $0`
    330 tmpdir=/tmp/`basename $0`.$$
    331 trap "/bin/rm -rf $tmpdir" 0
    332 while getopts "R:" arg
    333 do
    334         case "$arg" in
    335                 R) basedir=$OPTARG;;
    336         esac
    337 done
    338 /bin/mkdir -p "$tmpdir" || exit
    339 tmpfile=$tmpdir/patches
    340 patchdir=$tmpdir/patchdir
    341 /bin/mkdir "$patchdir" || exit
    342 /usr/bin/gzip -c -d "$toolsdir/../Product/patches.gz" > $tmpfile || exit
    343 cd "$patchdir"
    344 /bin/cpio -idum < "$tmpfile" || exit
    345 /usr/sbin/patchadd -R "$basedir" -nu *
    346 EOF
    347 	$CHMOD a+rx "$distdir/Tools/install.sh"
    348 
    349 }
    350 
    351 
    352 #
    353 # Main
    354 #
    355 trap cleanup EXIT
    356 
    357 ISO=
    358 ISOLABEL=
    359 MEDIA_ROOT=
    360 VERBOSE_LEVEL=0
    361 VERBOSE_OPTS=
    362 
    363 while getopts ':d:o:l:v' opt
    364 do
    365 	case $opt in
    366 	d)	MEDIA_ROOT=$OPTARG
    367 		;;
    368 	o)	ISO=$OPTARG
    369 		if [ ! -z `echo $ISO | $GREP "^/tmp"` ]; then
    370 		        gettext "ISO images will not be created on /tmp.\nPlease choose a different output location.\n"
    371 			exit 3
    372 		fi
    373 		;;
    374 	l)	ISOLABEL=$OPTARG
    375 		;;
    376 	v)	(( VERBOSE_LEVEL += 1 ))
    377 		VERBOSE_OPTS="${VERBOSE_OPTS:--}$opt"	# collect -v options
    378 		;;
    379 	:)	gettext "Option -$OPTARG missing argument.\n"
    380 		usage
    381 		exit 1
    382 		;;
    383 	*)	gettext "Option -$OPTARG is invalid.\n"
    384 		usage
    385 		exit 2
    386 		;;
    387 	esac
    388 done
    389 shift 'OPTIND - 1'
    390 
    391 unset PACKAGES PATCHES				# reset arrays
    392 collect_objs "$@"
    393 
    394 # If there are no packages or patches, then print info and we're done.
    395 if (( ${#PACKAGES[*]} == 0 && ${#PATCHES[*]} == 0 ))
    396 then
    397 	gettext "No valid package or patch was specified.\nPackages and patches must be unpacked.\n"
    398 	usage
    399 	exit 1
    400 fi
    401 
    402 # -d option must be specified
    403 if [[ -z "$MEDIA_ROOT" ]]
    404 then
    405 	gettext "No media root (-d option) was specified.\n"
    406 	usage
    407 	exit 1
    408 fi
    409 
    410 check_prereqs		# must be called after $ISO is possibly set
    411 
    412 # Verify it's a Solaris install media.
    413 SOLARIS_DIR=$($LS -d $MEDIA_ROOT/Solaris* 2>/dev/null)
    414 if [[ -z "$SOLARIS_DIR" || ! -d "$SOLARIS_DIR/Tools/Boot" ]]
    415 then
    416 	gettext "$MEDIA_ROOT is not valid Solaris install media.\n"
    417 	exit 1
    418 fi
    419 
    420 $MKDIR -p "$TMP_DIR" || exit 1
    421 
    422 # Extract the Solaris release number from the Solaris_* directory and the
    423 # corresponding version number.  As defined by the ITU spec, a Solaris release
    424 # number 5.x corresponds to version number 2x (e.g. 5.10 -> 210).
    425 RELEASE=5.${SOLARIS_DIR##*Solaris_}
    426 VERSION=$(echo $RELEASE | $SED 's/5\./2/')
    427 
    428 # If user didn't specify ISO label, use the Solaris_* dir as label.
    429 ${ISOLABEL:=${SOLARIS_DIR##*/}} 2>/dev/null
    430 
    431 # Verify miniroot
    432 
    433 MINIROOT=
    434 # Relative to a Solaris media root.
    435 if [ "$MACH" = "sparc" ]; then
    436 	MINIROOT=$MEDIA_ROOT/boot/sparc.miniroot
    437 else
    438 	# x86/x64
    439 	MINIROOT=$MEDIA_ROOT/boot/x86.miniroot
    440 fi
    441 if [ ! -f $MINIROOT ]; then
    442 	gettext "No boot/x86.miniroot under media root.\n"
    443 	exit 1
    444 fi
    445 
    446 # Where to unpack the miniroot.
    447 UNPACKED_ROOT=${TMP_DIR}/miniroot
    448 
    449 # Create the ITU directory on the media, if necessary
    450 ITUDIR=$MEDIA_ROOT/ITUs
    451 $MKDIR -p "$ITUDIR" || exit 1
    452 
    453 # The ITU directory might contain multiple driver updates already, each in a
    454 # separate numbered subdirectory.  So look for the subdirectory with the
    455 # highest number and we'll add the packages and patches on the next one.
    456 typeset -Z3 COUNTDIR
    457 COUNTDIR=$($LS -d "$ITUDIR"/+([0-9]) 2>/dev/null | $SED 's;.*/;;' |
    458 		$SORT -rn | $HEAD -1)
    459 if [[ $COUNTDIR == *( ) ]]
    460 then
    461 	COUNTDIR=0
    462 else
    463 	(( COUNTDIR += 1 ))
    464 fi
    465 
    466 unpack_media || exit
    467 add_pkgs && add_patches
    468 if (( status=$? )) && [[ -s "$LOGFILE" ]]
    469 then
    470 	echo;
    471 	gettext "A package or patch installation has failed.\nMessages from pkgadd and patchadd have been saved in $LOGFILE\n"
    472 	exit $status
    473 else
    474 	$RM -f "$LOGFILE"
    475 fi
    476 print
    477 repack_media || exit
    478 mkiso
    479