Compare commits

...
7 Commits
Author SHA1 Message Date
gandalf f61932c6c6 revert(live-usb): restore v2.10.3 verbatim GRUB EFI + label-based search
Real UEFI hardware kept landing in grub rescue / grub shell across every
v2.12.x iteration: shim swap, hardened module list, $cmdpath embed,
search --fs-uuid bake-in. None of them shipped a working real-UEFI
boot. v2.10.3 was the last tag the operator confirmed booted his
hardware in both BIOS and UEFI mode.

Drop the experimental UUID capture + the embed-cfg fallback chain.
Restore EXACTLY what v2.10.3 had:

  # embed-cfg
  search --no-floppy --label ESP --set=root
  set prefix=($root)/boot/grub
  configfile $prefix/grub.cfg

  # main grub.cfg LIVE search
  search --no-floppy --label LIVE --set=live

Validated other v2.12.x improvements stay (mass-mask LXC services,
dynamic MOTD, modesetting for VBox, kiosk --no-block) — they don't
touch the boot path.
2026-05-25 17:18:42 +02:00
gandalf ff806523b7 fix(live-usb): use baked-in UUIDs for ESP/LIVE search (label lookups fail on real HW)
Real UEFI hardware v2.12.7/v2.12.8 still landed in the grub> rescue
shell, and the user reported the same happening in legacy BIOS mode
too — both code paths share the /boot/grub/grub.cfg which begins with
`search --no-floppy --label LIVE --set=live`. If that label lookup
fails (some firmware doesn't surface FAT/ext4 labels, or the firmware
exposes the disk through a path GRUB's label table doesn't recognise)
\$live stays unset, every menuentry's `linux (\$live)/live/vmlinuz`
becomes `linux ()/live/vmlinuz`, the kernel doesn't load, GRUB
drops the operator at the rescue prompt. Same root cause as the
EFI embed-cfg's `search --label ESP` failure.

Capture the ESP + LIVE UUIDs with blkid right after mkfs.* and
substitute them into both configs:

  * grub.cfg uses `search --fs-uuid <LIVE_UUID> --set=live` as
    primary; falls back to `search --label LIVE` for firmware that
    DOES prefer labels (no-op if UUID already resolved).
  * embed-cfg uses `\$cmdpath/grub.cfg` first, then falls back to
    `search --fs-uuid <ESP_UUID>` + configfile from that root.

UUIDs are deterministic at build time, can't be shadowed by similarly
labelled partitions, and don't depend on the firmware's label
indexing — should work on any firmware that surfaces the disk to
GRUB's block IO at all.
2026-05-25 15:48:34 +02:00
gandalf dcb4ce828b fix(live-usb): EFI embed uses \$cmdpath so it doesn't depend on label search
Real UEFI hardware v2.12.7 reported landing at the grub> rescue
prompt even with the simple pre-e1a53297 embed-cfg restored. Cause:
`search --no-floppy --label ESP --set=root` failed silently on that
firmware (FAT label not recognised, predictable rename, etc.), so
\$root stayed unset, prefix was empty, configfile fell through to
the rescue shell.

Replace the embed with a single-line `configfile \$cmdpath/grub.cfg`.
\$cmdpath is set by the EFI firmware itself to the directory it
loaded BOOTX64.EFI from — for us that's (hdX,gptY)/EFI/BOOT, where
the build copies grub.cfg already. The full menu at /EFI/BOOT/grub.cfg
still does its own `search --label LIVE --set=live` to locate the
squashfs partition (unchanged), so this only changes the very first
lookup hop.

Legacy BIOS grub-install --target=i386-pc untouched — it never had
the issue.
2026-05-25 11:00:44 +02:00
gandalf a88c177372 fix(live-usb): simplify bootstrap netplan — DHCP on e* only
Real-hardware report: only the phantom `br-lan` (interfaces: [] +
static 192.168.1.1/24) showed an IP, the physical ethernet stayed
sec. The old netplan declared three separate interface groups
(en*, eth*, all-wifi with empty SSID) plus a member-less bridge with
a static address — that was enough to confuse networkd into
honouring the bridge while dropping the real DHCP request on the
floor.

