Skip to content

Commit d35f02f

Browse files
committed
WIP: ISO boot detection fixes and test improvements
- Fix duplicate iso-scan/filename parameter injection - Add deduplication to cfg boot method extraction - Add selective parameter injection (boot=live, boot=casper, live-media) - Add header comments to kexec-iso-init.sh, kexec-parse-boot.sh, kexec-boot.sh - Add param injection validation to test suite - Fix Tails boot parameter regression (was injecting boot=casper incorrectly) - Show boot params in boot menu options
1 parent e9d1a49 commit d35f02f

5 files changed

Lines changed: 532 additions & 90 deletions

File tree

initrd/bin/kexec-boot.sh

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
#!/bin/bash
2-
# Launches kexec from saved configuration entries
2+
# Execute kexec to boot an OS kernel from parsed boot configuration
3+
#
4+
# This script takes a boot entry (from kexec-parse-boot.sh) and executes
5+
# kexec to load and boot the OS kernel. It handles:
6+
# - ELF kernels (standard Linux)
7+
# - Multiboot kernels (Xen)
8+
# - Initial ramdisks (initrd)
9+
# - Kernel command line modification (add/remove parameters)
10+
#
11+
# Options:
12+
# -b Boot directory (e.g., /boot)
13+
# -e Entry string (name|kexectype|kernel path[|initrd][|append])
14+
# -r Parameters to remove from cmdline
15+
# -a Parameters to add to cmdline
16+
# -o Override initrd path
17+
# -f Dry run: print files only
18+
# -i Dry run: print initrd only
19+
#
320
set -e -o pipefail
421
. /tmp/config
522
. /etc/functions.sh
@@ -65,6 +82,8 @@ fix_file_path() {
6582
adjusted_cmd_line="n"
6683
adjust_cmd_line() {
6784
DEBUG "adjust_cmd_line: original cmdline='$cmdline'"
85+
cmdline=$(echo "$cmdline" | sed 's/---.*$//' | xargs)
86+
DEBUG "adjust_cmd_line: after stripping --- separator='$cmdline'"
6887
if [ -n "$cmdremove" ]; then
6988
for i in $cmdremove; do
7089
cmdline=$(echo $cmdline | sed "s/\b$i\b//g")
@@ -164,6 +183,7 @@ if [ "$dryrun" = "y" ]; then exit 0; fi
164183

165184
DEBUG "kexec-boot.sh: cmdadd='$cmdadd'"
166185
DEBUG "kexec-boot.sh: cmdremove='$cmdremove'"
186+
DEBUG "kexec-boot.sh: final cmdline='$cmdline'"
167187
STATUS "Loading the new kernel"
168188
DEBUG "kexec command: $kexeccmd"
169189
DEBUG "kexec-boot: executing kexec with adjusted_cmd_line=$adjusted_cmd_line kexectype=$kexectype"

initrd/bin/kexec-iso-init.sh

Lines changed: 172 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,33 @@
11
#!/bin/bash
2-
# Boot from signed ISO
2+
# Boot from signed ISO file on USB media
3+
#
4+
# This script handles booting from ISO files stored on USB storage.
5+
# It works by mounting the ISO, detecting boot mechanisms supported by
6+
# the ISO's initrd, injecting appropriate kernel parameters, and
7+
# executing kexec to boot the OS.
8+
#
9+
# Detection approach:
10+
# 1. Mount the ISO as a loopback device
11+
# 2. Extract and scan the initrd for supported boot mechanisms
12+
# 3. Fall back to scanning *.cfg files if initrd detection yields nothing
13+
# 4. If no known boot-from-ISO mechanism is found, warn and guide user
14+
#
15+
# Supported boot mechanisms (detected in initrd or config):
16+
# - iso-scan/findiso: Dracut-based (Ubuntu, Debian Live, Tails, etc.)
17+
# - live-media: Dracut live-media parameter
18+
# - boot=live: Debian Live / Fedora Live
19+
# - boot=casper: Ubuntu Casper
20+
# - nixos: NixOS
21+
# - anaconda: Fedora/RHEL Anaconda (block device required)
22+
# - overlay: OverlayFS support
23+
# - toram: Load-to-RAM support
24+
#
25+
# If no mechanism is detected, the user is warned that the ISO may not
26+
# support booting from ISO file on USB, and is given alternative options:
27+
# - Write ISO directly to USB with dd
28+
# - Use Ventoy in USB emulation mode
29+
# - Boot from real DVD drive
30+
#
331
set -e -o pipefail
432
. /etc/functions.sh
533
. /etc/gui_functions.sh
@@ -52,6 +80,18 @@ mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot ||
5280

5381
DEV_UUID=$(blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2)
5482

83+
# Scan an initrd for supported filesystems and boot mechanisms.
84+
# This function unpacks the initrd and searches for:
85+
# - Kernel modules (*.ko/*.ko.xz) -> supported filesystems
86+
# - Scripts and configs (*.sh, *.conf, init, scripts/*) -> boot mechanisms
87+
#
88+
# Supported filesystems detected: ext4, vfat, exfat, ntfs, btrfs, xfs
89+
# Supported boot mechanisms detected: iso-scan, live-media, boot-live,
90+
# casper, nixos, anaconda, overlay, toram, device
91+
#
92+
# Results are stored in global variables:
93+
# - supported_fses: Space-separated list of supported filesystem types
94+
# - supported_boot: Space-separated list of supported boot mechanisms
5595
scan_initramfs() {
5696
local path="$1"
5797
local tmpdir=""
@@ -82,20 +122,40 @@ scan_initramfs() {
82122
boot_content=$(strings "$path" 2>/dev/null) || true
83123
fi
84124

85-
echo "$boot_content" | grep -qEi "iso.scan|findiso" &&
86-
supported_boot="${supported_boot}iso-scan/findiso " || true
87-
echo "$boot_content" | grep -qEi "live.media|live-media" &&
88-
supported_boot="${supported_boot}live-media= " || true
89-
echo "$boot_content" | grep -qEi "boot=live|rd.live.image|rd.live.squash" &&
90-
supported_boot="${supported_boot}boot=live " || true
91-
echo "$boot_content" | grep -qEi "boot.casper|casper" &&
92-
supported_boot="${supported_boot}boot=casper " || true
93-
echo "$boot_content" | grep -qEi "nixos" &&
94-
supported_boot="${supported_boot}nixos " || true
95-
echo "$boot_content" | grep -qEi "inst.stage2|inst.repo" &&
96-
supported_boot="${supported_boot}anaconda " || true
125+
for pattern in "iso.scan|findiso" "live.media|live-media" "boot=live|rd.live.image|rd.live.squash" "boot.casper|casper" "nixos" "inst.stage2|inst.repo" "overlay|overlayfs" "toram" "CDLABEL|img_dev|check_dev"; do
126+
case "$pattern" in
127+
iso.scan|findiso) label="iso-scan" ;;
128+
live.media|live-media) label="live-media" ;;
129+
boot=live|rd.live.image|rd.live.squash) label="boot-live" ;;
130+
boot.casper|casper) label="casper" ;;
131+
nixos) label="nixos" ;;
132+
inst.stage2|inst.repo) label="anaconda" ;;
133+
overlay|overlayfs) label="overlay" ;;
134+
toram) label="toram" ;;
135+
CDLABEL|img_dev|check_dev) label="device" ;;
136+
esac
137+
echo "$boot_content" | grep -qEi "$pattern" &&
138+
supported_boot="${supported_boot}${label} " || true
139+
done
97140
}
98141

