Home | History | Annotate | Download | only in scripts
      1 #!/bin/ksh -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 format=ufs
     27 ALT_ROOT=
     28 EXTRACT_ARGS=
     29 compress=yes
     30 SPLIT=unknown
     31 ERROR=0
     32 dirsize32=0
     33 dirsize64=0
     34 
     35 usage() {
     36 	echo "This utility is a component of the bootadm(1M) implementation"
     37 	echo "and it is not recommended for stand-alone use."
     38 	echo "Please use bootadm(1M) instead."
     39 	echo ""
     40 	echo "Usage: ${0##*/}: [-R \<root\>] [-p \<platform\>] [--nocompress]"
     41 	echo "where \<platform\> is one of i86pc, sun4u or sun4v"
     42 	exit
     43 }
     44 
     45 # default platform is what we're running on
     46 PLATFORM=`uname -m`
     47 
     48 #
     49 # set path, but inherit /tmp/bfubin if owned by
     50 # same uid executing this process, which must be root.
     51 #
     52 if [ "`echo $PATH | cut -f 1 -d :`" = /tmp/bfubin ] && \
     53     [ -O /tmp/bfubin ] ; then
     54 	export PATH=/tmp/bfubin
     55 	export GZIP_CMD=/tmp/bfubin/gzip
     56 else
     57 	export PATH=/usr/sbin:/usr/bin:/sbin
     58 	export GZIP_CMD=/usr/bin/gzip
     59 fi
     60 
     61 EXTRACT_FILELIST="/boot/solaris/bin/extract_boot_filelist"
     62 
     63 #
     64 # Parse options
     65 #
     66 while [ "$1" != "" ]
     67 do
     68         case $1 in
     69         -R)	shift
     70 		ALT_ROOT="$1"
     71 		if [ "$ALT_ROOT" != "/" ]; then
     72 			echo "Creating boot_archive for $ALT_ROOT"
     73 			EXTRACT_ARGS="${EXTRACT_ARGS} -R ${ALT_ROOT}"
     74 			EXTRACT_FILELIST="${ALT_ROOT}${EXTRACT_FILELIST}"
     75 		fi
     76 		;;
     77 	-n|--nocompress) compress=no
     78 		;;
     79 	-p)	shift
     80 		PLATFORM="$1"
     81 		EXTRACT_ARGS="${EXTRACT_ARGS} -p ${PLATFORM}"
     82 		;;
     83         *)      usage
     84 		;;
     85         esac
     86 	shift
     87 done
     88 
     89 if [ -x /usr/bin/mkisofs -o -x /tmp/bfubin/mkisofs ] ; then
     90 	format=isofs
     91 fi
     92 
     93 #
     94 # mkisofs on s8 doesn't support functionality used by GRUB boot.
     95 # Use ufs format for boot archive instead.
     96 #
     97 release=`uname -r`
     98 if [ "$release" = "5.8" ]; then
     99 	format=ufs
    100 fi
    101 
    102 shift `expr $OPTIND - 1`
    103 
    104 if [ $# -eq 1 ]; then
    105 	ALT_ROOT="$1"
    106 	echo "Creating boot_archive for $ALT_ROOT"
    107 fi
    108 
    109 case $PLATFORM in
    110 i386)	PLATFORM=i86pc
    111 	ISA=i386
    112 	ARCH64=amd64
    113 	;;
    114 i86pc)	ISA=i386
    115 	ARCH64=amd64
    116 	;;
    117 sun4u)	ISA=sparc
    118 	ARCH64=sparcv9
    119 	;;
    120 sun4v)	ISA=sparc
    121 	ARCH64=sparcv9
    122 	;;
    123 *)	usage
    124 	;;
    125 esac
    126 
    127 BOOT_ARCHIVE=platform/$PLATFORM/boot_archive
    128 BOOT_ARCHIVE_64=platform/$PLATFORM/$ARCH64/boot_archive
    129 
    130 if [ $PLATFORM = i86pc ] ; then
    131 	if [ ! -x "$ALT_ROOT"/boot/solaris/bin/symdef ]; then
    132 		# no dboot implies combined archives for example
    133 		# live-upgrade from s9 to s10u6 is multiboot-only
    134 		echo "Creating single archive at $ALT_ROOT/$BOOT_ARCHIVE"
    135 		SPLIT=no
    136 		compress=no
    137 	else
    138 		SPLIT=yes
    139 	fi
    140 else			# must be sparc
    141 	SPLIT=no	# there's only 64-bit (sparcv9), so don't split
    142 	compress=no	
    143 fi
    144 
    145 [ -x $GZIP_CMD ] || compress=no
    146 
    147 function cleanup
    148 {
    149 	umount -f "$rdmnt32" 2>/dev/null
    150 	umount -f "$rdmnt64" 2>/dev/null
    151 	lofiadm -d "$rdfile32" 2>/dev/null
    152 	lofiadm -d "$rdfile64" 2>/dev/null
    153 	[ -n "$rddir" ] && rm -fr "$rddir" 2> /dev/null
    154 	[ -n "$new_rddir" ] && rm -fr "$new_rddir" 2>/dev/null
    155 }
    156 
    157 function getsize
    158 {
    159 	# Estimate image size and add 10% overhead for ufs stuff.
    160 	# Note, we can't use du here in case we're on a filesystem, e.g. zfs,
    161 	# in which the disk usage is less than the sum of the file sizes.
    162 	# The nawk code 
    163 	#
    164 	#	{t += ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5}
    165 	#
    166 	# below rounds up the size of a file/directory, in bytes, to the
    167 	# next multiple of 1024.  This mimics the behavior of ufs especially
    168 	# with directories.  This results in a total size that's slightly
    169 	# bigger than if du was called on a ufs directory.
    170 	size32=$(cat "$list32" | xargs -I {} ls -lLd "{}" 2> /dev/null |
    171 		nawk '{t += ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5}
    172 		END {print int(t * 1.10 / 1024)}')
    173 	(( size32 += dirsize32 ))
    174 	size64=$(cat "$list64" | xargs -I {} ls -lLd "{}" 2> /dev/null |
    175 		nawk '{t += ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5}
    176 		END {print int(t * 1.10 / 1024)}')
    177 	(( size64 += dirsize64 ))
    178 	(( total_size = size32 + size64 ))
    179 
    180 	if [ $compress = yes ] ; then
    181 		total_size=`echo $total_size | nawk '{print int($1 / 2)}'`
    182 	fi
    183 }
    184 
    185 #
    186 # Copies all desired files to a target directory.  One argument should be
    187 # passed: the file containing the list of files to copy.  This function also
    188 # depends on several variables that must be set before calling:
    189 #
    190 # $ALT_ROOT - the target directory
    191 # $compress - whether or not the files in the archives should be compressed
    192 # $rdmnt - the target directory
    193 #
    194 function copy_files
    195 {
    196 	list="$1"
    197 
    198 	#
    199 	# If compress is set, the files are gzip'd and put in the correct
    200 	# location in the loop.  Nothing is printed, so the pipe and cpio
    201 	# at the end is a nop.
    202 	#
    203 	# If compress is not set, the file names are printed, which causes
    204 	# the cpio at the end to do the copy.
    205 	#
    206 	while read path
    207 	do
    208 		if [ $compress = yes ]; then
    209 			dir="${path%/*}"
    210 			[ -d "$rdmnt/$dir" ] || mkdir -p "$rdmnt/$dir"
    211 			$GZIP_CMD -c "$path" > "$rdmnt/$path"
    212 		else
    213 			print "$path"
    214 		fi
    215 	done <"$list" | cpio -pdum "$rdmnt" 2>/dev/null
    216 
    217 	if [ $ISA = sparc ] ; then
    218 		# copy links
    219 		find $filelist -type l -print 2>/dev/null |\
    220 		    cpio -pdum "$rdmnt" 2>/dev/null
    221 		if [ $compress = yes ] ; then
    222 			# always copy unix uncompressed
    223 			find $filelist -name unix -type f -print 2>/dev/null |\
    224 			    cpio -pdum "$rdmnt" 2>/dev/null
    225 		fi
    226 	fi
    227 
    228 }
    229 
    230 #
    231 # The first argument can be:
    232 #
    233 # "both" - create an archive with both 32-bit and 64-bit binaries
    234 # "32-bit" - create an archive with only 32-bit binaries
    235 # "64-bit" - create an archive with only 64-bit binaries
    236 #
    237 function create_ufs
    238 {
    239 	which=$1
    240 	archive=$2
    241 	lofidev=$3
    242 
    243 	# should we exclude amd64 binaries?
    244 	if [ "$which" = "32-bit" ]; then
    245 		rdfile="$rdfile32"
    246 		rdmnt="$rdmnt32"
    247 		list="$list32"
    248 	elif [ "$which" = "64-bit" ]; then
    249 		rdfile="$rdfile64"
    250 		rdmnt="$rdmnt64"
    251 		list="$list64"
    252 	else
    253 		rdfile="$rdfile32"
    254 		rdmnt="$rdmnt32"
    255 		list="$list32"
    256 	fi
    257 
    258 	newfs $lofidev < /dev/null 2> /dev/null
    259 	mkdir "$rdmnt"
    260 	mount -F mntfs mnttab /etc/mnttab > /dev/null 2>&1
    261 	mount -F ufs -o nologging $lofidev "$rdmnt"
    262 	files=
    263 
    264 	# do the actual copy
    265 	copy_files "$list"
    266 	umount -f "$rdmnt"
    267 	rmdir "$rdmnt"
    268 
    269 	if [ $ISA = sparc ] ; then
    270 		rlofidev=`echo "$lofidev" | sed -e "s/dev\/lofi/dev\/rlofi/"`
    271 		bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/ufs/bootblk"
    272 		# installboot is not available on all platforms
    273 		dd if=$bb of=$rlofidev bs=1b oseek=1 count=15 conv=sync 2>&1
    274 	fi
    275 
    276 	#
    277 	# Check if gzip exists in /usr/bin, so we only try to run gzip
    278 	# on systems that have gzip. Then run gzip out of the patch to
    279 	# pick it up from bfubin or something like that if needed.
    280 	#
    281 	# If compress is set, the individual files in the archive are
    282 	# compressed, and the final compression will accomplish very
    283 	# little.  To save time, we skip the gzip in this case.
    284 	#
    285 	if [ $ISA = i386 ] && [ $compress = no ] && \
    286 	    [ -x $GZIP_CMD ] ; then
    287 		gzip -c "$rdfile" > "${archive}-new"
    288 	else
    289 		cat "$rdfile" > "${archive}-new"
    290 	fi
    291 	
    292 	if [ $? -ne 0 ] ; then
    293 		rm -f "${archive}-new"
    294 	fi
    295 }
    296 
    297 #
    298 # The first argument can be:
    299 #
    300 # "both" - create an archive with both 32-bit and 64-bit binaries
    301 # "32-bit" - create an archive with only 32-bit binaries
    302 # "64-bit" - create an archive with only 64-bit binaries
    303 #
    304 function create_isofs
    305 {
    306 	which=$1
    307 	archive=$2
    308 
    309 	# should we exclude amd64 binaries?
    310 	if [ "$which" = "32-bit" ]; then
    311 		rdmnt="$rdmnt32"
    312 		errlog="$errlog32"
    313 		list="$list32"
    314 	elif [ "$which" = "64-bit" ]; then
    315 		rdmnt="$rdmnt64"
    316 		errlog="$errlog64"
    317 		list="$list64"
    318 	else
    319 		rdmnt="$rdmnt32"
    320 		errlog="$errlog32"
    321 		list="$list32"
    322 	fi
    323 
    324 	# create image directory seed with graft points
    325 	mkdir "$rdmnt"
    326 	files=
    327 	isocmd="mkisofs -quiet -graft-points -dlrDJN -relaxed-filenames"
    328 
    329 	if [ $ISA = sparc ] ; then
    330 		bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/hsfs/bootblk"
    331 		isocmd="$isocmd -G \"$bb\""
    332 	fi
    333 
    334 	copy_files "$list"
    335 	isocmd="$isocmd \"$rdmnt\""
    336 	rm -f "$errlog"
    337 
    338 	#
    339 	# Check if gzip exists in /usr/bin, so we only try to run gzip
    340 	# on systems that have gzip. Then run gzip out of the patch to
    341 	# pick it up from bfubin or something like that if needed.
    342 	#
    343 	# If compress is set, the individual files in the archive are
    344 	# compressed, and the final compression will accomplish very
    345 	# little.  To save time, we skip the gzip in this case.
    346 	#
    347 	mkiso_ret=0
    348 
    349 	if [ $ISA = i386 ] &&[ $compress = no ] && [ -x $GZIP_CMD ]
    350 	then
    351 		ksh -c "$isocmd" 2> "$errlog" | \
    352 		    gzip > "${archive}-new"
    353 	else
    354 		ksh -c "$isocmd" 2> "$errlog" > "${archive}-new"
    355 	fi
    356 
    357 	if [ $? -ne 0 ]; then
    358 		cat "$errlog"
    359 		rm -f "${archive}-new" 2> /dev/null
    360 		rm -f "$errlog" 2> /dev/null
    361 		return
    362 	fi
    363 
    364 	dd_ret=0
    365 	if [ $ISA = sparc ] ; then
    366 		bb="$ALT_ROOT/platform/$PLATFORM/lib/fs/hsfs/bootblk"
    367 		dd if="$bb" of="${archive}-new" bs=1b oseek=1 count=15 \
    368 		    conv=notrunc conv=sync >> "$errlog" 2>&1
    369 		dd_ret=$?
    370 	fi
    371 
    372 	if [ -s "$errlog" ] || [ $dd_ret -ne 0 ] ; then
    373 		grep Error: "$errlog" >/dev/null 2>&1
    374 		if [ $? -eq 0 ] || [ $dd_ret -ne 0 ] ; then
    375 			cat "$errlog"
    376 			rm -f "${archive}-new"
    377 		fi
    378 	fi
    379 	rm -f "$errlog"
    380 }
    381 
    382 function create_archive
    383 {
    384 	which=$1
    385 	archive=$2
    386 	lofidev=$3
    387 
    388 	echo "updating $archive"
    389 
    390 	if [ "$format" = "ufs" ]; then
    391 		create_ufs "$which" "$archive" "$lofidev"
    392 	else
    393 		create_isofs "$which" "$archive"
    394 	fi
    395 
    396 	# sanity check the archive before moving it into place
    397 	#
    398 	ARCHIVE_SIZE=`ls -l "${archive}-new" 2> /dev/null | nawk '{ print $5 }'`
    399 	if [ $compress = yes ] || [ $ISA = sparc ] ; then
    400 		#
    401 		# 'file' will report "English text" for uncompressed
    402 		# boot_archives.  Checking for that doesn't seem stable,
    403 		# so we just check that the file exists.
    404 		#
    405 		ls "${archive}-new" >/dev/null 2>&1
    406 	else
    407 		#
    408 		# the file type check also establishes that the
    409 		# file exists at all
    410 		#
    411 		LC_MESSAGES=C file "${archive}-new" | grep gzip > /dev/null
    412 	fi
    413 
    414 	if [ $? = 1 ] && [ -x $GZIP_CMD ] || [ "$ARCHIVE_SIZE" -lt 10000 ]
    415 	then
    416 		#
    417 		# Two of these functions may be run in parallel.  We
    418 		# need to allow the other to clean up, so we can't
    419 		# exit immediately.  Instead, we set a flag.
    420 		#
    421 		echo "update of $archive failed"
    422 		ERROR=1
    423 	else
    424 		lockfs -f "/$ALT_ROOT" 2>/dev/null
    425 		mv "${archive}-new" "$archive"
    426 		lockfs -f "/$ALT_ROOT" 2>/dev/null
    427 	fi
    428 
    429 }
    430 
    431 function fatal_error
    432 {
    433 	print -u2 $*
    434 	exit 1
    435 }
    436 
    437 #
    438 # get filelist
    439 #
    440 if [ ! -f "$ALT_ROOT/boot/solaris/filelist.ramdisk" ] &&
    441     [ ! -f "$ALT_ROOT/etc/boot/solaris/filelist.ramdisk" ]
    442 then
    443 	print -u2 "Can't find filelist.ramdisk"
    444 	exit 1
    445 fi
    446 filelist=$($EXTRACT_FILELIST $EXTRACT_ARGS \
    447 	/boot/solaris/filelist.ramdisk \
    448 	/etc/boot/solaris/filelist.ramdisk \
    449 		2>/dev/null | sort -u)
    450 
    451 #
    452 # We use /tmp/ for scratch space now.  This may be changed later if there
    453 # is insufficient space in /tmp/.
    454 #
    455 rddir="/tmp/create_ramdisk.$$.tmp"
    456 new_rddir=
    457 rm -rf "$rddir"
    458 mkdir "$rddir" || fatal_error "Could not create temporary directory $rddir"
    459 
    460 # Clean up upon exit.
    461 trap 'cleanup' EXIT
    462 
    463 list32="$rddir/filelist.32"
    464 list64="$rddir/filelist.64"
    465 
    466 touch $list32 $list64
    467 
    468 #
    469 # This loop creates the 32-bit and 64-bit lists of files.  The 32-bit list
    470 # is written to stdout, which is redirected at the end of the loop.  The
    471 # 64-bit list is appended with each write.
    472 #
    473 cd "/$ALT_ROOT"
    474 find $filelist -print 2>/dev/null | while read path
    475 do
    476 	if [ $SPLIT = no ]; then
    477 		print "$path"
    478 	elif [ -d "$path" ]; then
    479 		if [ $format = ufs ]; then
    480 			size=`ls -lLd "$path" | nawk '
    481 			    {print ($5 % 1024) ? (int($5 / 1024) + 1) * 1024 : $5}'`
    482 			if [ `basename "$path"` != "amd64" ]; then
    483 				(( dirsize32 += size ))
    484 			fi
    485 			(( dirsize64 += size ))
    486 		fi
    487 	else
    488 		case `LC_MESSAGES=C /usr/bin/file -m /dev/null "$path" 2>/dev/null` in
    489 		*ELF\ 64-bit*)
    490 			print "$path" >> "$list64"
    491 			;;
    492 		*ELF\ 32-bit*)
    493 			print "$path"
    494 			;;
    495 		*)
    496 			# put in both lists
    497 			print "$path"
    498 			print "$path" >> "$list64"
    499 		esac
    500 	fi
    501 done >"$list32"
    502 
    503 if [ $format = ufs ] ; then
    504 	# calculate image size
    505 	getsize
    506 
    507 	# check to see if there is sufficient space in tmpfs 
    508 	#
    509 	tmp_free=`df -b /tmp | tail -1 | awk '{ printf ($2) }'`
    510 	(( tmp_free = tmp_free / 3 ))
    511 	if [ $SPLIT = yes ]; then
    512 		(( tmp_free = tmp_free / 2 ))
    513 	fi
    514 
    515 	if [ $total_size -gt $tmp_free  ] ; then
    516 		# assumes we have enough scratch space on $ALT_ROOT
    517 		new_rddir="/$ALT_ROOT/var/tmp/create_ramdisk.$$.tmp"
    518 		rm -rf "$new_rddir"
    519 		mkdir "$new_rddir" || fatal_error \
    520 		    "Could not create temporary directory $new_rddir"
    521 
    522 		# Save the file lists
    523 		mv "$list32" "$new_rddir"/
    524 		mv "$list64" "$new_rddir"/
    525 		list32="/$new_rddir/filelist.32"
    526 		list64="/$new_rddir/filelist.64"
    527 
    528 		# Remove the old $rddir and set the new value of rddir
    529 		rm -rf "$rddir"
    530 		rddir="$new_rddir"
    531 		new_rddir=
    532 	fi
    533 fi
    534 
    535 rdfile32="$rddir/rd.file.32"
    536 rdfile64="$rddir/rd.file.64"
    537 rdmnt32="$rddir/rd.mount.32"
    538 rdmnt64="$rddir/rd.mount.64"
    539 errlog32="$rddir/rd.errlog.32"
    540 errlog64="$rddir/rd.errlog.64"
    541 lofidev32=""
    542 lofidev64=""
    543 
    544 if [ $SPLIT = yes ]; then
    545 	#
    546 	# We can't run lofiadm commands in parallel, so we have to do
    547 	# them here.
    548 	#
    549 	if [ "$format" = "ufs" ]; then
    550 		mkfile ${size32}k "$rdfile32"
    551 		lofidev32=`lofiadm -a "$rdfile32"`
    552 		mkfile ${size64}k "$rdfile64"
    553 		lofidev64=`lofiadm -a "$rdfile64"`
    554 	fi
    555 	create_archive "32-bit" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32 &
    556 	create_archive "64-bit" "$ALT_ROOT/$BOOT_ARCHIVE_64" $lofidev64
    557 	wait
    558 	if [ "$format" = "ufs" ]; then
    559 		lofiadm -d "$rdfile32"
    560 		lofiadm -d "$rdfile64"
    561 	fi
    562 else
    563 	if [ "$format" = "ufs" ]; then
    564 		mkfile ${total_size}k "$rdfile32"
    565 		lofidev32=`lofiadm -a "$rdfile32"`
    566 	fi
    567 	create_archive "both" "$ALT_ROOT/$BOOT_ARCHIVE" $lofidev32
    568 	[ "$format" = "ufs" ] && lofiadm -d "$rdfile32"
    569 fi
    570 if [ $ERROR = 1 ]; then
    571 	cleanup
    572 	exit 1
    573 fi
    574 
    575 #
    576 # For the diskless case, hardlink archive to /boot to make it
    577 # visible via tftp. /boot is lofs mounted under /tftpboot/<hostname>.
    578 # NOTE: this script must work on both client and server.
    579 #
    580 grep "[	 ]/[	 ]*nfs[	 ]" "$ALT_ROOT/etc/vfstab" > /dev/null
    581 if [ $? = 0 ]; then
    582 	rm -f "$ALT_ROOT/boot/boot_archive" "$ALT_ROOT/boot/amd64/boot_archive"
    583 	ln "$ALT_ROOT/$BOOT_ARCHIVE" "$ALT_ROOT/boot/boot_archive"
    584 	if [ $SPLIT = yes ]; then
    585 		ln "$ALT_ROOT/$BOOT_ARCHIVE_64" \
    586 		    "$ALT_ROOT/boot/amd64/boot_archive"
    587 	fi
    588 fi
    589 [ -n "$rddir" ] && rm -rf "$rddir"
    590