[PATCH] grub-installer: Support menu selection of grub boot disk
Support user selection of grub boot disk from a list of disks
via a new question, grub-installer/choose_bootdev.
Check for a mismatch between a preseeded value of grub-installer/bootdev
and the guess at the default boot disk made by grub-installer,
and prompt the user to choose the correct disk.
This should help the user avoid grub-installer writing to the MBR
of the wrong device (e.g. #696877) and fix the issue of preseeded
values of bootdev being ignored (e.g. #666974).
Signed-off-by: Vincent McIntyre <vincent.mcintyre@csiro.au>
---
debian/grub-installer.templates | 16 +++++
debian/po/templates.pot | 17 +++++
grub-installer | 130 +++++++++++++++++++++++++++++++++++++--
3 files changed, 159 insertions(+), 4 deletions(-)
diff --git a/debian/grub-installer.templates b/debian/grub-installer.templates
index 888a656..5d15411 100644
--- a/debian/grub-installer.templates
+++ b/debian/grub-installer.templates
@@ -86,6 +86,22 @@ _Description: Device for boot loader installation:
drive;
- "/dev/fd0" will install GRUB to a floppy.
+Template: grub-installer/choose_bootdev
+Type: select
+Choices-C: ${CHOICES}
+Choices: ${DESCRIPTIONS}
+# :sl2:
+_Description: Select disk for boot loader installation:
+ You need to make the newly installed system bootable, by installing
+ the GRUB boot loader on a bootable device. The usual way to do this is to
+ install GRUB on the master boot record of your first hard drive. If you
+ prefer, you can install GRUB elsewhere on the drive, or to another drive,
+ or even to a floppy.
+ .
+ Select a disk from the list. GRUB will be installed to the master boot
+ record of that disk. If you want to install GRUB somewhere other than the
+ master boot record of one of these disks, select 'Enter device manually'.
+
Template: grub-installer/password
Type: password
# :sl2:
diff --git a/debian/po/templates.pot b/debian/po/templates.pot
index dad01db..54085e6 100644
--- a/debian/po/templates.pot
+++ b/debian/po/templates.pot
@@ -198,6 +198,23 @@ msgid ""
" - \"/dev/fd0\" will install GRUB to a floppy."
msgstr ""
+#. Type: string
+#. Description
+#. :sl2:
+#: ../grub-installer.templates:7001
+msgid "Select disk for boot loader installation:"
+msgstr ""
+
+#. Type: string
+#. Description
+#. :sl2:
+#: ../grub-installer.templates:7001
+msgid ""
+"Select a disk from the list. GRUB will be installed to the master boot\n"
+"record of that disk. If you want to install GRUB somewhere other than the\n"
+"master boot record of the listed disks, select 'Enter device manually'.\n"
+msgstr ""
+
#. Type: password
#. Description
#. :sl2:
diff --git a/grub-installer b/grub-installer
index f01eda1..8eefd23 100755
--- a/grub-installer
+++ b/grub-installer
@@ -1,5 +1,5 @@
#! /bin/sh
-
+# export DEBCONF_DEBUG=5
set -e
. /usr/share/debconf/confmodule
#set -x
@@ -217,6 +217,51 @@ devices_to_ids()
echo "$ids"
}
+
+# Produce a comma-separated list of '/dev/X (id)' strings suitable for debconf.
+# The first argument will select /dev/X or (id) strings to be returned.
+# The second argument will cause that <device> to be the only one returned.
+# Examples:
+# echo $(device_list)
+# /dev/sda (ata-SAMSUNG_SSD_PM810_2.5__7mm_128GB_S0NRNEABC02304), /dev/sdb (ata-WDC_WD2002FAEX-007BA0_WD-WMAY05111513)
+# echo $(device_list description /dev/sdb)
+# (ata-WDC_WD2002FAEX-007BA0_WD-WMAY05111513)
+
+device_list()
+{
+ [ -d /dev/disk/by-id ] || return
+
+ local result arg id path dev disk output
+
+ result="$1"
+ case "$result" in
+ /*) result="both"; arg="$1" ;;
+ *) arg="$2" ;;
+ esac
+ case "$result" in
+ disk*|device*) filter_dev() { cut -d' ' -f1; }; ;;
+ description*) filter_dev() { cut -d' ' -f2; }; ;;
+ *) filter_dev() { cat; }; ;;
+ esac
+
+ # /dev/disk/by-id has multiple links for the same physical disk.
+ # Sorting the output on only one column gives just one disk,path pair
+ # for each physical disk. Then filter to get the column(s) requested.
+ output="$(
+ for path in /dev/disk/by-id/*; do
+ [ -e "$path" ] || continue
+ id="$(echo "$path" | sed -e 's+^.*/++')"
+ dev="$(readlink -f "$path")"
+ disk="$(device_to_disk "$dev")"
+ [ -n "$arg" ] && dev="$arg"
+ [ "$dev" = "$disk" ] && printf '%s (%s)\n' "$disk" "$id"
+ done | \
+ sort -k1,1 -s -u | filter_dev | sed -e 's/$/, /'
+ )"
+ output="$(echo $output | sed -e 's/, *$//')"
+ echo "$output"
+}
+
rootfs=$(findfs /)
bootfs=$(findfs /boot)
[ -n "$bootfs" ] || bootfs="$rootfs"
@@ -590,7 +635,66 @@ esac
db_progress STEP 1
db_progress INFO grub-installer/progress/step_bootdev
+select_bootdev() {
+ [ "X" = "X${DEBCONF_DEBUG}" ] || log "select_bootdev: arg='$1'"
+
+ local manual_entry bootdev_choices default_choice chosen result
+
+ result=""
+ default_choice="$1"
+
+ manual_entry="Enter device manually"
+ bootdev_choices="$(device_list disk)"
+ bootdev_descriptions="$(device_list)"
+
+ bootdev_choices="$manual_entry, $bootdev_choices"
+ bootdev_descriptions="$manual_entry (An entry dialog will appear), $bootdev_descriptions"
+ [ "X" = "X${DEBCONF_DEBUG}" ] || log "Bootdev Choices: '$bootdev_choices'"
+ [ "X" = "X${DEBCONF_DEBUG}" ] || log "Bootdev Descriptions: '$bootdev_descriptions'"
+
+ db_subst grub-installer/choose_bootdev CHOICES "$bootdev_choices"
+ db_subst grub-installer/choose_bootdev DESCRIPTIONS "$bootdev_descriptions"
+ # set initial selection
+ if [ -n "$default_choice" ] ; then
+ chosen="$(device_list disk $default_choice)"
+ if [ -n "$chosen" ] ;then
+ db_set grub-installer/choose_bootdev "$chosen"
+ fi
+ fi
+
+ db_input high grub-installer/choose_bootdev || true
+ if ! db_go; then
+ log "Returning to menu"
+ db_progress STOP
+ exit 10
+ fi
+ db_get grub-installer/choose_bootdev || true
+ if [ "$RET" = "$manual_entry" ] ; then
+ result=""
+ else
+ result="$(echo "$RET" | cut -d' ' -f1)"
+ fi
+
+ [ "X" = "X${DEBCONF_DEBUG}" ] || log "select_bootdev: result='$result'"
+ echo "$result"
+}
+
+# make sure this question is displayed at least once
+db_fset grub-installer/choose_bootdev seen false
+
+if [ "$bootdev" != "dummy" ] && [ ! "$frdev" ]; then
+ # check for a preseeded value
+ db_get grub-installer/bootdev || true
+ if [ -n "$RET" ] ; then
+ bootdev="$RET"
+ fi
+fi
+
while : ; do
+
+ [ "X" = "X${DEBCONF_DEBUG}" ] || \
+ log "q='$q' state='$state' defbd='$default_bootdev' bd='$bootdev'"
+
if [ "$state" = 1 ]; then
db_input high $q || true
if ! db_go; then
@@ -600,8 +704,18 @@ while : ; do
fi
db_get $q
if [ "$RET" = true ]; then
- bootdev="$default_bootdev"
- break
+ # default_bootdev can be guessed incorrectly.
+ # If the user supplied a value for bootdev,
+ # ask them to resolve any conflict.
+ if [ "$bootdev" != "$default_bootdev" ] ; then
+ bootdev="$(select_bootdev "$bootdev")"
+ previous_state=1
+ fi
+ if [ -e "$bootdev" ] ; then
+ break
+ else
+ state=2
+ fi
else
# Exit to menu if /boot is on SATA RAID/multipath; we
# don't support device selection in that case
@@ -612,7 +726,15 @@ while : ; do
state=2
fi
elif [ "$state" = 2 ]; then
- db_input critical grub-installer/bootdev || true
+
+ if [ "$previous_state" != "1" ]; then
+ bootdev="$(select_bootdev "$bootdev")"
+ unset previous_state
+ fi
+
+ if [ ! -e "$bootdev" ]; then
+ db_input critical grub-installer/bootdev || true
+ fi
if ! db_go; then
if [ "$q" ]; then
state=1
Reply to: