Skip to content

Commit 1b06210

Browse files
committed
initrd/bin/kexec-select-boot.sh: add boot menu compat markers and back-to-menu
New functions - boot_marker(): reads /tmp/kexec_initrd_compat.txt (written by kexec-iso-init.sh Layer 1) and returns [OK] (green in CLI), [!] (yellow), or blank per-initrd marker - fmt_boot_target(): formats kernel/initrd paths as "[path | path]", collapsing NixOS store paths to basename when > 35 chars parse_option() rewrite - Extract kernel, initrd, and params from pipe-delimited entries (field 4/5) - Log parsed values for debugging Menu display (get_menu_option) - Show NOTE legend for [OK]/[!]/blank markers before menu - Display "[OK] name (params) [kernel | initrd]" format in both whiptail and CLI modes - Add "b" option: "Select different ISO" (exit code 2) - Log each menu line to debug.log for remote troubleshooting - Sort entries alphabetically by name; dedup strips --- markers first - Replace expr with $((...)) Confirmation dialog (confirm_menu_option) - Show full kernel, initrd, params, Board+/Board- add/remove, USB+ params, and Final combined cmdline - Add "Back to menu" (b) option — Esc/Cancel returns to menu instead of aborting - Default to "y" on Enter in CLI mode Exit code semantics - 1 = user abort (from menu) - 2 = return to ISO selection (kexec-iso-init.sh re-displays ISOs) Misc - Remove CHANGED_FILES whiptail display before DIE (variable unused) - Soft-check confirm_menu_option return in whiptail - Add DEBUG log for hash mismatch Signed-off-by: Thierry Laurion <insurgo@riseup.net>
1 parent 597cd75 commit 1b06210

1 file changed

Lines changed: 146 additions & 36 deletions

File tree

initrd/bin/kexec-select-boot.sh

