#!/bin/ksh # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #pragma ident "@(#)install-recovery.sh 1.3 08/06/25 SMI" # # This script looks for installed Solaris instances to facilitate # user recovery of corrupt rootfs # validate_ckyorn() { if [ $1 = y ] || [ $1 = Y ] || [ $1 = yes ] || [ $1 = Yes ] || \ [ $1 = yEs ] || [ $1 = YEs ] || [ $1 = yeS ] || [ $1 = YeS ] || \ [ $1 = yES ] || [ $1 = YES ] ; then echo y else echo n fi } update_archive() { dev=$1 fstype=$2 printf "\nAn out of sync boot archive was detected on $dev.\n" printf "The boot archive is a cache of files used during boot and\n" printf "should be kept in sync to ensure proper system" printf " operation.\n" help="This will fsck and mount, if needed, and update the boot archive" prompt="Do you wish to automatically update this boot archive?" ans=`/usr/bin/ckyorn -Q -p "${prompt}" -h "${help}"` ans=`validate_ckyorn $ans` if [ "$ans" = "y" ] ; then if [ "X${fstype}" = Xufs ]; then umount /a 2> /dev/null fsck_and_mount $dev fi echo Updating boot archive on $dev. bootadm update-archive -R /a > /dev/null # If bootadm fails, it's pretty verbose on stderr. If it # succeeds, it is silent. In this case however, clear # positive indication of success is desirable: if [ $? = 0 ] ; then echo The boot archive on $dev was updated successfully. fi if [ "X${fstype}" = Xufs ]; then umount /a fi fi } update_md_msg() { printf "To manually recover the boot archive on a root mirror," printf "mount the first\nside (the one that the system boots from)" printf " and run:\n\n\tbootadm update-archive -R " printf "\n\n" } find_OS_roots() { # # Find any bootable zfs pools # /usr/sbin/install.d/rootdev -z | while read pool bootfs guid junk do OS_found=`expr $OS_found + 1` echo $bootfs >> $rel_list OS_rootdevs="$OS_rootdevs $pool:$guid:zfs" done # # now find ufs roots # for Dev in /dev/dsk/*s[0-7]; do Typ=`/usr/sbin/fstyp $Dev 2> /dev/null` if [ "X${Typ}" = Xufs ]; then /sbin/mount -o ro -F ufs $Dev /a status=$? if [ ${status} != 0 ]; then echo "Failed to mount $Dev read-only: skipping." continue; fi if [ -f /a/etc/release ]; then /sbin/bootadm update-archive -n -R /a \ > /dev/null 2>&1 if [ $? = 0 ] ; then ARCHIVE=synced else ARCHIVE=faulted fi VFSENT=`grep "[ ]/[ ]" /a/etc/vfstab` # Check if this root is under md control. # If it is, leave it alone. MD=`echo $VFSENT | grep "^/dev/md"` if [ -n "$MD" ] ; then echo $Dev is under md control, skipping. if [ $ARCHIVE = faulted ] ; then update_md_msg fi umount -f /a continue; fi # sort out root fs' that don't use a matching # slice in their vfstab. This amongst other # things catches s2 (overlap). # VSLICE=`echo $VFSENT | cut -f 4 -d / |\ cut -f 2 -d s | awk '{ print $1 }'` DSLICE=`echo $Dev | cut -f 4 -d / |\ cut -f 2 -d s | awk '{ print $1 }'` if [ "$VSLICE" != "$DSLICE" ] ; then umount -f /a continue fi OS_found=`expr $OS_found + 1` rel=`sed -n '1s/^ *//p' /a/etc/release` if [ $ARCHIVE = faulted ] ; then update_archive $Dev ufs fi echo $rel >> $rel_list OS_rootdevs="$OS_rootdevs $Dev:ufs" fi umount /a 2> /dev/null fi done } fsck_and_mount() { dsk=$1 rdsk=`echo $dsk | sed "s#/dsk/#/rdsk/#"` fsck -m -F ufs $rdsk > /dev/null 2>&1 if [ $? != 0 ] ; then fsck -F ufs $rdsk fi umount /a 2> /dev/null mount $dsk /a } # # This function will import the root pool under /a and then mount the # the appropriate root filesystem as specified by the 'bootfs' property. # Since the root filesystem is a zfs mount which has the 'canmount=noauto' # property set, it will not be automatically mounted as part of the import. # As a result, we mount the root filesystem and then remount any filesystems # which failed to mount during the import phase. # import_and_mount() { pool=${1%%:*} guid=${1##*:} # # We try to import the pool and ignore the return status # since many file systems in the pool will probably fail # to mount. We ensure the import succeeded by running # the 'zpool list ' command. # zpool import -f -R /a $guid > /dev/null 2>&1 zpool list $pool > /dev/null 2>&1 if [ $? != 0 ]; then echo "import of $pool failed" return fi zpool get bootfs $pool | tail -1 | read name prop bootfs source zfs mount $bootfs if [ $? != 0 ]; then echo "Unable to mount $bootfs as root" return fi # # Now that the root is mounted we try to remount all remaining # zfs filesystems. # zfs mount -a > /dev/null 2>&1 if [ -f /a/etc/release ]; then /sbin/bootadm update-archive -n -R /a > /dev/null 2>&1 if [ $? != 0 ] ; then update_archive $pool zfs fi fi } OS_found=0 OS_rootdevs= rel_list=/tmp/rel_list.$$ mnthelp="Check and mount the selected root device read-write." mntprompt="Do you wish to have it mounted read-write on /a?" pkill dial echo "Searching for installed OS instances..." find_OS_roots if [ $OS_found -eq 0 ]; then echo "No installed OS instance found." elif [ $OS_found -eq 1 ]; then dev=`echo ${OS_rootdevs%%:*}` fstype=`echo ${OS_rootdevs##*:}` rel=`cat $rel_list` printf "\n$rel was found on $dev." ans=`/usr/bin/ckyorn -Q -p "${mntprompt}" -h "${mnthelp}"` ans=`validate_ckyorn $ans` if [ "$ans" = "y" ] ; then echo "mounting $dev on /a" if [ "X${fstype}" = Xufs ]; then fsck_and_mount $dev elif [ "X${fstype}" = Xzfs ]; then import_and_mount ${OS_rootdevs%:*} fi fi else printf "\nMultiple OS instances were found. To check and mount " printf "one of them\nread-write under /a, select it from the " printf "following list. To not mount\nany, select 'q'.\n" item=1 itemlist=/tmp/itemlist_$$ rm -f $itemlist for dev in $OS_rootdevs ; do rel=`head -$item $rel_list | tail -1` dev=`echo ${dev%:*}` printf "$dev\t$rel\n" >> $itemlist item=`expr $item + 1` done prompt="Please select a device to be mounted (q for none)" dev=`/usr/bin/ckitem -p "${prompt}" -h "${mnthelp}" -f $itemlist` if [ "$dev" != "q" ] ; then echo "mounting ${dev:%%:*} on /a" disk=$(echo $dev | cut -b1-9) if [ "X${disk}" = X/dev/dsk/ ];then fsck_and_mount $dev else import_and_mount $dev fi break fi rm -f $itemlist fi rm -f $rel_list printf "\nStarting shell.\n" /sbin/sulogin