fix: UEFI boot fallback — search by file when label fails

The embedded GRUB EFI config only searched by volume label ARCHIPELAGO.
Some UEFI firmware presents USB devices differently, causing the search
to fail and GRUB to stall.

Added fallbacks:
1. search --file /archipelago/auto-install.sh (known ISO file)
2. Fall back to $cmdpath (EFI partition itself)
3. Use configfile before normal for explicit config loading
4. Added search_fs_file module to grub-mkstandalone

Also added same fallback to the main ISO grub.cfg.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-03-28 23:58:42 +00:00
parent 6e356412b8
commit e9903e7b4b

View File

@ -561,9 +561,10 @@ export INSTALLER_STARTED=1
sleep 1
clear
echo ""
echo -e "\033[1;37m a r c h i p e l a g o\033[0m"
echo -e "\033[1;33m ━━━━━━━━━━━━━━━━━━━━━\033[0m"
echo -e "\033[37m automatic installer\033[0m"
echo -e "\033[38;5;208m ▄▀█ █▀▄ █▀▀ █ █ █ █▀█ █▀▀ █ ▄▀█ █▀▀ █▀█\033[0m"
echo -e "\033[38;5;208m █▀█ █▀▄ █ █▀█ █ █▀▀ ██▀ █ █▀█ █ █ █ █\033[0m"
echo -e "\033[38;5;208m ▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀\033[0m"
echo -e " \033[38;5;130mbitcoin node os\033[0m"
echo ""
BOOT_MEDIA=""
@ -687,19 +688,38 @@ mksquashfs /installer /output/filesystem.squashfs -comp xz -Xbcj x86 -noappend -
echo " [container] Building GRUB EFI image..."
cat > /tmp/grub-embed.cfg <<GRUBEMBED
insmod part_gpt
insmod part_msdos
insmod fat
insmod iso9660
insmod search
insmod search_label
insmod search_fs_file
insmod normal
insmod linux
insmod all_video
# Try label first (standard path)
search --no-floppy --set=root --label ARCHIPELAGO
# Fallback: search for a known file on the ISO
if [ -z "\$root" ]; then
search --no-floppy --set=root --file /archipelago/auto-install.sh
fi
# Fallback: try configfile from the EFI partition path
if [ -z "\$root" ]; then
set root=\$cmdpath
fi
set prefix=(\$root)/boot/grub
configfile (\$root)/boot/grub/grub.cfg
# If configfile fails, try normal
normal
GRUBEMBED
grub-mkstandalone -O x86_64-efi \
--modules="part_gpt part_msdos fat iso9660 search search_label normal linux all_video font gfxterm configfile echo cat ls test true loopback png" \
--modules="part_gpt part_msdos fat iso9660 search search_label search_fs_file normal linux all_video font gfxterm configfile echo cat ls test true loopback png" \
--locales="" \
--themes="" \
--fonts="" \
@ -1405,81 +1425,56 @@ case "$(uname -m)" in
;;
esac
# Colors (basic ANSI — works on bare-metal Linux console)
# Colors — 256-color ANSI (works on Linux fbcon console)
ORANGE=$'\033[38;5;208m'
ORANGE_DIM=$'\033[38;5;130m'
ORANGE_BRIGHT=$'\033[38;5;214m'
RED=$'\033[31m'
GREEN=$'\033[32m'
YELLOW=$'\033[1;33m'
ORANGE=$'\033[1;33m'
DIM=$'\033[37m'
CYAN=$'\033[36m'
WHITE=$'\033[1;37m'
DIM=$'\033[38;5;242m'
DIMMER=$'\033[38;5;238m'
NC=$'\033[0m'
BOLD=$'\033[1m'
# Adaptive centering
# Fixed left-margin layout (no more mixed centering)
TW=$(tput cols 2>/dev/null || echo 60)
[ "$TW" -gt 120 ] && TW=120
cc() { local s=$(echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g'); local p=$(( (TW - ${#s}) / 2 )); [ $p -lt 0 ] && p=0; printf "%*s" "$p" ""; echo -e "$1"; }
hrule() { local len=$((TW > 50 ? 50 : TW - 4)); local hr=""; for i in $(seq 1 $len); do hr="${hr}"; done; cc "${DIM}${hr}${NC}"; }
[ "$TW" -gt 100 ] && TW=100
PAD=$(( (TW - 50) / 2 ))
[ "$PAD" -lt 0 ] && PAD=0
PADS=$(printf "%*s" "$PAD" "")
box() {
local bw=$((TW > 52 ? 52 : TW - 4))
local inner=$((bw - 2))
local top="╭"; local bot="╰"
for i in $(seq 1 $inner); do top="${top}"; bot="${bot}"; done
top="${top}"; bot="${bot}"
cc "${DIM}${top}${NC}"
}
boxend() {
local bw=$((TW > 52 ? 52 : TW - 4))
local inner=$((bw - 2))
local bot="╰"
for i in $(seq 1 $inner); do bot="${bot}"; done
bot="${bot}"
cc "${DIM}${bot}${NC}"
}
boxline() {
local bw=$((TW > 52 ? 52 : TW - 4))
local inner=$((bw - 2))
local stripped=$(echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g')
local pad=$((inner - ${#stripped}))
[ $pad -lt 0 ] && pad=0
local right=""
for i in $(seq 1 $pad); do right="${right} "; done
cc "${DIM}${NC} $1${right}${DIM}${NC}"
}
p() { printf "%s%b\n" "$PADS" "$1"; }
hrule() { local hr=""; for i in $(seq 1 48); do hr="${hr}*"; done; p "${ORANGE_DIM}${hr}${NC}"; }
# TUI helpers — Claude Code-inspired status display
# Phase display
STEP=0
TOTAL_STEPS=8
step() {
STEP=$((STEP + 1))
echo ""
cc "${WHITE}[$STEP/$TOTAL_STEPS]${NC} ${DIM}$1${NC}"
p "${ORANGE}[$STEP/$TOTAL_STEPS] $1${NC}"
}
ok() { cc " ${GREEN}$1${NC}"; }
warn() { cc " ${YELLOW}$1${NC}"; }
fail() { cc " ${RED}$1${NC}"; }
ok() { p " ${ORANGE_BRIGHT}$1${NC}"; }
warn() { p " ${ORANGE}$1${NC}"; }
fail() { p " ${RED}$1${NC}"; }
spinner() {
local pid=$1 msg=$2
local frames='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'
local i=0
while kill -0 "$pid" 2>/dev/null; do
printf "\r ${DIM}%s %s${NC}" "${frames:i%10:1}" "$msg"
printf "\r%s %b%s %s%b" "$PADS" "$ORANGE" "${frames:i%10:1}" "$msg" "$NC"
i=$((i + 1))
sleep 0.1
done
printf "\r ${GREEN}✓ %s${NC}\n" "$msg"
printf "\r%s %b✓ %s%b\n" "$PADS" "$ORANGE_BRIGHT" "$msg" "$NC"
}
clear
echo ""
box
boxline ""
boxline "${WHITE}a r c h i p e l a g o${NC}"
boxline "${ORANGE}━━━━━━━━━━━━━━━━━━━━━${NC}"
boxline "${DIM}automatic installation${NC}"
boxline ""
boxend
echo -e " ${ORANGE}▄▀█ █▀▄ █▀▀ █ █ █ █▀█ █▀▀ █ ▄▀█ █▀▀ █▀█${NC}"
echo -e " ${ORANGE}█▀█ █▀▄ █ █▀█ █ █▀▀ ██▀ █ █▀█ █ █ █ █${NC}"
echo -e " ${ORANGE}▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀${NC}"
echo -e " ${ORANGE_DIM}bitcoin node os${NC}"
echo ""
# Check required tools are present (should be bundled in ISO)
@ -1582,9 +1577,9 @@ ok "$TARGET_DISK ($TARGET_SIZE)"
echo ""
hrule
echo ""
cc "${RED}all data on $TARGET_DISK will be erased${NC}"
p "${ORANGE}all data on $TARGET_DISK will be erased${NC}"
echo ""
cc "${DIM}press enter to install | ctrl+c to cancel${NC}"
p "${ORANGE_DIM} press enter to install | ctrl+c to cancel${NC}"
read -s
echo ""
@ -1631,7 +1626,7 @@ mkfs.ext4 -F -L archipelago "$ROOT_PART"
# Mount root + extract rootfs (need cryptsetup from rootfs for LUKS)
ok "Partitions created"
echo ""
cc "${DIM}Mounting filesystems...${NC}"
p " ${ORANGE_DIM}Mounting filesystems...${NC}"
mkdir -p /mnt/target
mount "$ROOT_PART" /mnt/target
mkdir -p /mnt/target/boot/efi
@ -1877,31 +1872,29 @@ if [ -t 0 ] && [ -z "$ARCHIPELAGO_WELCOMED" ]; then
done
O='\033[38;5;208m'
D='\033[38;5;242m'
OD='\033[38;5;130m'
W='\033[1;37m'
N='\033[0m'
clear
echo ""
echo -e " ${O}█▀█ █▀▄ █▀▀ █ █ █ █▀▄▀█ █▀▀ █ █▀█ █▀▀ █▀█${N}"
echo -e " ${O}█▀█ █▀▄ █ █▀█ █ █ ▀ █ ██▀ █ █▀█ █ █ █ █${N}"
echo -e " ${O}▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀${N}"
echo -e " ${D}bitcoin node os${N}"
echo ""
echo -e " ${O}▄▀█ █▀▄ █▀▀ █ █ █ █▀█ █▀▀ █ ▄▀█ █▀▀ █▀█${N}"
echo -e " ${O}█▀█ █▀▄ █ █▀█ █ █▀▀ ██▀ █ █▀█ █ █ █ █${N}"
echo -e " ${O}▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀${N}"
echo -e " ${OD}bitcoin node os${N}"
if [ -n "$IP" ]; then
echo -e " ${D}web ui${N} http://$IP"
echo -e " ${D}ssh${N} archipelago@$IP"
echo -e " ${D}password${N} archipelago (SSH) / password123 (Web)"
echo -e " ${W}web ui http://$IP${N}"
echo -e " ${W}ssh archipelago@$IP${N}"
echo -e " ${W}password archipelago (SSH) / password123 (Web)${N}"
else
echo -e " ${D}Waiting for network...${N}"
echo -e " ${OD}Waiting for network...${N}"
fi
echo ""
if [ -b /dev/mapper/archipelago-data ]; then
echo -e " ${D}storage${N} LUKS2 encrypted"
echo -e " ${OD}storage LUKS2 encrypted${N}"
fi
if systemctl is-active archipelago-kiosk.service >/dev/null 2>&1; then
echo -e " ${D}display${N} Kiosk active (Ctrl+Alt+F1 for terminal)"
echo -e " ${OD}display Kiosk active (Ctrl+Alt+F1 for terminal)${N}"
else
echo -e " ${D}display${N} Console (Ctrl+Alt+F7 for kiosk)"
echo -e " ${OD}display Console (Ctrl+Alt+F7 for kiosk)${N}"
fi
echo ""
fi
@ -2533,25 +2526,20 @@ cryptsetup close archipelago-data 2>/dev/null || true
umount /mnt/target 2>/dev/null || true
echo ""
echo -e " ${ORANGE}▄▀█ █▀▄ █▀▀ █ █ █ █▀█ █▀▀ █ ▄▀█ █▀▀ █▀█${NC}"
echo -e " ${ORANGE}█▀█ █▀▄ █ █▀█ █ █▀▀ ██▀ █ █▀█ █ █ █ █${NC}"
echo -e " ${ORANGE}▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀${NC}"
echo -e " ${ORANGE_DIM}bitcoin node os${NC}"
echo ""
box
boxline ""
boxline "${WHITE}A R C H I P E L A G O${NC}"
boxline "${GREEN}━━━━━━━━━━━━━━━━━━━━━${NC}"
boxline "${GREEN}Installation Complete${NC}"
boxline ""
boxend
p "${ORANGE_BRIGHT} ✓ Installation Complete${NC}"
echo ""
cc "${DIM}After reboot, open the Web UI from any device on your network.${NC}"
p "${ORANGE_DIM} After reboot, access from any device:${NC}"
echo ""
cc "${DIM}Web UI:${NC} ${WHITE}http://<this machine's IP>${NC}"
cc "${DIM}SSH:${NC} ${DIM}ssh archipelago@<IP>${NC}"
cc "${DIM}Password:${NC} ${DIM}archipelago${NC}"
cc "${DIM}Web Login:${NC} ${DIM}password123${NC}"
p "${ORANGE} http://<this machine's IP>${NC}"
echo ""
cc "${DIM}Pre-loaded apps (start via Web UI):${NC}"
cc "${DIM}Bitcoin Knots, LND, Home Assistant,${NC}"
cc "${DIM}BTCPay Server, Mempool, Nostr Relay${NC}"
p "${WHITE} SSH ssh archipelago@<IP>${NC}"
p "${WHITE} Password archipelago${NC}"
p "${WHITE} Web Login password123${NC}"
echo ""
hrule
echo ""
@ -2562,19 +2550,19 @@ echo 1 > /proc/sys/kernel/printk 2>/dev/null || true
cat > /tmp/archipelago-reboot.sh <<'REBOOTSCRIPT'
#!/bin/bash
# This script runs from tmpfs — safe after USB removal
TW=$(tput cols 2>/dev/null || echo 60)
[ "$TW" -gt 120 ] && TW=120
cc() { local s=$(echo -e "$1" | sed 's/\x1b\[[0-9;]*m//g'); local p=$(( (TW - ${#s}) / 2 )); [ $p -lt 0 ] && p=0; printf "%*s" "$p" ""; echo -e "$1"; }
O=$'\033[38;5;208m'
OD=$'\033[38;5;130m'
N=$'\033[0m'
cc "\033[1;33m>>> REMOVE THE USB DRIVE NOW <<<\033[0m"
echo -e " ${O}>>> REMOVE THE USB DRIVE NOW <<<${N}"
echo ""
cc "\033[37mPress Enter to reboot (or wait 30 seconds)\033[0m"
echo -e " ${OD}Press Enter to reboot (or wait 30 seconds)${N}"
# Wait for Enter or timeout
read -t 30 -s 2>/dev/null || true
echo ""
cc "\033[37mRebooting...\033[0m"
echo -e " ${OD}Rebooting...${N}"
sleep 1
reboot -f
REBOOTSCRIPT
@ -2628,8 +2616,15 @@ insmod part_msdos
insmod fat
insmod iso9660
insmod all_video
insmod search
insmod search_label
insmod search_fs_file
# Find boot media — try label first, then known file fallback
search --no-floppy --set=root --label ARCHIPELAGO
if [ -z "$root" ]; then
search --no-floppy --set=root --file /archipelago/auto-install.sh
fi
set timeout=5
set default=0