Cut to the bone: one `eth-all` block matching `e*` (which covers
enpXsY, enoX, ensX, enxAABBCC and ethX), DHCP only, no static, no
bridge, no wifi-with-empty-SSID. Operators wire router-mode br-lan
or wifi later via the secubox-net-* tooling once they've decided
this is an endpoint vs a router.

For the no-DHCP fallback case, secubox-net-fallback.service is
already shipped and runs after networkd to ARP-probe common gateways
and assign a free .250 IP — that remains unchanged.

This means v2.12.6 has both a clean EFI build AND a netplan that
actually requests DHCP on the physical NIC instead of squatting on
the bridge.
2026-05-25 09:40:57 +02:00
gandalf 36612f622d revert(live-usb): restore simple GRUB EFI build (pre-e1a53297)
Real UEFI hardware boot was broken by my e1a53297 "hardening" — the
gold-plated module list (disk/usb/usbms/ahci/ata/...) and multi-stage
embed-cfg search fallback that I added to chase a VBox EFI Shell drop
turned out to break legitimate UEFI firmware too. None of those
modules exist in x86_64-efi (block IO comes from the EFI firmware
itself), and the if/else cmdpath fallback landed users in a `grub>`
rescue shell on hardware that worked fine with the original config.

Revert the EFI section to the v2.10.x layout that v2.10/v2.11 booted
cleanly on real amd64 boxes: 14 modules, single-line search, plain
grub-mkimage with no compress/no Secure Boot shim. Also drop the
shim-signed + grub-efi-amd64-signed from the host apt install — they
were only there to feed the SB swap that's now removed.

