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