Lines changed: 146 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ verify_global_hashes() {
8787
valid_global_hash='y'
8888
else
8989
if [ "$gui_menu" = "y" ]; then
90-
CHANGED_FILES=$(grep -v 'OK$' /tmp/hash_output | cut -f1 -d ':')
9190
whiptail_error --title 'ERROR: Boot Hash Mismatch' \
9291
--msgbox "The following files failed the verification process:\n${CHANGED_FILES}\nExiting to a recovery shell" 0 80
9392
fi
93+
DEBUG "kexec-select-boot: hash mismatch in $TMP_HASH_FILE"
9494
DIE "$TMP_HASH_FILE: boot hash mismatch"
9595
fi
9696
# If user enables it, check root hashes before boot as well
@@ -139,40 +139,73 @@ get_menu_option() {
139139
if [ $num_options -eq 1 -a $first_menu = "y" ]; then
140140
option_index=1
141141
elif [ "$gui_menu" = "y" ]; then
142+
if [ ! -f /tmp/kexec_compat_shown ]; then
143+
NOTE "$(printf '\033[0;32m[OK]\033[0m=compatible \033[1;33m[!]\033[0m=may fail after kexec (blank)=cannot verify')"
144+
touch /tmp/kexec_compat_shown
145+
fi
142146
MENU_OPTIONS=()
143147
n=0
148+
# Show kernel/initrd in menu as "[OK] name (params) [kernel | initrd]"
149+
# Log to debug.log so remote troubleshooting can see exact menu format.
150+
# Long store paths (NixOS) collapse to basename; short paths keep directory context
144151
while read option; do
145152
parse_option
146153
n=$(expr $n + 1)
147-
MENU_OPTIONS+=("$n" "$name")
154+
local marker target optline
155+
marker=$(boot_marker)
156+
target=$(fmt_boot_target)
157+
if [ -n "$params" ]; then
158+
optline="$name ($params) $target"
159+
else
160+
optline="$name $target"
161+
fi
162+
if [ -n "$marker" ]; then
163+
MENU_OPTIONS+=("$n" "$marker $optline")
164+
else
165+
MENU_OPTIONS+=("$n" "$optline")
166+
fi
167+
DEBUG "whiptail menu: [$n] $marker $optline"
148168
done <$TMP_MENU_FILE
169+
MENU_OPTIONS+=("b" "Select different ISO")
149170

150171
whiptail_type $BG_COLOR_MAIN_MENU --title "Select your boot option" \
151-
--menu "Choose the boot option [1-$n, a to abort]:" 0 80 8 \
172+
--menu "Choose the boot option [1-$n, a to abort, b to select different ISO]:" 0 80 8 \
152173
-- "${MENU_OPTIONS[@]}" \
153-
2>/tmp/whiptail || DIE "Aborting boot attempt"
174+
2>/tmp/whiptail || option_index="a"
154175

155176
option_index=$(cat /tmp/whiptail)
156177
else
178+
if [ ! -f /tmp/kexec_compat_shown ]; then
179+
NOTE "$(printf '\033[0;32m[OK]\033[0m=compatible \033[1;33m[!]\033[0m=may fail after kexec (blank)=cannot verify')"
180+
touch /tmp/kexec_compat_shown
181+
fi
157182
STATUS "Select your boot option:"
158183
n=0
159184
while read option; do
160185
parse_option
161-
n=$(expr $n + 1)
162-
# Use the same device routing as INPUT so option lines and the
163-
# prompt share the same unbuffered fd (HEADS_TTY when in gui-init
164-
# context, stderr otherwise). Writing to stdout is wrong here
165-
# because DO_WITH_DEBUG pipes stdout through tee for debug logging,
166-
# making it fully buffered — the last option would appear after the
167-
# INPUT prompt.
168-
printf '%d. %s [%s]\n' "$n" "$name" "$kernel" >"${HEADS_TTY:-/dev/stderr}"
186+
n=$((n + 1))
187+
local marker target optline
188+
marker=$(boot_marker)
189+
target=$(fmt_boot_target)
190+
if [ -n "$marker" ]; then
191+
optline="$n. $marker $name ${params:+($params)} $target"
192+
else
193+
optline="$n. $name ${params:+($params)} $target"
194+
fi
195+
printf '%s\n' "$optline" >"${HEADS_TTY:-/dev/stderr}"
196+
DEBUG "CLI menu: $optline"
169197
done <$TMP_MENU_FILE
170198

171-
INPUT "Choose the boot option [1-$n, a to abort]:" -r option_index
199+
INPUT "Choose the boot option [1-$n, a to abort, b for different ISO]:" -r option_index
200+
fi
172201

173-
if [ "$option_index" = "a" ]; then
174-
DIE "Aborting boot attempt"
175-
fi
202+
if [ "$option_index" = "a" ]; then
203+
STATUS "Boot aborted by user"
204+
exit 1
205+
fi
206+
if [ "$option_index" = "b" ]; then
207+
STATUS "Returning to ISO selection"
208+
exit 2
176209
fi
177210
first_menu="n"
178211

@@ -181,40 +214,117 @@ get_menu_option() {
181214
}
182215

183216
confirm_menu_option() {
184-
if [ "$gui_menu" = "y" ]; then
185-
default_text="Make default"
186-
[[ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" = "y" ]] && default_text="${default_text} and boot"
187-
whiptail_warning --title "Confirm boot details" \
188-
--menu "Confirm the boot details for $name:\n\n$(echo $kernel | fold -s -w 80) \n\n" 0 80 8 \
189-
-- 'd' "${default_text}" 'y' "Boot one time" \
190-
2>/tmp/whiptail || DIE "Aborting boot attempt"
191-
192-
option_confirm=$(cat /tmp/whiptail)
217+
# Show full kernel/initrd/params in the confirmation dialog.
218+
# Cancel/Esc returns to the menu (option_confirm="b") instead of aborting,
219+
# so users can change their selection without restarting the boot flow.
220+
# The full cmdline combines the entry's parsed params with the global ADD
221+
# params (injected by kexec-iso-init.sh for ISO boot).
222+
if [ "$gui_menu" = "y" ]; then
223+
default_text="Make default"
224+
[[ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" = "y" ]] && default_text="${default_text} and boot"
225+
whiptail_warning --title "Confirm boot details" \
226+
--menu "$name\n\nKernel: $kernel\nInitramfs: ${initrd:--}\nOptions: ${params:--}\n${CONFIG_BOOT_KERNEL_ADD:+Board adds: $CONFIG_BOOT_KERNEL_ADD\n}${CONFIG_BOOT_KERNEL_REMOVE:+Board removes: $CONFIG_BOOT_KERNEL_REMOVE\n}${add:+ISO params: $add\n}Kernel cmdline: $(echo "$params $CONFIG_BOOT_KERNEL_ADD $add" | xargs)\n" 0 80 8 \
227+
-- 'y' "Boot" 'd' "${default_text}" 'b' "Back to menu" \
228+
2>/tmp/whiptail && option_confirm=$(cat /tmp/whiptail) || option_confirm="b"
193229
else
194-
STATUS "Confirm boot details for $name:"
195-
INFO "$option"
196-
197-
INPUT "Confirm selection by pressing 'y', make default with 'd':" -n 1 option_confirm
230+
STATUS " Confirm boot details for $name:"
231+
STATUS " Kernel: $kernel"
232+
STATUS " Initramfs: ${initrd:--}"
233+
STATUS " Options: ${params:--}"
234+
[ -n "$CONFIG_BOOT_KERNEL_ADD" ] && STATUS " Board adds: $CONFIG_BOOT_KERNEL_ADD"
235+
[ -n "$CONFIG_BOOT_KERNEL_REMOVE" ] && STATUS " Board removes: $CONFIG_BOOT_KERNEL_REMOVE"
236+
[ -n "$add" ] && STATUS " ISO params: $add"
237+
local final="$params"
238+
for rem in $CONFIG_BOOT_KERNEL_REMOVE; do final=$(echo "$final" | sed "s/ $rem / /g; s/^$rem //; s/ $rem$//"); done
239+
final="$final $CONFIG_BOOT_KERNEL_ADD $add"
240+
STATUS " Kernel cmdline: $(echo "$final" | xargs)"
241+
INPUT "Boot (Y), make default (d), back to menu (b) [Y/d/b]:" -n 1 option_confirm
242+
[ -z "$option_confirm" ] && option_confirm="y"
243+
return 0
198244
fi
199245
}
200246

201247
parse_option() {
248+
# Parse pipe-delimited boot entry: name|kexectype|kernel /path|initrd /path|append params
249+
# Field 4 can be either "initrd /path" or "append ..." when no initrd is present.
202250
name=$(echo $option | cut -d\| -f1)
203-
kernel=$(echo $option | cut -d\| -f3)
251+
kernel=$(echo $option | cut -d\| -f3 | sed 's/^kernel //')
252+
initrd=""; params=""
253+
f4=$(echo $option | cut -d\| -f4)
254+
case "$f4" in
255+
initrd*) initrd="${f4#initrd }"; params=$(echo $option | cut -d\| -f5 | sed 's/append //' | xargs) ;;
256+
append*) params=$(echo "$f4" | sed 's/^append //' | xargs) ;;
257+
*) ;;
258+
esac
259+
LOG "parse_option: name='$name' kernel='$kernel' initrd='$initrd' params='${params:0:80}...'"
260+
}
261+
262+
# Return the initrd compat marker for the current entry's initrd.
263+
# Three possible states:
264+
# [OK] — initrd has the USB fs module as .ko or in modules.builtin
265+
# [!] — initrd has loadable modules but none for the USB fs type
266+
# "" — initrd has zero .ko files (can't verify — assume OK)
267+
#
268+
# The $initrd global is set by parse_option() for each menu entry before
269+
# this function is called, so each entry independently looks up its own
270+
# initrd in the compat file.
271+
#
272+
# Compat file format (written by kexec-iso-init.sh Layer 1):
273+
# initrd/relative/path [OK]
274+
# other/initrd/path [!]
275+
# (absent entries = zero modules, can't verify)
276+
#
277+
# Each line maps one initrd to its USB-filesystem compat status.
278+
# boot_marker() greps for the current entry's initrd path and returns
279+
# that initrd's marker — entries using different initrds show different
280+
# markers even on the same ISO.
281+
# In CLI mode adds ANSI colors: green [OK], yellow [!].
282+
boot_marker() {
283+
local m="" grn="" ylw="" rst=""
284+
[ "$gui_menu" != "y" ] && { grn=$'\033[0;32m'; ylw=$'\033[1;33m'; rst=$'\033[0m'; }
285+
if [ -n "$initrd" ] && [ -r "/tmp/kexec_initrd_compat.txt" ]; then
286+
local ip=$(echo "$initrd" | sed 's|^/*||')
287+
m=$(grep "^$ip " /tmp/kexec_initrd_compat.txt 2>/dev/null | head -1 | cut -d' ' -f2)
288+
[ -n "$m" ] && LOG "boot_marker: initrd=$ip marker=$m" || LOG "boot_marker: initrd=$ip no compat entry"
289+
[ "$m" = "[OK]" ] && m="${grn}[OK]${rst}"
290+
[ "$m" = "[!]" ] && m="${ylw}[!]${rst}"
291+
fi
292+
echo "$m"
293+
}
294+
295+
# Format kernel/initrd for menu display: "[path | path]"
296+
# Keeps directory context for short paths (live/vmlinuz) but falls back to
297+
# basename for unreasonably long store paths (NixOS /nix/store/.../bzImage).
298+
# 35-char threshold: typical paths like "boot/x86_64/loader/linux" fit;
299+
# NixOS store paths with hashes exceed it.
300+
fmt_boot_target() {
301+
local k i
302+
k=$(echo "$kernel" | sed 's|^/*||')
303+
[ -z "$k" ] && k="$kernel"
304+
[ "${#k}" -gt 35 ] && k=$(basename "$k")
305+
i=$(echo "$initrd" | sed 's|^/*||')
306+
[ "${#i}" -gt 35 ] && i=$(basename "$i")
307+
if [ -n "$i" ]; then echo "[$k | $i]"; else echo "[$k]"; fi
204308
}
205309

206310
scan_options() {
207-
STATUS "Scanning for boot options"
311+
STATUS "Scanning for unsigned boot options"
208312
option_file="/tmp/kexec_options.txt"
209313
scan_boot_options "$bootdir" "$config" "$option_file"
210314
if [ ! -s $option_file ]; then
211315
DIE "Failed to parse any boot options"
212316
fi
213-
if [ "$unique" = 'y' ]; then
214-
sort -r $option_file | uniq >$TMP_MENU_FILE
215-
else
216-
cp $option_file $TMP_MENU_FILE
217-
fi
317+
# Sort entries by name so users can scan the menu alphabetically.
318+
# When -u (unique) is set, strip --- markers from append params first
319+
# so entries differing only by GRUB's bootloader separator get deduped.
320+
if [ "$unique" = 'y' ]; then
321+
sed 's/|append \([^|]*\)---[^|]*/|append \1/g' "$option_file" | sort -t\| -k1 -u >"$TMP_MENU_FILE"
322+
else
323+
sort -t\| -k1 "$option_file" >"$TMP_MENU_FILE"
324+
fi
325+
DEBUG "kexec-select-boot: parsed boot options for user selection"
326+
# Option entries are already logged as echo_entry by kexec-parse-boot.sh;
327+
# no need to dump them again here.
218328
}
219329

220330
save_default_option() {

0 commit comments

Comments
 (0)