142+
# Detect if the mounted ISO is an installer ISO (not a live/bootable ISO).
143+
# Installer ISOs (like Debian DVD installer) do not support booting from
144+
# ISO file on USB - they only work with physical CD/DVD or PXE boot.
145+
#
146+
# Detection checks for:
147+
# - /boot/install* directory (installer content)
148+
# - /boot/isolinux or /boot/grub (boot configs, but no live boot)
149+
# - /boot/install.amd/vmlinuz and initrd.gz (installer kernel/initrd)
150+
#
151+
# Detect boot mechanisms supported by the ISO's initrd.
152+
# This function:
153+
# 1. Parses all *.cfg files to find initrd paths
154+
# 2. For each initrd, calls scan_initramfs() to extract supported features
155+
# 3. Outputs two lines: "fs:..." and "boot:..." with detected support
156+
#
157+
# This is the primary detection method - scanning initrd content directly
158+
# provides the most accurate picture of what the ISO can do.
99159
detect_initrd_boot_support() {
100160
local supported_fses=""
101161
local supported_boot=""
@@ -131,6 +191,13 @@ detect_initrd_boot_support() {
131191
return 0
132192
}
133193

194+
# Fallback detection: scan *.cfg files for boot parameters.
195+
# This is used when initrd detection fails or yields no results.
196+
# It greps through boot config files (GRUB, syslinux, ISOLINUX) for
197+
# known boot parameters that indicate ISO-on-USB support.
198+
#
199+
# This method is less accurate than initrd scanning but can provide
200+
# hints when initrd extraction fails.
134201
extract_boot_params_from_cfg() {
135202
for cfg in $(find /boot -name '*.cfg' -type f 2>/dev/null); do
136203
[ -r "$cfg" ] || continue
@@ -141,22 +208,49 @@ extract_boot_params_from_cfg() {
141208
while IFS= read -r line; do
142209
case "$line" in
143210
*boot=live* | *rd.live.image* | *rd.live.squashimg=*)
144-
boot_params="${boot_params}boot=live "
211+
if ! echo "$boot_params" | grep -q "boot-live"; then
212+
boot_params="${boot_params}boot-live "
213+
fi
145214
;;
146215
*iso-scan/filename=* | *findiso=*)
147-
boot_params="${boot_params}iso-scan/findiso "
216+
if ! echo "$boot_params" | grep -q "iso-scan"; then
217+
boot_params="${boot_params}iso-scan "
218+
fi
148219
;;
149220
*live-media=* | *live.media=*)
150-
boot_params="${boot_params}live-media= "
221+
if ! echo "$boot_params" | grep -q "live-media"; then
222+
boot_params="${boot_params}live-media "
223+
fi
151224
;;
152225
*boot=casper* | *casper*)
153-
boot_params="${boot_params}boot=casper "
226+
if ! echo "$boot_params" | grep -q "casper"; then
227+
boot_params="${boot_params}casper "
228+
fi
154229
;;
155230
*inst.stage2=* | *inst.repo=*)
156-
boot_params="${boot_params}anaconda "
231+
if ! echo "$boot_params" | grep -q "anaconda"; then
232+
boot_params="${boot_params}anaconda "
233+
fi
157234
;;
158235
*nixos*)
159-
boot_params="${boot_params}nixos "
236+
if ! echo "$boot_params" | grep -q "nixos"; then
237+
boot_params="${boot_params}nixos "
238+
fi
239+
;;
240+
*overlay=* | *overlayfs*)
241+
if ! echo "$boot_params" | grep -q "overlay"; then
242+
boot_params="${boot_params}overlay "
243+
fi
244+
;;
245+
*toram*)
246+
if ! echo "$boot_params" | grep -q "toram"; then
247+
boot_params="${boot_params}toram "
248+
fi
249+
;;
250+
*CDLABEL=* | *img_dev=* | *check_dev*)
251+
if ! echo "$boot_params" | grep -q "device"; then
252+
boot_params="${boot_params}device "
253+
fi
160254
;;
161255
esac
162256
done <"$cfg"
@@ -165,6 +259,15 @@ extract_boot_params_from_cfg() {
165259
return 1
166260
}
167261

