aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xinstaller/archangel24
-rw-r--r--installer/lib/common.sh24
-rw-r--r--tests/unit/test_common.bats78
3 files changed, 116 insertions, 10 deletions
diff --git a/installer/archangel b/installer/archangel
index aa8eeaa..b3c232f 100755
--- a/installer/archangel
+++ b/installer/archangel
@@ -1159,16 +1159,20 @@ configure_zfsbootmenu() {
--quiet
done
- # Get the boot entry number and set as first in boot order
- local bootnum
- bootnum=$(efibootmgr | grep "ZFSBootMenu" | head -1 | grep -oP 'Boot\K[0-9A-F]+')
- if [[ -n "$bootnum" ]]; then
- # Get current boot order, prepend our entry
- local current_order
- current_order=$(efibootmgr | grep "BootOrder" | cut -d: -f2 | tr -d ' ')
- efibootmgr --bootorder "$bootnum,$current_order" --quiet
- info "ZFSBootMenu set as primary boot option"
- fi
+ # Look up our boot entry and the current boot order, then prepend
+ # our entry. Capture efibootmgr output once and parse via helpers
+ # so a missing entry or a malformed BootOrder line fails fast with
+ # an actionable message instead of silently leaving the boot order
+ # unset (which produces a non-booting machine after reboot).
+ local efibootmgr_output bootnum current_order
+ efibootmgr_output=$(efibootmgr) \
+ || error "efibootmgr command failed; cannot configure boot order"
+ bootnum=$(parse_efibootmgr_entry "ZFSBootMenu" <<< "$efibootmgr_output") \
+ || error "Could not find ZFSBootMenu entry in efibootmgr output; boot order not set"
+ current_order=$(parse_efibootmgr_bootorder <<< "$efibootmgr_output") \
+ || error "Could not parse BootOrder line from efibootmgr output; boot order not set"
+ efibootmgr --bootorder "$bootnum,$current_order" --quiet
+ info "ZFSBootMenu set as primary boot option"
info "ZFSBootMenu configuration complete."
}
diff --git a/installer/lib/common.sh b/installer/lib/common.sh
index 8193b19..98220fa 100644
--- a/installer/lib/common.sh
+++ b/installer/lib/common.sh
@@ -236,6 +236,30 @@ install_dropin() {
cat > "${dir}/${dropin_name}.conf"
}
+# Read efibootmgr output from stdin and echo the boot number of the
+# first entry whose label contains $1. Returns 1 (with empty output)
+# if the label is empty, no entry matches, or the matched line has no
+# Boot[0-9A-F]+ prefix. The empty-label guard is important: an empty
+# string would match every line, and a line like "BootCurrent: 0001"
+# would falsely satisfy the Boot[hex]+ regex (capturing "C").
+parse_efibootmgr_entry() {
+ local label="$1"
+ [[ -z "$label" ]] && return 1
+ local line
+ line=$(grep -F -m 1 "$label") || return 1
+ [[ "$line" =~ Boot([0-9A-Fa-f]+) ]] || return 1
+ echo "${BASH_REMATCH[1]}"
+}
+
+# Read efibootmgr output from stdin and echo the comma-separated boot
+# numbers from the BootOrder line, with whitespace stripped. Returns 1
+# (with empty output) if no BootOrder line is present.
+parse_efibootmgr_bootorder() {
+ local line
+ line=$(grep "^BootOrder:") || return 1
+ echo "${line#BootOrder:}" | tr -d ' '
+}
+
# List available disks (not in use)
list_available_disks() {
local disks=()
diff --git a/tests/unit/test_common.bats b/tests/unit/test_common.bats
index c81d2e3..0bb76a1 100644
--- a/tests/unit/test_common.bats
+++ b/tests/unit/test_common.bats
@@ -246,3 +246,81 @@ Environment="FOO=bar baz"'
[[ "$output" == *'"FOO=bar baz"'* ]]
teardown_dropin_tmp
}
+
+#############################
+# parse_efibootmgr_entry
+#############################
+
+@test "parse_efibootmgr_entry returns boot number for matching label" {
+ local sample="BootCurrent: 0001
+Boot0000* Windows Boot Manager
+Boot0001* ZFSBootMenu
+Boot0002* PXE Boot"
+ run parse_efibootmgr_entry "ZFSBootMenu" <<< "$sample"
+ [ "$status" -eq 0 ]
+ [ "$output" = "0001" ]
+}
+
+@test "parse_efibootmgr_entry returns first match when multiple labels match" {
+ local sample="Boot0001* ZFSBootMenu
+Boot0002* ZFSBootMenu-disk2"
+ run parse_efibootmgr_entry "ZFSBootMenu" <<< "$sample"
+ [ "$status" -eq 0 ]
+ [ "$output" = "0001" ]
+}
+
+@test "parse_efibootmgr_entry handles hex characters in boot number" {
+ local sample="Boot00FE* ZFSBootMenu"
+ run parse_efibootmgr_entry "ZFSBootMenu" <<< "$sample"
+ [ "$status" -eq 0 ]
+ [ "$output" = "00FE" ]
+}
+
+@test "parse_efibootmgr_entry returns 1 with empty output when label absent" {
+ local sample="Boot0001* Windows Boot Manager"
+ run parse_efibootmgr_entry "ZFSBootMenu" <<< "$sample"
+ [ "$status" -eq 1 ]
+ [ -z "$output" ]
+}
+
+@test "parse_efibootmgr_entry returns 1 with empty output for empty input" {
+ run parse_efibootmgr_entry "ZFSBootMenu" < /dev/null
+ [ "$status" -eq 1 ]
+ [ -z "$output" ]
+}
+
+@test "parse_efibootmgr_entry returns 1 for empty label without false-matching BootCurrent" {
+ local sample="BootCurrent: 0001
+Boot0001* ZFSBootMenu"
+ run parse_efibootmgr_entry "" <<< "$sample"
+ [ "$status" -eq 1 ]
+ [ -z "$output" ]
+}
+
+#############################
+# parse_efibootmgr_bootorder
+#############################
+
+@test "parse_efibootmgr_bootorder extracts comma-separated boot numbers" {
+ local sample="BootCurrent: 0001
+BootOrder: 0001,0002,0003
+Boot0001* ZFSBootMenu"
+ run parse_efibootmgr_bootorder <<< "$sample"
+ [ "$status" -eq 0 ]
+ [ "$output" = "0001,0002,0003" ]
+}
+
+@test "parse_efibootmgr_bootorder strips whitespace from boot order" {
+ local sample="BootOrder: 0001, 0002 , 0003"
+ run parse_efibootmgr_bootorder <<< "$sample"
+ [ "$status" -eq 0 ]
+ [ "$output" = "0001,0002,0003" ]
+}
+
+@test "parse_efibootmgr_bootorder returns 1 when BootOrder line absent" {
+ local sample="BootCurrent: 0001
+Boot0001* ZFSBootMenu"
+ run parse_efibootmgr_bootorder <<< "$sample"
+ [ "$status" -eq 1 ]
+ [ -z "$output" ]
+}