VBox EFI drops to the GRUB rescue shell with this config; that's
acceptable per operator direction ("je m'en fous de booter en legacy
sur vbox, ca marche sur real hw") — VBox testing has always used the
BIOS firmware path which still works perfectly.
2026-05-25 09:39:15 +02:00
gandalf 930a885f04 fix(kiosk): --no-block start so firstboot.sh's enable doesn't deadlock
Live USB v2.12.4 boot showed `secubox-kiosk-setup enable --x11` running
for 5+ minutes while the kiosk.service stayed `inactive (dead)`. The
synchronous `systemctl start secubox-kiosk.service` at the end of
enable_kiosk was hanging.

Root cause: secubox-kiosk.service declares
  Conflicts=getty@tty7.service
  TTYPath=/dev/tty7

systemd waits for tty7 to be released and for the conflicting unit's
JobsToStart to clear before kiosk's ExecStart can fire. When the call
comes from a process running inside the tty1 autologin shell (which
is what firstboot.sh's `secubox-kiosk-setup enable --x11` safety net
does), systemd deadlocks itself waiting on a tty operation the caller
can't unblock. The kiosk service appears to start fine when triggered
manually from SSH (proven during debug — once the stuck setup PID was
killed, `systemctl start secubox-kiosk.service` returned in <1s and
the service activated cleanly).

Fix: pass --no-block so systemctl returns as soon as the job is
queued, regardless of the tty wait. systemd then runs the start in
the background after the parent autologin shell exits, breaking the
loop. The exit-code branch is preserved for legitimate failures.
2026-05-25 08:59:28 +02:00
gandalf 24d6f28dab fix(live-usb): IP shown live in MOTD + modesetting fallback for VBox kiosk
Three regressions surfaced on the v2.12.3 live USB amd64 boot console:

1. /etc/issue + /etc/motd had `<IP>` as literal text — nothing was
   substituting at runtime, so operators saw `https://<IP>:9443`
   instead of the actual address. The bashrc splash showed real IP
   via `hostname -I` but was masked by the static MOTD on top.

   Fixes:
   * /etc/issue: switch to getty's `\4` escape (resolves to first
     IPv4 address at TTY render time).
   * /etc/motd: blank out the static file + ship the banner as
     /etc/update-motd.d/10-secubox, which pam_motd regenerates on
     every interactive login. The script substitutes the live
     `hostname -I` first address (falls back to `no-ip` while DHCP
     hasn't completed).

2. Kiosk Xorg failed under VirtualBox VMSVGA. The auto-detect picked
   the `vmware` Xorg driver, which loads vmwgfx kernel module — vmwgfx
   tries to talk to a VMware Workstation host channel that doesn't
   exist on VBox, prints "Failed to send host log message" then
   bails. The kiosk launcher then logs Xorg failure, retries 3 times,
   self-disables. Operator sees no kiosk even after running
   `secubox-kiosk-setup enable` (because the launcher disabled the
   sentinel again).

   Fix: secubox-x11-setup unconditionally picks `modesetting` on
   oracle (VirtualBox), regardless of VMSVGA vs VBoxVGA. modesetting
   uses DRM/KMS through vmwgfx but skips the broken host channel.
   Real VMware Workstation users (VM_TYPE=vmware) still get `vmware`
   via the dedicated case branch — only the VBox path changed.

Note: didn't tag yet — wait for user retest after CI rebuilds. If the
boot console comes up with the right IP + the kiosk paints pixels, we
tag v2.12.4 next.
2026-05-25 08:10:28 +02:00
2 changed files with 75 additions and 182 deletions
+66 -180
View File
@@ -141,8 +141,7 @@ fi
# Required tools
log "Checking dependencies..."
apt-get install -y -qq debootstrap squashfs-tools \
grub-efi-amd64-bin grub-efi-amd64-signed grub-pc-bin shim-signed \
apt-get install -y -qq debootstrap squashfs-tools grub-efi-amd64-bin grub-pc-bin \
xorriso mtools dosfstools parted e2fsprogs live-boot 2>/dev/null || true
for cmd in debootstrap parted mkfs.fat mkfs.ext4 mksquashfs grub-mkimage; do
@@ -781,96 +780,37 @@ else
# Uses two separate match patterns to avoid conflicts when multiple interfaces match
# secubox-net-detect.service will generate proper config at first boot
cat > "${ROOTFS}/etc/netplan/00-secubox.yaml" <<'NETPLAN'
# /etc/netplan/00-secubox.yaml — SecuBox Live USB (Bare Metal) — Bootstrap Config
# This is a minimal bootstrap configuration for real AMD64 hardware.
# secubox-net-detect.service will generate the proper config at first boot.
# /etc/netplan/00-secubox.yaml — SecuBox Live USB bootstrap.
#
# Strategy: Enable DHCP on ALL detected Ethernet interfaces initially.
# secubox-net-detect will refine this to router mode (WAN + br-lan).
# DHCP on every ethernet interface. That's it. Operators wire WiFi
# and router-mode br-lan later via secubox-net-* tools.
#
# Earlier versions baked an empty `br-lan` bridge with a static
# 192.168.1.1/24 address into the bootstrap. On bare-metal real
# hardware the physical NIC went silent and only the phantom br-lan
# showed an IP — networkd was honouring the static bridge but
# something (predictable rename? secubox-net-detect leftover?)
# stopped the DHCP request reaching the real cable. Stripping the
# bridge + the wifi-with-empty-SSID block restores classic DHCP.
# secubox-net-detect.service (disabled by default, no .wants/ link)
# can still be run by hand once the operator has decided whether
# this box is a router vs an endpoint.
network:
version: 2
renderer: networkd
ethernets:
# Bootstrap: Enable DHCP on all ethernet interfaces for initial connectivity
# This ensures we get an IP regardless of interface naming (eno1, enp2s0, etc.)
# After first boot, secubox-net-detect rewrites this with proper WAN/LAN split.
# Match modern interface patterns (enp*, eno*, ens*, enx*).
# WAN fallback (closes #370): static 192.168.1.55/24 + gw
# 192.168.1.254 carried alongside DHCP. systemd-networkd accepts
# both — DHCP routes get the low metric (100), the static gets
# metric 1000, so DHCP wins when available and the static keeps
# the appliance reachable when DHCP fails. Caveat: if multiple
# interfaces match, both will try to claim 192.168.1.55 and one
# will fail with address-in-use; bare-metal boxes typically have
# one primary NIC, and multi-NIC operators run net-detect to
# refine the layout anyway.
eth-dhcp:
# Match everything that *looks* like ethernet — covers enpXsY /
# enoX / ensX / enxAABBCC (USB ethernet) / ethX. All get DHCP.
eth-all:
match:
name: "en*"
name: "e*"
dhcp4: true
dhcp6: false
dhcp4-overrides:
use-dns: true
use-routes: true
route-metric: 100
optional: true
# Match legacy interface patterns (eth0, eth1, etc.)
eth-legacy:
match:
name: "eth*"
dhcp4: true
dhcp6: false
dhcp4-overrides:
use-dns: true
use-routes: true
route-metric: 200
optional: true
# No-IP fallback is handled by secubox-net-fallback.service (ARP-probes
# common gateways, picks a free .250 IP in the discovered subnet). My
# earlier v2.12.1 attempt to bake `addresses: [192.168.1.55/24]` into
# both eth-dhcp and eth-legacy created an address-in-use collision when
# both matched the same NIC and broke netplan apply entirely on bare
# metal + VBox — boot console showed no IP at all (regressed even DHCP).
# Keep the netplan DHCP-only; trust net-fallback for the fallback path.
wifis:
# All WiFi interfaces (wlp*, wlan*, wl*)
all-wifi:
match:
name: "wl*"
dhcp4: true
dhcp4-overrides:
route-metric: 300
use-dns: true
use-routes: true
optional: true
access-points:
# Open networks (fallback)
"": {}
bridges:
# br-lan: Pre-defined but empty - secubox-net-detect populates interfaces
br-lan:
interfaces: []
addresses:
- 192.168.1.1/24
dhcp4: false
optional: true
parameters:
stp: false
forward-delay: 0
# Note: At first boot, secubox-net-detect.service will:
# 1. Detect board type (x64-baremetal) and available interfaces
# 2. Determine WAN (first interface with link/DHCP response)
# 3. Assign remaining interfaces to br-lan
# 4. Rewrite this file with explicit interface names
#
# To force re-detection: rm /var/lib/secubox/.net-configured && reboot
NETPLAN
chmod 600 "${ROOTFS}/etc/netplan/00-secubox.yaml"
fi # end static-IP branch
@@ -2234,17 +2174,17 @@ DRIVER="modesetting" # Safe default
case "$VM_TYPE" in
oracle)
# VirtualBox - check graphics controller type
if echo "$GPU_INFO" | grep -qi "SVGA\|VMware"; then
# VMSVGA controller (default in VBox 6+) - use vmware driver
DRIVER="vmware"
log "VirtualBox VMSVGA detected → vmware driver"
else
# VBoxVGA or VBoxSVGA - modesetting works
DRIVER="modesetting"
log "VirtualBox VBoxVGA detected → modesetting driver"
fi
# Load VirtualBox kernel modules
# VirtualBox: use modesetting unconditionally. The previous
# "vmware" branch for VMSVGA controllers triggered vmwgfx kernel
# ERRORs ("Failed to send host log message") because vmwgfx
# expects a real VMware Workstation host — on VBox it half-loads
# then fails on the Xorg side, kiosk launcher gives up after 3
# retries and self-disables. modesetting talks DRM/KMS through
# the same vmwgfx kmod but ignores the broken host channel and
# actually paints pixels.
DRIVER="modesetting"
log "VirtualBox detected → modesetting driver (works for both VMSVGA + VBoxVGA)"
# Load VirtualBox kernel modules so Guest Additions integration works
modprobe vboxguest 2>/dev/null || true
modprobe vboxvideo 2>/dev/null || true
;;
@@ -3012,14 +2952,25 @@ printf '%b' "\e[38;5;29m
\e[38;5;242m Build: ${BUILD_TIMESTAMP}\e[0m
\e[38;5;250m 🔐 Default: \e[38;5;214mroot\e[38;5;250m / \e[38;5;214msecubox\e[0m
\e[38;5;250m 🌐 Web UI: \e[38;5;45mhttps://<IP>:9443\e[0m
\e[38;5;250m 🌐 Web UI: \e[38;5;45mhttps://\\4:9443\e[0m
\e[38;5;250m 📡 SSH: \e[38;5;45mport 22\e[0m
\e[38;5;242m─────────────────────────────────────────────────────────────\e[0m
" > "${ROOTFS}/etc/issue"
# Post-login MOTD - use printf %b for real escape sequences
# Post-login MOTD via update-motd.d so the IP can be substituted live
# at login time (pam_motd runs /etc/update-motd.d/* on every interactive
# session). Static /etc/motd kept blank — without it, pam_motd printed
# the literal `<IP>:9443` because nothing was substituting the placeholder.
# Operators see the box's actual IPv4 (or `no-ip` when DHCP hasn't fired).
: > "${ROOTFS}/etc/motd"
mkdir -p "${ROOTFS}/etc/update-motd.d"
cat > "${ROOTFS}/etc/update-motd.d/10-secubox" <<MOTD_DYN
#!/bin/sh
# Generated by build-live-usb.sh — dynamic MOTD with live IP.
ip=\$(hostname -I 2>/dev/null | awk '{print \$1}')
[ -z "\$ip" ] && ip="no-ip"
printf '%b' "\e[38;5;214m
╔═══════════════════════════════════════════════════════════════╗
║\e[38;5;45m ███████╗███████╗ ██████╗██╗ ██╗██████╗ ██████╗ ██╗ ██╗ \e[38;5;214m║
@@ -3033,13 +2984,15 @@ printf '%b' "\e[38;5;214m
\e[38;5;242m Build: ${BUILD_TIMESTAMP}\e[0m
\e[38;5;250m 🌐 Web UI: \e[38;5;45mhttps://<IP>:9443\e[0m
\e[38;5;250m 🌐 Web UI: \e[38;5;45mhttps://\${ip}:9443\e[0m
\e[38;5;250m 🔐 Credentials: \e[38;5;214mroot\e[38;5;250m / \e[38;5;214msecubox\e[0m
\e[38;5;250m 📖 Docs: \e[38;5;45mhttps://secubox.in/docs\e[0m
\e[38;5;242m Type \e[38;5;82msecubox-status\e[38;5;242m for system overview\e[0m
" > "${ROOTFS}/etc/motd"
"
MOTD_DYN
chmod +x "${ROOTFS}/etc/update-motd.d/10-secubox"
# Dynamic status script for interactive use
cat > "${ROOTFS}/usr/bin/secubox-status" <<'STATUS_SCRIPT'
@@ -3568,6 +3521,7 @@ else
fi
fi
# Mount for file copy
MNT="${WORK_DIR}/mnt"
mkdir -p "${MNT}/esp" "${MNT}/live"
@@ -3735,109 +3689,41 @@ fi
cp "${MNT}/esp/boot/grub/grub.cfg" "${MNT}/esp/EFI/BOOT/grub.cfg"
# ── Build GRUB EFI ────────────────────────────────────────────────────────
# Module set tuned for booting from a USB stick / VM disk under both
# OVMF (VirtualBox + qemu) and real UEFI firmware. The list MUST include
# `disk`, `usb`, `usbms` so the firmware can hand off block IO; without
# them OVMF drops to the EFI Shell PXE prompt instead of loading GRUB
# (this was the v2.12.0/v2.12.1 regression — see issue #382 followup).
GRUB_MODS="part_gpt part_msdos fat ext2 ntfs iso9660 normal linux boot \
configfile loopback chain efi_gop efi_uga ls cat echo test help \
search search_label search_fs_uuid search_fs_file \
gfxterm gfxterm_background all_video gzio png jpeg font \
fat exfat read sleep reboot halt true \
disk usb usbms ahci ata loadenv minicmd terminal"
# Build GRUB EFI — verbatim from v2.10.3 (last known-good real UEFI
# boot). Do NOT optimise this block: every "improvement" attempted in
# the v2.12.x series (Secure Boot shim, $cmdpath, search --fs-uuid,
# extra modules, multi-stage fallbacks) broke at least one piece of
# hardware. Stay simple, stay shipped.
GRUB_MODS="part_gpt part_msdos fat ext2 normal linux boot configfile loopback chain efi_gop efi_uga ls search search_label gfxterm all_video"
cat > "${WORK_DIR}/grub-embed.cfg" <<'EMBEDCFG'
# Fallback chain — label first (set by mkfs.fat -n ESP), then UUID,
# then EFI's $cmdpath (the directory the .EFI was loaded from). Last
# resort: hardcode the relative prefix so configfile can still find the
# menu even if no var resolution worked.
search --no-floppy --label ESP --set=root --no-floppy
if [ -z "$root" ]; then
search --no-floppy --label LIVE --set=root --no-floppy
fi
if [ -n "$root" ]; then
set prefix=($root)/boot/grub
else
set prefix=($cmdpath)/../../boot/grub
fi
search --no-floppy --label ESP --set=root
set prefix=($root)/boot/grub
configfile $prefix/grub.cfg
EMBEDCFG
# Build the EFI binary. Bail loudly if grub-mkimage fails — silent
# failure leaves the ESP without BOOTX64.EFI and the box drops to PXE
# at first boot, which is exactly the bug we just hit.
if ! grub-mkimage -o "${MNT}/esp/EFI/BOOT/BOOTX64.EFI" \
-O x86_64-efi \
-c "${WORK_DIR}/grub-embed.cfg" \
-p /boot/grub \
--compress=xz \
${GRUB_MODS}; then
err "grub-mkimage failed — the image will not boot under EFI"
fi
[[ -s "${MNT}/esp/EFI/BOOT/BOOTX64.EFI" ]] || err "BOOTX64.EFI missing or empty after grub-mkimage"
EFI_SIZE=$(stat -c%s "${MNT}/esp/EFI/BOOT/BOOTX64.EFI")
ok "BOOTX64.EFI built (${EFI_SIZE} bytes)"
grub-mkimage -o "${MNT}/esp/EFI/BOOT/BOOTX64.EFI" \
-O x86_64-efi \
-c "${WORK_DIR}/grub-embed.cfg" \
-p /boot/grub \
${GRUB_MODS}
# Mirror to /EFI/BOOT/grubx64.efi (some firmware looks for this name)
# and to /EFI/secubox/grubx64.efi (lets `efibootmgr` register a named
# entry once the live system is running, without clobbering /BOOT).
cp "${MNT}/esp/EFI/BOOT/BOOTX64.EFI" "${MNT}/esp/EFI/BOOT/grubx64.efi"
mkdir -p "${MNT}/esp/EFI/secubox"
cp "${MNT}/esp/EFI/BOOT/BOOTX64.EFI" "${MNT}/esp/EFI/secubox/grubx64.efi"
# Secure Boot assets: ship shim + signed grub alongside the unsigned
# BOOTX64.EFI, but DON'T swap shim into BOOTX64.EFI.
#
# Rationale: shim requires Microsoft-signed keys enrolled in the firmware
# to chainload grubx64.efi. OVMF / VirtualBox EFI ship without those keys
# and Secure Boot off — shim refuses to hand off and the boot drops to
# the EFI Shell / PXE. By keeping the unsigned grub-mkimage as
# BOOTX64.EFI, every firmware (OVMF, VBox, real laptops with SB off)
# loads grub directly.
#
# For Secure Boot ON systems: the firmware boot menu / efibootmgr can
# point at /EFI/BOOT/shimx64.efi explicitly. Users opting into SB do
# this once when registering the boot entry.
SHIM_SRC="/usr/lib/shim/shimx64.efi.signed"
GRUB_SIGNED_SRC="/usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed"
if [[ -f "$SHIM_SRC" && -f "$GRUB_SIGNED_SRC" ]]; then
cp "$SHIM_SRC" "${MNT}/esp/EFI/BOOT/shimx64.efi"
cp "$GRUB_SIGNED_SRC" "${MNT}/esp/EFI/BOOT/grubx64-signed.efi"
cp "$GRUB_SIGNED_SRC" "${MNT}/esp/EFI/secubox/grubx64-signed.efi"
ok "Secure Boot assets shipped (shimx64.efi + grubx64-signed.efi alongside unsigned default)"
else
log "Secure Boot assets not present — image boots fine without SB"
log " (to enable: apt-get install shim-signed grub-efi-amd64-signed)"
fi
# EFI shell fallback — some OVMF builds drop into the shell on first
# boot if no BootOrder is set. startup.nsh auto-runs the bootloader.
# Add startup.nsh for EFI shell auto-boot (VirtualBox/OVMF compatibility)
cat > "${MNT}/esp/startup.nsh" <<'STARTUPNSH'
@echo -off
echo "SecuBox Live — handing off to BOOTX64.EFI..."
\EFI\BOOT\BOOTX64.EFI
STARTUPNSH
# Copy GRUB modules — only the bits BOOTX64.EFI's embedded prefix
# might want to load on demand. Failure is non-fatal: we statically
# linked everything we need above, this is belt-and-suspenders.
# Copy GRUB modules
cp /usr/lib/grub/x86_64-efi/*.mod "${MNT}/esp/boot/grub/x86_64-efi/" 2>/dev/null || true
# ── BIOS GRUB ──────────────────────────────────────────────────────────────
# Fail loudly here too — BIOS-only systems (older laptops, VBox legacy
# default) silently won't boot if i386-pc grub isn't installed in the
# BIOS-boot partition (p1, set bios_grub on).
if ! grub-install --target=i386-pc \
--boot-directory="${MNT}/esp/boot" \
--recheck "${LOOP}" 2>&1 | tee "${WORK_DIR}/grub-install-bios.log"; then
cat "${WORK_DIR}/grub-install-bios.log"
err "BIOS grub-install failed — image will not boot on legacy systems"
fi
# Install BIOS GRUB
grub-install --target=i386-pc --boot-directory="${MNT}/esp/boot" --recheck "${LOOP}" 2>/dev/null || warn "BIOS GRUB failed"
cp /usr/lib/grub/i386-pc/*.mod "${MNT}/esp/boot/grub/i386-pc/" 2>/dev/null || true
ok "GRUB installed (UEFI BOOTX64.EFI + BIOS i386-pc, dual-boot ready)"
ok "GRUB installed (UEFI + BIOS)"
# Persistence
if [[ $INCLUDE_PERSISTENCE -eq 1 ]] && [[ -b "${LOOP}p4" ]]; then
+9 -2
View File
@@ -377,8 +377,15 @@ enable_kiosk() {
# Enable kiosk service
systemctl enable secubox-kiosk.service
# Start kiosk
systemctl start secubox-kiosk.service || log "Kiosk will start on next boot"
# Start kiosk in the background so this caller doesn't block. The
# service has Conflicts=getty@tty7.service + TTYPath=/dev/tty7 and
# systemd will sometimes wait for /dev/tty7 to release / sync up,
# which deadlocks a synchronous `systemctl start` invoked from
# firstboot.sh (firstboot runs from inside the autologin shell on
# tty1 itself — observed live USB hang where kiosk-setup ran for
# 5+ minutes and the service stayed `inactive (dead)`).
systemctl --no-block start secubox-kiosk.service \
|| log "Kiosk will start on next boot"
ok "Kiosk mode enabled (${mode})"
log "SecuBox WebUI will display on the connected screen"