262+
# ============================================================================
263+
# Main detection flow
264+
# ============================================================================
265+
# Step 1: Scan initrd for supported boot mechanisms
266+
# Step 2: If no boot method found, fall back to cfg file scanning
267+
# Step 3: Check USB filesystem compatibility
268+
# Step 4: If no known mechanism found, warn user with guidance
269+
# ============================================================================
270+
168271
STATUS "Detecting USB filesystem and boot method support..."
169272
SUPPORTED_FSES=""
170273
SUPPORTED_BOOT=""
@@ -177,12 +280,16 @@ SUPPORTED_BOOT=$(echo "$tmp_support" | grep "^boot:" | sed 's/^boot://') || SUPP
177280
DEBUG "SUPPORTED_FSES='$SUPPORTED_FSES'"
178281
DEBUG "SUPPORTED_BOOT from initrd='$SUPPORTED_BOOT'"
179282

180-
if [ -z "$SUPPORTED_BOOT" ]; then
181-
DEBUG "No boot method in initrd, scanning *.cfg files..."
182-
CFG_BOOT=$(extract_boot_params_from_cfg 2>/dev/null | grep "^cfg:" | sed 's/^cfg://') || CFG_BOOT=""
183-
DEBUG "CFG_BOOT='$CFG_BOOT'"
184-
else
185-
DEBUG "Boot method found in initrd, skipping cfg extraction"
283+
DEBUG "Scanning *.cfg files to augment initrd results..."
284+
CFG_BOOT=$(extract_boot_params_from_cfg 2>/dev/null | grep "^cfg:" | sed 's/^cfg://') || CFG_BOOT=""
285+
DEBUG "CFG_BOOT='$CFG_BOOT'"
286+
287+
if [ -n "$SUPPORTED_BOOT" ] && [ -n "$CFG_BOOT" ]; then
288+
SUPPORTED_BOOT="$SUPPORTED_BOOT $CFG_BOOT"
289+
DEBUG "Combined boot methods: $SUPPORTED_BOOT"
290+
elif [ -z "$SUPPORTED_BOOT" ] && [ -n "$CFG_BOOT" ]; then
291+
SUPPORTED_BOOT="$CFG_BOOT"
292+
DEBUG "Using cfg boot methods: $SUPPORTED_BOOT"
186293
fi
187294

