3 ###############################################################################
5 ## elilo installs efi bootloader onto a bootstrap partition (based on ybin)
6 ## Copyright (C) 2001 Ethan Benson
8 ## This program is free software; you can redistribute it and/or
9 ## modify it under the terms of the GNU General Public License
10 ## as published by the Free Software Foundation; either version 2
11 ## of the License, or (at your option) any later version.
13 ## This program is distributed in the hope that it will be useful,
14 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ## GNU General Public License for more details.
18 ## You should have received a copy of the GNU General Public License
19 ## along with this program; if not, write to the Free Software
20 ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 ###############################################################################
24 PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
25 ## allow to run out of /target in boot-floppies
26 if [ -n "$PATH_PREFIX" ] ; then
27 PATH="${PATH}:${PATH_PREFIX}/sbin:${PATH_PREFIX}/bin:${PATH_PREFIX}/usr/sbin:${PATH_PREFIX}/usr/bin:${PATH_PREFIX}/usr/local/sbin:${PATH_PREFIX}/usr/local/bin"
30 SIGINT="$PRG: Interrupt caught ... exiting"
36 # Beware, /EFI/debian occurs with double backslashes in the script too
37 # so change those as well as EFIROOT, if need be.
41 ARCHITECTURE=$(dpkg --print-installation-architecture)
43 ## catch signals, clean up junk in /tmp.
45 trap "cleanup; exit 129" HUP
46 trap "echo 1>&2 $SIGINT ; cleanup; exit 130" INT
47 trap "cleanup; exit 131" QUIT
48 trap "cleanup; exit 143" TERM
50 ## define default config file
53 ERR=" Error in $CONF:"
55 ## define default configuration
58 ## allow default to work on packaged and non-packaged elilo.
59 if [ -f /usr/local/lib/elilo/elilo.efi ] ; then
60 install=/usr/local/lib/elilo/elilo.efi
61 elif [ -f /usr/lib/elilo/elilo.efi ] ; then
62 install=/usr/lib/elilo/elilo.efi
71 ## elilo autoconf defaults
76 # image default is controlled by /etc/kernel-img.conf, if it exists
77 if [ -f /etc/kernel-img.conf ] &&
78 grep -E -qi "^(image|link)_in_boot *= *yes" /etc/kernel-img.conf; then
80 initrdline=initrd=/boot/initrd.img
81 initrdoldline=initrd=/boot/initrd.img.old
84 initrdline=initrd=/initrd.img
85 initrdoldline=initrd=/initrd.img.old
87 if [ -f /etc/kernel-img.conf ] &&
88 ! grep -qi "^do_initrd *= *yes" /etc/kernel-img.conf; then
93 ## make fake `id' if its missing, outputs 0 since if its missing we
94 ## are probably running on boot floppies and thus are root.
95 if (command -v id > /dev/null 2>&1) ; then
109 Written by Richard Hirst, based on work by Ethan Benson
111 This is free software; see the source for copying conditions. There is NO
112 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
119 "Usage: $PRG [OPTION]...
120 Update/install bootloader onto a bootstrap partition.
122 -b, --boot set bootstrap partition device [ -b /dev/sda1 ]
123 -i, --install pathname to the actual bootloader binary
124 default: /usr/{local/}lib/elilo/elilo.efi
125 -C, --config use alternate configuration file [ -C config_file ]
126 --autoconf auto-generate a /etc/elilo.conf
127 --efiboot elilo auto configuration: create an efi boot
128 manager entry for elilo
129 --timeout elilo auto configuration: sets the time elilo
130 will wait for user input before booting default
131 image default: 20 (2 seconds)
132 --image elilo auto configuration: sets the path to the
133 kernel image. default: /vmlinuz
134 --label elilo auto configuration: sets the image label
136 --root elilo auto configuration: sets the root device
138 --format create a new FAT filesystem on the boot partition
139 -v, --verbose make $PRG more verbose
140 --debug print boring junk only useful for debugging
141 -h, --help display this help and exit
142 -V, --version output version information and exit"
145 ## we have to do some things differently with a retarded devfs name.
149 /dev/ide/*|/dev/scsi/*|/dev/discs/*)
158 ## the SmartArray RAID controllers use /dev/cciss/c0d0p1 kinds of names...
172 ## configuration file parsing. FIXME: need a method which can parse
178 v=`grep "^$2[\ ,=]" "$CONF"` ; echo "${v#*=}"
181 grep "^$2\>" "$CONF" > /dev/null && echo 0 || echo 1
184 grep "^$2[\ ,=]" "$CONF" > /dev/null && echo 0 || echo 1
189 ## check for existence of a configuration file, and make sure we have
193 if [ ! -e "$CONF" ] ; then
194 echo 1>&2 "$PRG: $CONF: No such file or directory"
196 elif [ ! -f "$CONF" ] ; then
197 echo 1>&2 "$PRG: $CONF: Not a regular file"
199 elif [ ! -r "$CONF" ] ; then
200 echo 1>&2 "$PRG: $CONF: Permission denied"
207 ## check to make sure the configuration file is sane and correct.
208 ## maybe this is an insane ammount of error checking, but I want to
209 ## make sure (hopefully) nothing unexpected ever happens. and i just
210 ## like useful errors from programs. every error just marks an error
211 ## variable so we give the user as much info as possible before we
215 if [ -L "$boot" ] ; then
217 boot=$(readlink -f $oldboot)
218 echo 1>&2 "$PRG: $oldboot is a symbolic link, using $boot instead"
220 if [ ! -e "$boot" ] ; then
221 echo 1>&2 "$PRG: $boot: No such file or directory"
223 elif [ ! -b "$boot" ] && [ ! -f "$boot" ] ; then
224 echo 1>&2 "$PRG: $boot: Not a regular file or block device"
226 elif [ ! -w "$boot" ] || [ ! -r "$boot" ] ; then
227 echo 1>&2 "$PRG: $boot: Permission denied"
231 ## sanity check, make sure boot=bootstrap and not something dumb
235 echo 1>&2 "$PRG:$ERR \`boot=$boot' would result in the destruction of all data on $boot"
239 echo 1>&2 "$PRG:$ERR \`boot=$boot' would result in the destruction of all data on $boot"
243 echo 1>&2 "$PRG:$ERR \`boot=$boot' would result in the destruction of all data on $boot"
248 ## now make sure its not something dumb like the root partition
249 ROOT="$(v=`df / 2> /dev/null | grep ^/dev/` ; echo ${v%%[ ]*})"
250 BOOT="$(v=`df /boot 2> /dev/null | grep ^/dev/` ; echo ${v%%[ ]*})"
251 if [ "$boot" = "$ROOT" ] ; then
252 echo 1>&2 "$PRG:$ERR \`boot=$boot' would result in the destruction of the root filesystem"
254 elif [ "$boot" = "$BOOT" ] ; then
255 echo 1>&2 "$PRG:$ERR \`boot=$boot' would result in the destruction of the /boot filesystem"
259 ## Make sure boot is not already mounted
260 mount | grep "^$boot " > /dev/null
262 echo 1>&2 "$PRG: $boot appears to be mounted"
266 if [ ! -e "$install" ] ; then
267 echo 1>&2 "$PRG: $install: No such file or directory"
269 elif [ ! -f "$install" ] ; then
270 echo 1>&2 "$PRG: $install: Not a regular file"
272 elif [ ! -r "$install" ] ; then
273 echo 1>&2 "$PRG: $install: Permission denied"
277 if [ ! -e "$bootconf" ] ; then
278 echo 1>&2 "$PRG: $bootconf: No such file or directory"
280 elif [ ! -f "$bootconf" ] ; then
281 echo 1>&2 "$PRG: $bootconf: Not a regular file"
283 elif [ ! -r "$bootconf" ] ; then
284 echo 1>&2 "$PRG: $bootconf: Permission denied"
288 # efibootmgr needs efivars, make sure kernel module is loaded
289 if modprobe -q efivars ; then
290 echo "Loaded efivars kernel module to enable use of efibootmgr"
293 if [ ! -d /proc/efi/vars ] && [ ! -d /sys/firmware/efi/vars ] && [ "$efiboot" = 1 ] ; then
294 echo 1>&2 "$PRG: no efi/vars under /proc or /sys/firmware, boot menu not updated"
298 if [ "$efiboot" = 1 ] ; then
299 ## see if efibootmgr exists and is executable
300 if (command -v efibootmgr > /dev/null 2>&1) ; then
301 [ -x `command -v efibootmgr` ] || MISSING=1 ; else MISSING=1
304 if [ "$MISSING" = 1 ] ; then
306 echo 1>&2 "$PRG: Warning: \`efibootmgr' could not be found, boot menu not updated"
309 if [ -f "$boot" ] ; then
310 echo 1>&2 "$PRG: $boot is a regular file, disabling boot menu update"
315 if [ "$CONFERR" = 1 ] ; then
325 ## we can even create bootstrap filesystem images directly if you
327 if [ -f "$boot" ] ; then
331 if [ -e "$TMP/bootstrap.$$" ] ; then
332 echo 1>&2 "$PRG: $TMP/bootstrap.$$ exists, aborting."
336 mkdir -m 700 "$TMP/bootstrap.$$"
337 if [ $? != 0 ] ; then
338 echo 1>&2 "$PRG: Could not create mountpoint directory, aborting."
342 mount | grep "^$boot " > /dev/null
344 echo 1>&2 "$PRG: $boot appears to be mounted! aborting."
348 [ "$VERBOSE" = 1 ] && echo "$PRG: Mounting $boot..."
349 mount -t "$fstype" -o codepage=437,iocharset=iso8859-1,rw,noexec,umask=077$loop "$boot" "$TMP/bootstrap.$$"
350 if [ $? != 0 ] ; then
351 echo 1>&2 "$PRG: An error occured mounting $boot"
355 TARGET="$TMP/bootstrap.$$"
363 imagefiles=`grep '^image[[:space:]]*=' $bootconf | \
364 sed 's/^image[[:space:]]*=[[:space:]]*//' | grep -v ':'`
365 initrdfiles=`grep '^[[:space:]]*initrd[[:space:]]*=' $bootconf | \
366 sed 's/.*=[[:space:]]*//' | grep -v ':'`
367 vmmfiles=`grep '^[[:space:]]*vmm[[:space:]]*=' $bootconf | \
368 sed 's/.*=[[:space:]]*//' | grep -v ':'`
370 ## Point of no return, removing the old EFI/debian tree
371 rm -rf $TARGET/$EFIROOT
373 echo 2>&1 "$PRG: Failed to delete old boot files, aborting"
376 mkdir -p $TARGET/$EFIROOT
378 ## Add a README to warn that this tree is deleted every time elilo is run
380 This directory tree is managed by /usr/sbin/elilo, and is deleted and\n\
381 recreated every time elilo runs. Any local changes will be lost.\n\
382 " > $TARGET/$EFIROOT/README.TXT
384 ## this is probably insecure on modern filesystems, but i think
385 ## safe on crippled hfs/dosfs.
386 [ "$VERBOSE" = 1 ] && echo "$PRG: Installing primary bootstrap $install onto $boot..."
387 cp -f "$install" "$TARGET/$EFIROOT/$BTFILE"
388 if [ $? != 0 ] ; then
389 echo 1>&2 "$PRG: An error occured while writing to $boot"
393 [ "$VERBOSE" = 1 ] && echo "$PRG: Installing $bootconf on $boot..."
394 ## we comment out boot= and install=, because they are only really
395 ## needed in the /etc/elilo.conf file, and elilo.efi currently
396 ## doesn't understand them. We also need to add /EFI/debian on to
397 ## the front of any paths that don't contain colons (device paths),
398 ## and replace tabs with spaces.
399 sed -e "s|^boot[[:space:]]*=|# &|" -e "s|^install[[:space:]]*=|# &|" \
401 -e "s|\(^image[[:space:]]*=[[:space:]]*\)\([^:]*\)$|\1$EFIROOT\2|" \
402 -e "s|\(^[[:space:]]*initrd[[:space:]]*=[[:space:]]*\)\([^:]*\)$|\1$EFIROOT\2|" \
403 -e "s|\(^[[:space:]]*vmm[[:space:]]*=[[:space:]]*\)\([^:]*\)$|\1$EFIROOT\2|" \
404 < "$bootconf" > "$TARGET/$EFIROOT/$CFFILE"
405 if [ $? != 0 ] ; then
406 echo 1>&2 "$PRG: An error occured while writing to $boot"
410 [ "$DEBUG" = 1 ] && echo "----" && cat "$TARGET/$EFIROOT/$CFFILE" && echo "----"
412 for i in $imagefiles $initrdfiles $vmmfiles; do
413 [ "$VERBOSE" = 1 ] && echo "$PRG: Installing $i on $boot..."
415 mkdir -p `dirname "$TARGET/$EFIROOT/$i"`
416 if [ $? != 0 ] ; then
417 echo 1>&2 "$PRG: An error occured creating directory `dirname $EFIROOT/$i` on $boot"
420 cp -f "$i" "$TARGET/$EFIROOT/$i"
421 if [ $? != 0 ] ; then
422 echo 1>&2 "$PRG: An error occured writing $i to $boot"
426 echo "$PRG: Warning: $i not found"
432 ## update the boot-device variable in EFI.
433 if [ "$efiboot" = 1 ] ; then
434 [ "$VERBOSE" = 1 ] && echo "$PRG: Updating EFI boot-device variable..."
436 [ "$VERBOSE" = 1 ] && efiquiet=""
437 if ckdevfs "$boot" ; then
438 BOOTDISK="${boot%/*}/disc"
439 BOOTPART="${boot##*part}"
440 elif ckcciss "$boot" ; then
441 BOOTDISK="${boot%p[0-9]*}"
442 BOOTPART="${boot##*[a-z]}"
444 BOOTDISK="${boot%%[0-9]*}"
445 BOOTPART="${boot##*[a-z]}"
447 if [ -z "$BOOTDISK" ] || [ -z "$BOOTPART" ] ; then
448 echo 2>&1 "$PRG: Could not determine boot disk, aborting..."
452 [ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: boot-disk = $BOOTDISK"
453 [ "$DEBUG" = 1 ] && echo 1>&2 "$PRG: DEBUG: boot-partition = $BOOTPART"
454 # delete other entries with name "Debian GNU/Linux"
455 for b in `efibootmgr | grep "Debian GNU/Linux" | awk '{print substr($1,5,4) }'`; do
456 efibootmgr $efiquiet -b $b -B
458 # Add a new entry for this installation
459 efibootmgr $efiquiet -c -d $BOOTDISK -p $BOOTPART -w -L "Debian GNU/Linux" \
460 -l \\EFI\\debian\\elilo.efi -u -- elilo -C \\EFI\\debian\\elilo.conf
461 if [ $? != 0 ] ; then
462 echo 1>&2 "$PRG: An error occured while updating boot menu, we'll ignore it"
464 # Now, if 2nd and 3rd boot entries are for floppy and CD/DVD,
465 # move them up to 1st and 2nd, making our entry the 3rd.
466 bootorder=$(efibootmgr | sed -n 's/^BootOrder: \(.*\)$/\1/p')
467 boot1st=$(echo $bootorder | sed -n "s/\(....\).*$/\1/p")
468 boot2nd=$(echo $bootorder | sed -n "s/....,\(....\).*$/\1/p")
469 boot3rd=$(echo $bootorder | sed -n "s/....,....,\(....\).*$/\1/p")
470 boot456=$(echo $bootorder | sed -n "s/....,....,....\(.*\).*$/\1/p")
471 name2nd=$(efibootmgr | sed -n "s/^Boot$boot2nd[\*] \(.*\)$/\1/p")
472 name3rd=$(efibootmgr | sed -n "s/^Boot$boot3rd[\*] \(.*\)$/\1/p")
473 name23="@$name2nd@$name3rd"
474 if ( echo $name23 | grep -qi "@floppy" ); then
475 if ( echo $name23 | grep -qi "@cd") || ( echo $name23 | grep -qi "@dvd"); then
476 efibootmgr $efiquiet -o $boot2nd,$boot3rd,$boot1st$boot456
487 mount | grep "^$boot\>" > /dev/null
489 echo 1>&2 "$PRG: $boot appears to be mounted! aborting."
493 if (command -v mkdosfs > /dev/null 2>&1) ; then
494 [ -x `command -v mkdosfs` ] || FAIL=1 ; else FAIL=1 ; fi
495 if [ "$FAIL" = 1 ] ; then
496 echo 1>&2 "$PRG: mkdosfs is not installed or cannot be found"
500 [ "$VERBOSE" = 1 ] && echo "$PRG: Creating DOS filesystem on $boot..."
501 mkdosfs -n bootstrap "$boot" > /dev/null
502 if [ $? != 0 ] ; then
503 echo 1>&2 "$PRG: DOS filesystem creation failed!"
511 ## defaults for this are defined at the beginning of the script with
514 # We want to create an append= line from the current /proc/cmdline,
515 # so things like console=ttyS0 get picked up automatically.
516 # We also want to filter out bits of cmdline we are not interested in.
518 if [ -f /proc/cmdline ]; then
519 cmdline=`cat /proc/cmdline`
521 echo 1>&2 "$PRG: Warning: couldn't read /proc/cmdline, may need to add append=... to elilo.conf"
525 append=`echo $cmdline | tr ' ' '\n' | grep "^console=" | tr '\n' ' '`
526 if [ ! -z "$append" ]; then append="append=\"$append\""; fi
529 "## elilo configuration file generated by elilo $VERSION
535 " > "$TMPCONF" || return 1
537 if [ "$ARCHITECTURE" = "ia64" ]
539 echo "relocatable" >> "$TMPCONF" || return 1
556 " >> "$TMPCONF" || return 1
558 ## Copy the new elilo.conf to /etc
559 if [ -f $CONF ]; then
560 echo 1>&2 "$PRG: backing up existing $CONF as ${CONF}-"
564 cp -f "$TMPCONF" "$CONF"
565 if [ $? != 0 ] ; then
566 echo 1>&2 "$PRG: An error occured while writing to $conf"
573 # check partition will be big enough for all we want to add to it
577 imagefiles=`grep '^image[[:space:]]*=' $bootconf | \
578 sed 's/^image[[:space:]]*=[[:space:]]*//' | grep -v ':'`
579 initrdfiles=`grep '^[[:space:]]*initrd[[:space:]]*=' $bootconf | \
580 sed 's/.*=[[:space:]]*//' | grep -v ':'`
581 vmmfiles=`grep '^[[:space:]]*vmm[[:space:]]*=' $bootconf | \
582 sed 's/.*=[[:space:]]*//' | grep -v ':'`
583 bytesneeded=`cat $imagefiles $initrdfiles $vmmfiles $install $bootconf 2>/dev/null | wc -c`
584 # convert to KB, allowing 5% overhead
585 kbneeded=$(( bytesneeded / 1024 + bytesneeded / 20480 ))
586 kbavailable=$(df -P -k $TARGET | sed -n "s|^$boot[[:space:]]\+[0-9]\+[[:space:]]\+[0-9]\+[[:space:]]\+\([0-9]\+\).*$|\1|p")
587 if [ -z $kbavailable ]; then
588 echo 2>&1 "$PRG: unable to determine space on $boot, aborting"
591 if [ -d $TARGET/$EFIROOT ]; then
592 kbused=$(du -ks $TARGET/$EFIROOT | sed -n "s/[ ].*$//p")
596 [ "$VERBOSE" = 1 ] && echo "$PRG: ${kbneeded}KB needed, ${kbavailable}KB free, ${kbused}KB to reuse"
597 kbavailable=$(( kbavailable + kbused ))
598 if [ "$kbavailable" -lt "$kbneeded" ] ; then
599 echo 1>&2 "$PRG: Insufficient space on $boot, need ${kbneeded}KB, only ${kbavailable}KB available"
606 ## take out the trash.
609 if [ -n "$TARGET" ]; then
611 [ "$VERBOSE" = 1 ] && echo "$PRG: Unmounting $boot"
613 [ $? != 0 ] && echo 2>&1 "$PRG: Warning, failed to unmount $TARGET"
615 if [ -n "$TMPCONF" ] ; then rm -f "$TMPCONF" ; fi
616 if [ -d "$TMP/bootstrap.$$" ] ; then rmdir "$TMP/bootstrap.$$" ; fi
617 if [ "$umountproc" = 1 ] ; then umount /proc ; fi
625 ## absurdly bloated case statement to parse command line options.
626 if [ $# != 0 ] ; then
646 # allow --force for now, boot-floppies 3.0.20 and
647 # systemconfigurator use that instead of --format
648 echo 1>&2 "$PRG: Warning: --force is now deprecated. Use --for\mat."
649 echo 1>&2 "Try \`$PRG --help' for more information."
662 if [ -n "$2" ] ; then
667 echo 1>&2 "$PRG: option requires an argument $1"
668 echo 1>&2 "Try \`$PRG --help' for more information."
673 if [ -n "$2" ] ; then
678 echo 1>&2 "$PRG: option requires an argument $1"
679 echo 1>&2 "Try \`$PRG --help' for more information."
684 if [ -n "$2" ] ; then
687 ERR=" Error in $CONF:"
690 echo 1>&2 "$PRG: option requires an argument $1"
691 echo 1>&2 "Try \`$PRG --help' for more information."
701 if [ -n "$2" ] ; then
706 echo 1>&2 "$PRG: option requires an argument $1"
707 echo 1>&2 "Try \`$PRG --help' for more information."
712 if [ -n "$2" ] ; then
717 echo 1>&2 "$PRG: option requires an argument $1"
718 echo 1>&2 "Try \`$PRG --help' for more information."
723 if [ -n "$2" ] ; then
728 echo 1>&2 "$PRG: option requires an argument $1"
729 echo 1>&2 "Try \`$PRG --help' for more information."
734 if [ -n "$2" ] ; then
739 echo 1>&2 "$PRG: option requires an argument $1"
740 echo 1>&2 "Try \`$PRG --help' for more information."
748 echo 1>&2 "$PRG: unrecognized option \`$1'"
749 echo 1>&2 "Try \`$PRG --help' for more information."
756 ## check that are root
757 if [ `id -u` != 0 ] ; then
758 echo 1>&2 "$PRG: requires root privileges, go away."
762 ## check that autoconf options are only specified with --autoconf
763 if [ "$bootconf" = "auto" ] && [ "$autoconf" = "0" ] ; then
764 echo 1>&2 "$PRG: Auto-config options specified without --autoconf."
768 ## check that specified config file exists, unless we are to generate it,
769 ## which case we assume all options are done on the command line.
770 if [ "$autoconf" = "0" ] ; then
774 ## /proc is needed to parse /proc/partitions, etc.
775 if [ ! -f /proc/uptime ]; then
776 [ "$VERBOSE" = 1 ] && echo "$PRG: Mounting /proc..."
777 mount -t proc proc /proc 2> /dev/null
779 echo 1>&2 "$PRG: Failed to mount /proc, aborting."
785 # We need vfat support, so make sure it is loaded
786 modprobe vfat >/dev/null 2>&1
788 ## elilo.conf autogeneration. MUST have secure mktemp to
789 ## avoid race conditions. Debian's mktemp qualifies.
790 if [ "$autoconf" = "1" ] ; then
791 TMPCONF=`mktemp -q "$TMP/$PRG.XXXXXX"`
792 if [ $? != 0 ] ; then
793 echo 1>&2 "$PRG: Could not create temporary file, aborting."
797 if [ $? != 0 ] ; then
798 echo 1>&2 "$PRG: An error occured generating elilo.conf, aborting."
805 ## Checks if each option was defined on the command line, and if so
806 ## don't read it from the configuration file. this avoids
807 ## configuration options from being set null, as well as command line
808 ## options from being clobbered.
809 [ "$ARGBT" != 1 ] && [ $(parseconf ck boot) = 0 ] && boot=`parseconf str boot`
812 if [ "$boot" = unconfigured ] ; then
813 echo 1>&2 "$PRG: You must specify the device for the bootstrap partition. (ie: -b /dev/hdaX)"
814 echo 1>&2 "$PRG: Try \`$PRG --help' for more information."
818 ## validate configuration for sanity.
821 if [ "$FORMAT" = "yes" ]; then
825 [ "$VERBOSE" = 1 ] && echo "$PRG: Checking filesystem on $boot..."
826 dosfsck $boot > /dev/null
828 echo 1>&2 "$PRG: Filesystem on $boot is corrupt, please fix that and rerun $PRG."
838 echo 1>&2 "$PRG: Failed to unmount $boot"
843 [ "$VERBOSE" = 1 ] && echo "$PRG: Installation complete."