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