188295
if [ -n "$SUPPORTED_FSES" ]; then
@@ -197,28 +304,58 @@ fi
197304

198305
if [ -n "$SUPPORTED_BOOT" ]; then
199306
DETECTED_METHODS="$SUPPORTED_BOOT"
200-
DEBUG "Initrd supports boot methods: $DETECTED_METHODS"
201-
elif [ -n "$CFG_BOOT" ]; then
202-
DETECTED_METHODS="$CFG_BOOT"
203-
DEBUG "Boot config (*.cfg) indicates boot methods: $DETECTED_METHODS"
307+
DEBUG "Detected boot methods: $DETECTED_METHODS"
204308
fi
205309

206310
DEBUG "DETECTED_METHODS='$DETECTED_METHODS'"
207311
if [ -z "$DETECTED_METHODS" ]; then
208312
WARN "ISO may not boot from USB file: no supported boot method detected"
209313
if [ -x /bin/whiptail ]; then
210-
if ! whiptail_warning --title 'ISO BOOT COMPATIBILITY WARNING' --yesno \
211-
"ISO boot from USB file may not work.\n\nThis ISO does not appear to support booting from ISO file on USB stick.\n\nKnown compatible ISOs: Ubuntu, Debian Live, Tails, NixOS, Fedora Workstation, PureOS, Kicksecure.\n\nFor this ISO, try:\n- Use distribution USB creation tool (Ventoy, Rufus, etc)\n- Write ISO directly to USB with dd\n- Report to upstream that ISO should support USB file boot\n\nDo you want to try anyway?" \
314+
if ! whiptail_warning --title 'ISO BOOT NOT SUPPORTED' --yesno \
315+
"This ISO does not support booting from ISO file on USB.\n\nThe initrd does not include boot-from-ISO mechanisms (no live-boot, casper, fromiso, iso-scan, anaconda, or nixos support detected).\n\nTo use this ISO, write the hybrid image directly to a USB flash drive:\n\nLinux: sudo cp image.iso /dev/sdX (Be cautious!)\nWindows/Mac: Use Rufus, select DD mode (NOT ISO mode)\n\nWrite to whole-disk device (NOT a partition, e.g. /dev/sdX not /dev/sdX1),\nthen boot from USB device directly (not as ISO file).\n\nSee Debian wiki: https://wiki.debian.org/DebianInstall" \
212316
0 80; then
213-
DIE "ISO boot cancelled - unsupported ISO on USB file"
317+
DIE "ISO boot cancelled - initrd does not support USB file boot"
214318
fi
215319
else
216-
INPUT "ISO may not support USB file boot. Try anyway? [y/N]:" -n 1 response
217-
[ "$response" != "y" ] && [ "$response" != "Y" ] && DIE "ISO boot cancelled - unsupported ISO on USB file"
320+
ERROR "ISO initrd has no boot-from-ISO support (no live-boot/casper/iso-scan)"
321+
ERROR "Write hybrid image to USB: Linux: cp iso /dev/sdX | Win/Mac: Rufus DD mode"
322+
INPUT "Try anyway? [y/N]:" -n 1 response
323+
[ "$response" != "y" ] && [ "$response" != "Y" ] && DIE "ISO boot cancelled"
218324
fi
219325
fi
220326

