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 31readonly PROG=$0 32readonly TMP_DIR=${TMPDIR:-/tmp}/${PROG##*/}.$$ 33readonly LOGFILE=${TMPDIR:-/tmp}/${PROG##*/}-log.$$ 34 35# Must-have utilities 36readonly CPIO=/bin/cpio 37readonly GZIP=/bin/gzip 38readonly MKISOFS=/usr/bin/mkisofs 39readonly PATCHADD=/usr/sbin/patchadd 40readonly LOFIADM=/usr/sbin/lofiadm 41readonly MKDIR=/usr/bin/mkdir 42readonly RM=/usr/bin/rm 43readonly CP=/usr/bin/cp 44readonly MKBOOTMEDIA=/usr/bin/mkbootmedia 45readonly PKG2DU=/usr/bin/pkg2du 46readonly TOUCH=/usr/bin/touch 47readonly NAWK=/usr/bin/nawk 48readonly CHMOD=/usr/bin/chmod 49readonly GREP=/usr/bin/grep 50readonly LS=/usr/bin/ls 51readonly LN=/usr/bin/ln 52readonly SED=/usr/bin/sed 53readonly CAT=/usr/bin/cat 54readonly FIND=/usr/bin/find 55readonly HEAD=/usr/bin/head 56readonly SORT=/usr/bin/sort 57readonly UNAME=/usr/bin/uname 58readonly MACH=`$UNAME -p` 59 60ROOT_ARCHIVE=/usr/sbin/root_archive 61# for gettext 62TEXTDOMAIN=SUNW_OST_OSCMD 63export TEXTDOMAIN 64 65 66function 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 76function 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 100function cleanup 101{ 102 $RM -rf "$TMP_DIR" 103} 104 105 106function 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 124function 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 148function 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 161function 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 183function 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 234function 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 328basedir=/ 329toolsdir=`dirname $0` 330tmpdir=/tmp/`basename $0`.$$ 331trap "/bin/rm -rf $tmpdir" 0 332while getopts "R:" arg 333do 334 case "$arg" in 335 R) basedir=$OPTARG;; 336 esac 337done 338/bin/mkdir -p "$tmpdir" || exit 339tmpfile=$tmpdir/patches 340patchdir=$tmpdir/patchdir 341/bin/mkdir "$patchdir" || exit 342/usr/bin/gzip -c -d "$toolsdir/../Product/patches.gz" > $tmpfile || exit 343cd "$patchdir" 344/bin/cpio -idum < "$tmpfile" || exit 345/usr/sbin/patchadd -R "$basedir" -nu * 346EOF 347 $CHMOD a+rx "$distdir/Tools/install.sh" 348 349} 350 351 352# 353# Main 354# 355trap cleanup EXIT 356 357ISO= 358ISOLABEL= 359MEDIA_ROOT= 360VERBOSE_LEVEL=0 361VERBOSE_OPTS= 362 363while getopts ':d:o:l:v' opt 364do 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 388done 389shift 'OPTIND - 1' 390 391unset PACKAGES PATCHES # reset arrays 392collect_objs "$@" 393 394# If there are no packages or patches, then print info and we're done. 395if (( ${#PACKAGES[*]} == 0 && ${#PATCHES[*]} == 0 )) 396then 397 gettext "No valid package or patch was specified.\nPackages and patches must be unpacked.\n" 398 usage 399 exit 1 400fi 401 402# -d option must be specified 403if [[ -z "$MEDIA_ROOT" ]] 404then 405 gettext "No media root (-d option) was specified.\n" 406 usage 407 exit 1 408fi 409 410check_prereqs # must be called after $ISO is possibly set 411 412# Verify it's a Solaris install media. 413SOLARIS_DIR=$($LS -d $MEDIA_ROOT/Solaris* 2>/dev/null) 414if [[ -z "$SOLARIS_DIR" || ! -d "$SOLARIS_DIR/Tools/Boot" ]] 415then 416 gettext "$MEDIA_ROOT is not valid Solaris install media.\n" 417 exit 1 418fi 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). 425RELEASE=5.${SOLARIS_DIR##*Solaris_} 426VERSION=$(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 433MINIROOT= 434# Relative to a Solaris media root. 435if [ "$MACH" = "sparc" ]; then 436 MINIROOT=$MEDIA_ROOT/boot/sparc.miniroot 437else 438 # x86/x64 439 MINIROOT=$MEDIA_ROOT/boot/x86.miniroot 440fi 441if [ ! -f $MINIROOT ]; then 442 gettext "No boot/x86.miniroot under media root.\n" 443 exit 1 444fi 445 446# Where to unpack the miniroot. 447UNPACKED_ROOT=${TMP_DIR}/miniroot 448 449# Create the ITU directory on the media, if necessary 450ITUDIR=$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. 456typeset -Z3 COUNTDIR 457COUNTDIR=$($LS -d "$ITUDIR"/+([0-9]) 2>/dev/null | $SED 's;.*/;;' | 458 $SORT -rn | $HEAD -1) 459if [[ $COUNTDIR == *( ) ]] 460then 461 COUNTDIR=0 462else 463 (( COUNTDIR += 1 )) 464fi 465 466unpack_media || exit 467add_pkgs && add_patches 468if (( status=$? )) && [[ -s "$LOGFILE" ]] 469then 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 473else 474 $RM -f "$LOGFILE" 475fi 476print 477repack_media || exit 478mkiso 479