221-
ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH img_dev=/dev/disk/by-uuid/$DEV_UUID iso-scan/filename=/${ISO_PATH} img_loop=$ISO_PATH iso=$DEV_UUID/$ISO_PATH live-media=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH live-media-path=casper"
327+
# ============================================================================
328+
# Boot parameter injection
329+
# ============================================================================
330+
# Inject all known boot-from-ISO parameters. The ISO's initrd will use
331+
# whichever parameters it understands and ignore the rest.
332+
#
333+
# Parameters injected (covering all major boot systems):
334+
# - findiso, fromiso, iso-scan/filename: Dracut standard
335+
# - img_dev, img_loop: additional Dracut variants
336+
# - iso: alternative parameter
337+
# - live-media, live-media-path: live-boot parameters
338+
# - boot=live, boot=casper: casper/live-boot parameters
339+
# ============================================================================
340+
341+
ISO_DEV="/dev/disk/by-uuid/$DEV_UUID"
342+
ISO_PATH_ABS="/$ISO_PATH"
343+
344+
base_params="findiso=$ISO_DEV/$ISO_PATH fromiso=$ISO_DEV/$ISO_PATH iso-scan/filename=$ISO_PATH_ABS img_dev=$ISO_DEV img_loop=$ISO_PATH iso=$DEV_UUID/$ISO_PATH"
345+
346+
add_params=""
347+
if echo "$DETECTED_METHODS" | grep -q "casper"; then
348+
add_params="$add_params boot=casper live-media-path=casper"
349+
fi
350+
if echo "$DETECTED_METHODS" | grep -q "boot-live"; then
351+
add_params="$add_params boot=live"
352+
fi
353+
if echo "$DETECTED_METHODS" | grep -q "live-media"; then
354+
add_params="$add_params live-media=$ISO_DEV/$ISO_PATH"
355+
fi
356+
357+
ADD="$base_params $add_params"
358+
DEBUG "Injecting boot params: $ADD"
222359
REMOVE=""
223360

224361
paramsdir="/media/kexec_iso/$ISO_PATH"

initrd/bin/kexec-parse-boot.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
#!/bin/bash
2+
# Parse boot loader configs (GRUB, syslinux, ISOLINUX) to extract boot entries
3+
#
4+
# This script parses boot configuration files to build a list of boot entries
5+
# that can be used by kexec-boot.sh to boot an OS. It handles:
6+
# - GRUB config files (grub.cfg)
7+
# - SYSLINUX/ISOLINUX config files (isolinux.cfg, syslinux.cfg)
8+
# - Multiboot kernels (Xen)
9+
#
10+
# Output format: name|kexectype|kernel path[|initrd path][|append params]
11+
#
212
set -e -o pipefail
313
. /etc/functions.sh
414

initrd/bin/kexec-select-boot.sh

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,11 @@ get_menu_option() {
144144
while read option; do
145145
parse_option
146146
n=$(expr $n + 1)
147-
MENU_OPTIONS+=("$n" "$name")
147+
if [ -n "$params" ]; then
148+
MENU_OPTIONS+=("$n" "$name ($params)")
149+
else
150+
MENU_OPTIONS+=("$n" "$name")
151+
fi
148152
done <$TMP_MENU_FILE
149153

150154
whiptail_type $BG_COLOR_MAIN_MENU --title "Select your boot option" \
@@ -165,7 +169,7 @@ get_menu_option() {
165169
# because DO_WITH_DEBUG pipes stdout through tee for debug logging,
166170
# making it fully buffered — the last option would appear after the
167171
# INPUT prompt.
168-
printf '%d. %s [%s]\n' "$n" "$name" "$kernel" >"${HEADS_TTY:-/dev/stderr}"
172+
printf '%d. %s %s [%s]\n' "$n" "$name" "${params:+($params)}" "$kernel" >"${HEADS_TTY:-/dev/stderr}"
169173
done <$TMP_MENU_FILE
170174

171175
INPUT "Choose the boot option [1-$n, a to abort]:" -r option_index
@@ -202,6 +206,7 @@ confirm_menu_option() {
202206
parse_option() {
203207
name=$(echo $option | cut -d\| -f1)
204208
kernel=$(echo $option | cut -d\| -f3)
209+
params=$(echo $option | cut -d\| -f5 | sed 's/append //' | xargs)
205210
}
206211

207212
scan_options() {
@@ -212,10 +217,12 @@ scan_options() {
212217
DIE "Failed to parse any boot options"
213218
fi
214219
if [ "$unique" = 'y' ]; then
215-
sort -r $option_file | uniq >$TMP_MENU_FILE
220+
sed 's/|append \([^|]*\)---[^|]*/|append \1/g' "$option_file" | sort -r | uniq >"$TMP_MENU_FILE"
216221
else
217222
cp $option_file $TMP_MENU_FILE
218223
fi
224+
DEBUG "Parsed boot options for user selection:"
225+
cat "$TMP_MENU_FILE" | while read line; do DEBUG " Option: $line"; done
219226
}
220227

221228
save_default_option() {

0 commit comments

Comments
 (0)