Compare commits

...

10 Commits

Author SHA1 Message Date
Mikhail Chusavitin
f3962422c8 Fix lb config option name for squashfs compression options
--chroot-squashfs-options is not a valid lb_config option; the correct
name is --chroot-squashfs-compression-options. Without this fix lb config
aborts immediately, so the -no-xattrs flag (which prevents the
"unable to read xattr id index table" boot failure) was never applied.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 14:03:41 +03:00
Mikhail Chusavitin
ee36e3c711 Strip xattrs from squashfs to fix boot failure
Kernel squashfs driver fails with "unable to read xattr id index table"
when the squashfs contains POSIX ACL xattrs (system.posix_acl_*) written
by mksquashfs as unrecognised entries. This caused every built ISO to
drop to an initramfs shell at boot.

Add -no-xattrs to mksquashfs options so xattrs are stripped at build
time. xattrs are not needed in a live read-only rootfs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 13:56:26 +03:00
Mikhail Chusavitin
cca3b21d35 Revert squashfs layer split — live-boot cannot mount partial rootfs
split_live_squashfs_layers moved /usr out of filesystem.squashfs into a
separate 10-usr.squashfs, leaving a rootfs skeleton that live-boot
(1:20230131+deb12u1) cannot mount: the initramfs panics with
"Can not mount /dev/loop0 ... filesystem.squashfs".

live-boot in bookworm expects a single self-contained filesystem.squashfs.
Revert to the standard single-squashfs layout and remove the dead
multi-squashfs guard in needs_full_build().

The split_live_squashfs_layers function is kept for future reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 11:14:10 +03:00
Mikhail Chusavitin
75c33e073e Fix split_live_squashfs_layers crash under POSIX sh (dash)
trap RETURN is a bash extension not supported by /bin/sh on Debian.
With set -e active the unsupported trap call exited the build immediately
after lb build, before bootloader sync and ISO copy steps ran.

Remove both trap RETURN lines — explicit rm -rf at the end of the
function is sufficient for cleanup on the happy path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 09:52:31 +03:00
7b4bcc745a Split live rootfs into smaller squashfs layers 2026-05-03 23:15:22 +03:00
42774d44a6 Restore post-build GRUB and isolinux sync 2026-05-03 21:49:54 +03:00
5dc022ddf8 Drop post-build EFI bootloader patching 2026-05-03 21:22:53 +03:00
6623e159f5 Grow EFI image before syncing GRUB theme assets 2026-05-03 21:18:37 +03:00
bbd6d009f8 Avoid EFI image overflow when syncing GRUB theme 2026-05-03 21:16:36 +03:00
6c2b188ec9 Add no-GUI boot mode and quieter boot diagnostics 2026-05-03 21:14:45 +03:00
18 changed files with 428 additions and 159 deletions

View File

@@ -24,6 +24,8 @@ var supportBundleServices = []string{
"bee-selfheal.service", "bee-selfheal.service",
"bee-selfheal.timer", "bee-selfheal.timer",
"bee-sshsetup.service", "bee-sshsetup.service",
"display-manager.service",
"lightdm.service",
"nvidia-dcgm.service", "nvidia-dcgm.service",
"nvidia-fabricmanager.service", "nvidia-fabricmanager.service",
} }
@@ -44,12 +46,128 @@ var supportBundleCommands = []struct {
{name: "system/mount.txt", cmd: []string{"mount"}}, {name: "system/mount.txt", cmd: []string{"mount"}},
{name: "system/df-h.txt", cmd: []string{"df", "-h"}}, {name: "system/df-h.txt", cmd: []string{"df", "-h"}},
{name: "system/dmesg.txt", cmd: []string{"dmesg"}}, {name: "system/dmesg.txt", cmd: []string{"dmesg"}},
{name: "system/dmesg-gui-video-input.txt", cmd: []string{"sh", "-c", `
if command -v dmesg >/dev/null 2>&1; then
dmesg | grep -iE 'nvidia|drm|fb|framebuffer|vesa|efi|lightdm|Xorg|input|hid|usb|keyboard|mouse|virtual keyboard|virtual mouse|ami|aspeed|ast' || echo "no GUI/video/input kernel messages found"
else
echo "dmesg not found"
fi
`}},
{name: "system/kernel-aer-nvidia.txt", cmd: []string{"sh", "-c", ` {name: "system/kernel-aer-nvidia.txt", cmd: []string{"sh", "-c", `
if command -v dmesg >/dev/null 2>&1; then if command -v dmesg >/dev/null 2>&1; then
dmesg | grep -iE 'AER|NVRM|Xid|pcieport|nvidia' || echo "no AER/NVRM/Xid kernel messages found" dmesg | grep -iE 'AER|NVRM|Xid|pcieport|nvidia' || echo "no AER/NVRM/Xid kernel messages found"
else else
echo "dmesg not found" echo "dmesg not found"
fi fi
`}},
{name: "system/loginctl-sessions.txt", cmd: []string{"sh", "-c", `
if command -v loginctl >/dev/null 2>&1; then
loginctl list-sessions 2>&1 || true
else
echo "loginctl not found"
fi
`}},
{name: "system/loginctl-seats.txt", cmd: []string{"sh", "-c", `
if command -v loginctl >/dev/null 2>&1; then
loginctl list-seats 2>&1 || true
echo
for seat in $(loginctl list-seats --no-legend 2>/dev/null | awk '{print $1}'); do
echo "=== $seat ==="
loginctl seat-status "$seat" 2>&1 || true
echo
done
else
echo "loginctl not found"
fi
`}},
{name: "system/ps-gui.txt", cmd: []string{"sh", "-c", `
ps -ef | grep -iE 'lightdm|Xorg|X$|openbox|chromium|chrome|xinit|xsession' | grep -v grep || echo "no GUI processes found"
`}},
{name: "system/lspci-video-vv.txt", cmd: []string{"sh", "-c", `
if ! command -v lspci >/dev/null 2>&1; then
echo "lspci not found"
exit 0
fi
found=0
for dev in $(lspci -Dn | awk '$2 ~ /^03(00|02):$/ {print $1}'); do
found=1
echo "=== $dev ==="
lspci -s "$dev" -vv 2>&1 || true
echo
done
if [ "$found" -eq 0 ]; then
echo "no display-class PCI devices found"
fi
`}},
{name: "system/proc-fb.txt", cmd: []string{"cat", "/proc/fb"}},
{name: "system/drm-cards.txt", cmd: []string{"sh", "-c", `
if [ -d /sys/class/drm ]; then
for path in /sys/class/drm/card*; do
[ -e "$path" ] || continue
card=$(basename "$path")
echo "=== $card ==="
for f in status enabled dpms modes; do
[ -r "$path/$f" ] && printf " %-8s %s\n" "$f" "$(cat "$path/$f" 2>/dev/null)"
done
device=$(readlink -f "$path/device" 2>/dev/null || true)
[ -n "$device" ] && echo " device ${device##*/}"
echo
done
else
echo "/sys/class/drm not present"
fi
`}},
{name: "system/input-devices.txt", cmd: []string{"sh", "-c", `
if [ -r /proc/bus/input/devices ]; then
cat /proc/bus/input/devices
else
echo "/proc/bus/input/devices not readable"
fi
`}},
{name: "system/udevadm-input.txt", cmd: []string{"sh", "-c", `
if ! command -v udevadm >/dev/null 2>&1; then
echo "udevadm not found"
exit 0
fi
found=0
for dev in /dev/input/event*; do
[ -e "$dev" ] || continue
found=1
echo "=== $dev ==="
udevadm info --query=all --name="$dev" 2>&1 || true
echo
done
if [ "$found" -eq 0 ]; then
echo "no /dev/input/event* devices found"
fi
`}},
{name: "system/xinput-list.txt", cmd: []string{"sh", "-c", `
if command -v xinput >/dev/null 2>&1; then
DISPLAY=:0 xinput --list 2>&1 || true
else
echo "xinput not found"
fi
`}},
{name: "system/libinput-list-devices.txt", cmd: []string{"sh", "-c", `
if command -v libinput >/dev/null 2>&1; then
libinput list-devices 2>&1 || true
else
echo "libinput not found"
fi
`}},
{name: "system/systemctl-gui-units.txt", cmd: []string{"sh", "-c", `
if ! command -v systemctl >/dev/null 2>&1; then
echo "systemctl not found"
exit 0
fi
echo "=== unit files ==="
systemctl list-unit-files --no-pager --all 'lightdm*' 'display-manager*' 2>&1 || true
echo
echo "=== active units ==="
systemctl list-units --no-pager --all 'lightdm*' 'display-manager*' 2>&1 || true
echo
echo "=== failed units ==="
systemctl --failed --no-pager 2>&1 | grep -iE 'lightdm|display-manager|Xorg' || echo "no failed GUI units"
`}}, `}},
{name: "system/nvidia-smi-q.txt", cmd: []string{"nvidia-smi", "-q"}}, {name: "system/nvidia-smi-q.txt", cmd: []string{"nvidia-smi", "-q"}},
{name: "system/nvidia-smi-topo.txt", cmd: []string{"sh", "-c", ` {name: "system/nvidia-smi-topo.txt", cmd: []string{"sh", "-c", `
@@ -236,6 +354,13 @@ var supportBundleOptionalFiles = []struct {
}{ }{
{name: "system/kern.log", src: "/var/log/kern.log"}, {name: "system/kern.log", src: "/var/log/kern.log"},
{name: "system/syslog.txt", src: "/var/log/syslog"}, {name: "system/syslog.txt", src: "/var/log/syslog"},
{name: "system/Xorg.0.log", src: "/var/log/Xorg.0.log"},
{name: "system/Xorg.0.log.old", src: "/var/log/Xorg.0.log.old"},
{name: "system/lightdm/lightdm.log", src: "/var/log/lightdm/lightdm.log"},
{name: "system/lightdm/x-0.log", src: "/var/log/lightdm/x-0.log"},
{name: "system/lightdm/x-0-greeter.log", src: "/var/log/lightdm/x-0-greeter.log"},
{name: "system/home-bee-xsession-errors.log", src: "/home/bee/.xsession-errors"},
{name: "system/home-bee-chromium-debug.log", src: "/tmp/bee-chrome/chrome_debug.log"},
{name: "system/fabricmanager.log", src: "/var/log/fabricmanager.log"}, {name: "system/fabricmanager.log", src: "/var/log/fabricmanager.log"},
{name: "system/nvlsm.log", src: "/var/log/nvlsm.log"}, {name: "system/nvlsm.log", src: "/var/log/nvlsm.log"},
{name: "system/fabricmanager/fabricmanager.log", src: "/var/log/fabricmanager/fabricmanager.log"}, {name: "system/fabricmanager/fabricmanager.log", src: "/var/log/fabricmanager/fabricmanager.log"},

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
) )
@@ -18,7 +19,7 @@ type InstallDisk struct {
MountedParts []string // partition mount points currently active MountedParts []string // partition mount points currently active
} }
const squashfsPath = "/run/live/medium/live/filesystem.squashfs" const squashfsGlob = "/run/live/medium/live/*.squashfs"
// ListInstallDisks returns block devices suitable for installation. // ListInstallDisks returns block devices suitable for installation.
// Excludes the current live boot medium but includes USB drives. // Excludes the current live boot medium but includes USB drives.
@@ -176,11 +177,22 @@ func inferLiveBootKind(fsType, source, deviceType, transport string) string {
// squashfs size × 1.5 to allow for extracted filesystem and bootloader. // squashfs size × 1.5 to allow for extracted filesystem and bootloader.
// Returns 0 if the squashfs is not available (non-live environment). // Returns 0 if the squashfs is not available (non-live environment).
func MinInstallBytes() int64 { func MinInstallBytes() int64 {
fi, err := os.Stat(squashfsPath) files, err := filepath.Glob(squashfsGlob)
if err != nil { if err != nil || len(files) == 0 {
return 0 return 0
} }
return fi.Size() * 3 / 2 var total int64
for _, path := range files {
fi, statErr := os.Stat(path)
if statErr != nil {
continue
}
total += fi.Size()
}
if total == 0 {
return 0
}
return total * 3 / 2
} }
// toramActive returns true when the live system was booted with toram. // toramActive returns true when the live system was booted with toram.
@@ -222,12 +234,10 @@ func DiskWarnings(d InstallDisk) []string {
humanBytes(min), humanBytes(d.SizeBytes))) humanBytes(min), humanBytes(d.SizeBytes)))
} }
if toramActive() { if toramActive() {
sqFi, err := os.Stat(squashfsPath) free := freeMemBytes()
if err == nil { min := MinInstallBytes()
free := freeMemBytes() if free > 0 && min > 0 && free < (min*4/3) {
if free > 0 && free < sqFi.Size()*2 { w = append(w, "toram mode — low RAM, extraction may be slow or fail")
w = append(w, "toram mode — low RAM, extraction may be slow or fail")
}
} }
} }
return w return w

View File

@@ -1323,8 +1323,20 @@ td:first-child{color:#718096;width:55%}
<script> <script>
(function(){ (function(){
var gone = false; var gone = false;
var pollStarted = false;
var fallbackOpenTimer = null;
var AUTO_OPEN_DELAY_MS = 15000;
function go(){ if(!gone){gone=true;window.location.replace('/');} } function go(){ if(!gone){gone=true;window.location.replace('/');} }
function scheduleFallbackOpen(){
if(fallbackOpenTimer!==null) return;
fallbackOpenTimer=setTimeout(function(){
document.getElementById('spin').className='spinner hidden';
document.getElementById('st').textContent='Startup checks are taking too long — opening app...';
go();
},AUTO_OPEN_DELAY_MS);
}
function icon(s){ function icon(s){
if(s==='active') return '<span class="ok">&#9679; active</span>'; if(s==='active') return '<span class="ok">&#9679; active</span>';
if(s==='failed') return '<span class="fail">&#10005; failed</span>'; if(s==='failed') return '<span class="fail">&#10005; failed</span>';
@@ -1356,6 +1368,7 @@ function pollServices(){
tbl.innerHTML=html; tbl.innerHTML=html;
if(allSettled(svcs)){ if(allSettled(svcs)){
clearInterval(pollTimer); clearInterval(pollTimer);
if(fallbackOpenTimer!==null) clearTimeout(fallbackOpenTimer);
document.getElementById('spin').className='spinner hidden'; document.getElementById('spin').className='spinner hidden';
document.getElementById('st').textContent='Ready \u2014 opening...'; document.getElementById('st').textContent='Ready \u2014 opening...';
setTimeout(go,800); setTimeout(go,800);
@@ -1370,8 +1383,12 @@ function probe(){
if(r.ok){ if(r.ok){
document.getElementById('st').textContent='bee-web running \u2014 checking services...'; document.getElementById('st').textContent='bee-web running \u2014 checking services...';
document.getElementById('btn').style.display=''; document.getElementById('btn').style.display='';
pollServices(); scheduleFallbackOpen();
pollTimer=setInterval(pollServices,1500); if(!pollStarted){
pollStarted=true;
pollServices();
pollTimer=setInterval(pollServices,1500);
}
} else { } else {
document.getElementById('st').textContent='bee-web starting (status '+r.status+')...'; document.getElementById('st').textContent='bee-web starting (status '+r.status+')...';
setTimeout(probe,500); setTimeout(probe,500);

View File

@@ -604,6 +604,25 @@ func TestReadyIsOKWhenAuditPathIsUnset(t *testing.T) {
} }
} }
func TestLoadingPageHasFallbackAutoOpen(t *testing.T) {
handler := NewHandler(HandlerOptions{})
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/loading", nil))
if rec.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
}
body := rec.Body.String()
for _, needle := range []string{
`var AUTO_OPEN_DELAY_MS = 15000;`,
`function scheduleFallbackOpen(){`,
`Startup checks are taking too long — opening app...`,
} {
if !strings.Contains(body, needle) {
t.Fatalf("loading page missing %q: %s", needle, body)
}
}
}
func TestAuditPageRendersViewerFrameAndActions(t *testing.T) { func TestAuditPageRendersViewerFrameAndActions(t *testing.T) {
dir := t.TempDir() dir := t.TempDir()
path := filepath.Join(dir, "audit.json") path := filepath.Join(dir, "audit.json")

View File

@@ -42,4 +42,5 @@ lb config noauto \
--debootstrap-options "--include=ca-certificates" \ --debootstrap-options "--include=ca-certificates" \
--apt-recommends false \ --apt-recommends false \
--chroot-squashfs-compression-type zstd \ --chroot-squashfs-compression-type zstd \
--chroot-squashfs-compression-options "-no-xattrs" \
"${@}" "${@}"

View File

@@ -516,12 +516,12 @@ validate_iso_live_boot_entries() {
exit 1 exit 1
fi fi
grep -q 'menuentry "EASY-BEE"' "$grub_cfg" || { grep -q 'menuentry "EASY-BEE v' "$grub_cfg" || {
echo "ERROR: GRUB default EASY-BEE entry is missing" >&2 echo "ERROR: GRUB default EASY-BEE entry is missing" >&2
rm -f "$grub_cfg" "$isolinux_cfg" rm -f "$grub_cfg" "$isolinux_cfg"
exit 1 exit 1
} }
grep -q 'menuentry "EASY-BEE -- load to RAM (toram)"' "$grub_cfg" || { grep -q 'menuentry "EASY-BEE v.* -- load to RAM (toram)"' "$grub_cfg" || {
echo "ERROR: GRUB toram entry is missing" >&2 echo "ERROR: GRUB toram entry is missing" >&2
rm -f "$grub_cfg" "$isolinux_cfg" rm -f "$grub_cfg" "$isolinux_cfg"
exit 1 exit 1
@@ -562,40 +562,38 @@ validate_iso_live_boot_entries() {
echo "=== live boot validation OK ===" echo "=== live boot validation OK ==="
} }
validate_iso_grub_theme_assets() { validate_iso_grub_assets() {
iso_path="$1" iso_path="$1"
echo "=== validating GRUB theme assets in ISO ===" echo "=== validating GRUB assets in ISO ==="
[ -f "$iso_path" ] || { [ -f "$iso_path" ] || {
echo "ERROR: ISO not found for GRUB theme validation: $iso_path" >&2 echo "ERROR: ISO not found for GRUB asset validation: $iso_path" >&2
exit 1 exit 1
} }
require_iso_reader "$iso_path" >/dev/null 2>&1 || { require_iso_reader "$iso_path" >/dev/null 2>&1 || {
echo "ERROR: ISO reader unavailable for GRUB theme validation" >&2 echo "ERROR: ISO reader unavailable for GRUB asset validation" >&2
exit 1 exit 1
} }
iso_files="$(mktemp)" iso_files="$(mktemp)"
iso_list_files "$iso_path" > "$iso_files" || { iso_list_files "$iso_path" > "$iso_files" || {
echo "ERROR: failed to list ISO files for GRUB theme validation" >&2 echo "ERROR: failed to list ISO files for GRUB asset validation" >&2
rm -f "$iso_files" rm -f "$iso_files"
exit 1 exit 1
} }
for required in \ for required in \
boot/grub/config.cfg \ boot/grub/config.cfg \
boot/grub/theme.cfg \ boot/grub/grub.cfg; do
boot/grub/live-theme/theme.txt \
boot/grub/live-theme/bee-logo.tga; do
grep -q "^${required}$" "$iso_files" || { grep -q "^${required}$" "$iso_files" || {
echo "ERROR: missing GRUB theme asset in ISO: ${required}" >&2 echo "ERROR: missing GRUB asset in ISO: ${required}" >&2
rm -f "$iso_files" rm -f "$iso_files"
exit 1 exit 1
} }
done done
rm -f "$iso_files" rm -f "$iso_files"
echo "=== GRUB theme validation OK ===" echo "=== GRUB asset validation OK ==="
} }
validate_iso_nvidia_runtime() { validate_iso_nvidia_runtime() {
@@ -610,29 +608,37 @@ validate_iso_nvidia_runtime() {
squashfs_tmp="$(mktemp)" squashfs_tmp="$(mktemp)"
squashfs_list="$(mktemp)" squashfs_list="$(mktemp)"
iso_read_member "$iso_path" live/filesystem.squashfs "$squashfs_tmp" || { iso_files="$(mktemp)"
rm -f "$squashfs_tmp" "$squashfs_list" iso_list_files "$iso_path" > "$iso_files" || {
nvidia_runtime_fail "failed to extract live/filesystem.squashfs from ISO" rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
} nvidia_runtime_fail "failed to list ISO files for NVIDIA runtime validation"
unsquashfs -ll "$squashfs_tmp" > "$squashfs_list" 2>/dev/null || {
rm -f "$squashfs_tmp" "$squashfs_list"
nvidia_runtime_fail "failed to inspect filesystem.squashfs from ISO"
} }
grep '^live/.*\.squashfs$' "$iso_files" | while IFS= read -r squashfs_member; do
iso_read_member "$iso_path" "$squashfs_member" "$squashfs_tmp" || {
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
nvidia_runtime_fail "failed to extract $squashfs_member from ISO"
}
unsquashfs -ll "$squashfs_tmp" >> "$squashfs_list" 2>/dev/null || {
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
nvidia_runtime_fail "failed to inspect $squashfs_member from ISO"
}
: > "$squashfs_tmp"
done
grep -Eq 'usr/bin/dcgmi$' "$squashfs_list" || { grep -Eq 'usr/bin/dcgmi$' "$squashfs_list" || {
rm -f "$squashfs_tmp" "$squashfs_list" rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
nvidia_runtime_fail "dcgmi missing from final NVIDIA ISO" nvidia_runtime_fail "dcgmi missing from final NVIDIA ISO"
} }
grep -Eq 'usr/bin/nv-hostengine$' "$squashfs_list" || { grep -Eq 'usr/bin/nv-hostengine$' "$squashfs_list" || {
rm -f "$squashfs_tmp" "$squashfs_list" rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
nvidia_runtime_fail "nv-hostengine missing from final NVIDIA ISO" nvidia_runtime_fail "nv-hostengine missing from final NVIDIA ISO"
} }
grep -Eq 'usr/bin/dcgmproftester([0-9]+)?$' "$squashfs_list" || { grep -Eq 'usr/bin/dcgmproftester([0-9]+)?$' "$squashfs_list" || {
rm -f "$squashfs_tmp" "$squashfs_list" rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
nvidia_runtime_fail "dcgmproftester missing from final NVIDIA ISO" nvidia_runtime_fail "dcgmproftester missing from final NVIDIA ISO"
} }
rm -f "$squashfs_tmp" "$squashfs_list" rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
echo "=== NVIDIA runtime validation OK ===" echo "=== NVIDIA runtime validation OK ==="
} }
@@ -726,20 +732,25 @@ write_canonical_grub_cfg() {
kernel="$2" kernel="$2"
append_live="$3" append_live="$3"
initrd="$4" initrd="$4"
version_label="${PROJECT_VERSION_EFFECTIVE}"
cat > "$cfg" <<EOF cat > "$cfg" <<EOF
source /boot/grub/config.cfg source /boot/grub/config.cfg
menuentry "EASY-BEE" { menuentry "EASY-BEE v${version_label}" {
linux ${kernel} ${append_live} nomodeset bee.nvidia.mode=normal pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup linux ${kernel} ${append_live} nomodeset bee.nvidia.mode=normal pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
initrd ${initrd} initrd ${initrd}
} }
menuentry "EASY-BEE -- load to RAM (toram)" { menuentry "EASY-BEE v${version_label} -- load to RAM (toram)" {
linux ${kernel} ${append_live} toram nomodeset bee.nvidia.mode=normal pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup linux ${kernel} ${append_live} toram nomodeset bee.nvidia.mode=normal pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
initrd ${initrd} initrd ${initrd}
} }
menuentry "EASY-BEE v${version_label} -- no GUI / no X11" {
linux ${kernel} ${append_live} nomodeset bee.gui=off bee.nvidia.mode=gsp-off pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
initrd ${initrd}
}
if [ "\${grub_platform}" = "efi" ]; then if [ "\${grub_platform}" = "efi" ]; then
menuentry "Memory Test (memtest86+)" { menuentry "Memory Test (memtest86+)" {
@@ -764,21 +775,28 @@ write_canonical_isolinux_cfg() {
kernel="$2" kernel="$2"
initrd="$3" initrd="$3"
append_live="$4" append_live="$4"
version_label="${PROJECT_VERSION_EFFECTIVE}"
cat > "$cfg" <<EOF cat > "$cfg" <<EOF
label live-@FLAVOUR@-normal label live-@FLAVOUR@-normal
menu label ^EASY-BEE menu label ^EASY-BEE v${version_label}
linux ${kernel} linux ${kernel}
initrd ${initrd} initrd ${initrd}
append ${append_live} nomodeset bee.nvidia.mode=normal net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup append ${append_live} nomodeset bee.nvidia.mode=normal net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
label live-@FLAVOUR@-toram label live-@FLAVOUR@-toram
menu label EASY-BEE (^load to RAM) menu label EASY-BEE v${version_label} (^load to RAM)
menu default menu default
linux ${kernel} linux ${kernel}
initrd ${initrd} initrd ${initrd}
append ${append_live} toram nomodeset bee.nvidia.mode=normal net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup append ${append_live} toram nomodeset bee.nvidia.mode=normal net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
label live-@FLAVOUR@-console
menu label EASY-BEE v${version_label} (^no GUI / no X11)
linux ${kernel}
initrd ${initrd}
append ${append_live} nomodeset bee.gui=off bee.nvidia.mode=gsp-off net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
label live-@FLAVOUR@-gsp-off label live-@FLAVOUR@-gsp-off
menu label EASY-BEE (^NVIDIA GSP=off) menu label EASY-BEE (^NVIDIA GSP=off)
linux ${kernel} linux ${kernel}
@@ -822,10 +840,7 @@ enforce_live_build_bootloader_assets() {
if [ -f "$grub_cfg" ]; then if [ -f "$grub_cfg" ]; then
if extract_live_grub_entry "$grub_cfg"; then if extract_live_grub_entry "$grub_cfg"; then
mkdir -p "$grub_dir/live-theme"
cp "${BUILDER_DIR}/config/bootloaders/grub-efi/config.cfg" "$grub_dir/config.cfg" cp "${BUILDER_DIR}/config/bootloaders/grub-efi/config.cfg" "$grub_dir/config.cfg"
cp "${BUILDER_DIR}/config/bootloaders/grub-efi/theme.cfg" "$grub_dir/theme.cfg"
cp -R "${BUILDER_DIR}/config/bootloaders/grub-efi/live-theme/." "$grub_dir/live-theme/"
write_canonical_grub_cfg "$grub_cfg" "$grub_kernel" "${live_build_append:-$grub_append}" "$grub_initrd" write_canonical_grub_cfg "$grub_cfg" "$grub_kernel" "${live_build_append:-$grub_append}" "$grub_initrd"
echo "bootloader sync: rewrote binary/boot/grub/grub.cfg with canonical EASY-BEE menu" echo "bootloader sync: rewrote binary/boot/grub/grub.cfg with canonical EASY-BEE menu"
else else
@@ -841,73 +856,6 @@ enforce_live_build_bootloader_assets() {
echo "bootloader sync: WARNING: could not extract live entry from $isolinux_cfg" >&2 echo "bootloader sync: WARNING: could not extract live entry from $isolinux_cfg" >&2
fi fi
fi fi
sync_efi_grub_theme_assets "$lb_dir"
}
fat_image_has_file() {
img="$1"
path="$2"
mtype -i "$img" "$path" >/dev/null 2>&1
}
is_efi_grub_fat_image() {
img="$1"
fat_image_has_file "$img" "::/EFI/boot/grubx64.efi" || return 1
fat_image_has_file "$img" "::/boot/grub/grub.cfg" || return 1
return 0
}
copy_file_to_fat_image() {
img="$1"
src="$2"
dst="$3"
mcopy -o -i "$img" "$src" "$dst" >/dev/null
}
sync_efi_grub_theme_assets() {
lb_dir="$1"
found=0
for img in $(find "$lb_dir" -type f \( -name '*.img' -o -name '*.ima' -o -name '*.fat' \) 2>/dev/null); do
if ! is_efi_grub_fat_image "$img"; then
continue
fi
found=1
echo "bootloader sync: patching EFI GRUB image $img"
mmd -i "$img" "::/boot" >/dev/null 2>&1 || true
mmd -i "$img" "::/boot/grub" >/dev/null 2>&1 || true
mmd -i "$img" "::/boot/grub/live-theme" >/dev/null 2>&1 || true
copy_file_to_fat_image "$img" "${BUILDER_DIR}/config/bootloaders/grub-efi/config.cfg" "::/boot/grub/config.cfg"
copy_file_to_fat_image "$img" "${BUILDER_DIR}/config/bootloaders/grub-efi/theme.cfg" "::/boot/grub/theme.cfg"
for asset in "${BUILDER_DIR}"/config/bootloaders/grub-efi/live-theme/*; do
[ -f "$asset" ] || continue
copy_file_to_fat_image "$img" "$asset" "::/boot/grub/live-theme/"
done
fat_image_has_file "$img" "::/boot/grub/config.cfg" || {
echo "ERROR: EFI GRUB image missing /boot/grub/config.cfg after sync: $img" >&2
exit 1
}
fat_image_has_file "$img" "::/boot/grub/theme.cfg" || {
echo "ERROR: EFI GRUB image missing /boot/grub/theme.cfg after sync: $img" >&2
exit 1
}
fat_image_has_file "$img" "::/boot/grub/live-theme/theme.txt" || {
echo "ERROR: EFI GRUB image missing /boot/grub/live-theme/theme.txt after sync: $img" >&2
exit 1
}
fat_image_has_file "$img" "::/boot/grub/live-theme/bee-logo.tga" || {
echo "ERROR: EFI GRUB image missing /boot/grub/live-theme/bee-logo.tga after sync: $img" >&2
exit 1
}
done
if [ "$found" != "1" ]; then
echo "ERROR: no EFI GRUB FAT image found in live-build workdir; cannot sync theme assets" >&2
exit 1
fi
} }
copy_memtest_from_deb() { copy_memtest_from_deb() {
@@ -1004,6 +952,63 @@ fast_path_rebuild_iso() {
echo "=== fast-path: ISO rebuilt ===" echo "=== fast-path: ISO rebuilt ==="
} }
dir_has_entries() {
_dir="$1"
[ -d "$_dir" ] || return 1
find "$_dir" -mindepth 1 -print -quit 2>/dev/null | grep -q .
}
move_tree_to_layer() {
_src_root="$1"
_rel="$2"
_dst_root="$3"
[ -e "${_src_root}/${_rel}" ] || return 0
mkdir -p "${_dst_root}/$(dirname "$_rel")"
mv "${_src_root}/${_rel}" "${_dst_root}/${_rel}"
}
split_live_squashfs_layers() {
lb_dir="$1"
live_dir="${lb_dir}/binary/live"
base_sq="${live_dir}/filesystem.squashfs"
usr_sq="${live_dir}/10-usr.squashfs"
fw_sq="${live_dir}/20-firmware.squashfs"
[ -f "$base_sq" ] || return 0
command -v unsquashfs >/dev/null 2>&1 || return 0
command -v mksquashfs >/dev/null 2>&1 || return 0
tmp_root="$(mktemp -d)"
tmp_usr="$(mktemp -d)"
tmp_fw="$(mktemp -d)"
echo "=== splitting live squashfs into smaller layers ==="
unsquashfs -d "$tmp_root/root" "$base_sq" >/dev/null
mkdir -p "$tmp_usr/root" "$tmp_fw/root"
move_tree_to_layer "$tmp_root/root" "usr" "$tmp_usr/root"
move_tree_to_layer "$tmp_root/root" "lib/firmware" "$tmp_fw/root"
move_tree_to_layer "$tmp_root/root" "usr/lib/firmware" "$tmp_fw/root"
move_tree_to_layer "$tmp_root/root" "boot/firmware" "$tmp_fw/root"
rm -f "$usr_sq" "$fw_sq"
mksquashfs "$tmp_root/root" "${base_sq}.new" -comp zstd -b 1048576 -noappend -no-progress >/dev/null
mv "${base_sq}.new" "$base_sq"
if dir_has_entries "$tmp_usr/root"; then
mksquashfs "$tmp_usr/root" "${usr_sq}.new" -comp zstd -b 1048576 -noappend -no-progress >/dev/null
mv "${usr_sq}.new" "$usr_sq"
fi
if dir_has_entries "$tmp_fw/root"; then
mksquashfs "$tmp_fw/root" "${fw_sq}.new" -comp zstd -b 1048576 -noappend -no-progress >/dev/null
mv "${fw_sq}.new" "$fw_sq"
fi
echo "=== live squashfs layers ==="
find "$live_dir" -maxdepth 1 -type f -name '*.squashfs' -exec du -sh {} \; | sort
rm -rf "$tmp_root" "$tmp_usr" "$tmp_fw"
}
recover_iso_memtest() { recover_iso_memtest() {
lb_dir="$1" lb_dir="$1"
iso_path="$2" iso_path="$2"
@@ -1652,7 +1657,7 @@ if ! needs_full_build; then
fast_path_rebuild_iso fast_path_rebuild_iso
ISO_RAW="${LB_DIR}/live-image-amd64.hybrid.iso" ISO_RAW="${LB_DIR}/live-image-amd64.hybrid.iso"
validate_iso_live_boot_entries "$ISO_RAW" validate_iso_live_boot_entries "$ISO_RAW"
validate_iso_grub_theme_assets "$ISO_RAW" validate_iso_grub_assets "$ISO_RAW"
validate_iso_nvidia_runtime "$ISO_RAW" validate_iso_nvidia_runtime "$ISO_RAW"
cp "$ISO_RAW" "$ISO_OUT" cp "$ISO_RAW" "$ISO_OUT"
echo "" echo ""
@@ -1708,7 +1713,7 @@ if [ -f "$ISO_RAW" ]; then
fi fi
validate_iso_memtest "$ISO_RAW" validate_iso_memtest "$ISO_RAW"
validate_iso_live_boot_entries "$ISO_RAW" validate_iso_live_boot_entries "$ISO_RAW"
validate_iso_grub_theme_assets "$ISO_RAW" validate_iso_grub_assets "$ISO_RAW"
validate_iso_nvidia_runtime "$ISO_RAW" validate_iso_nvidia_runtime "$ISO_RAW"
cp "$ISO_RAW" "$ISO_OUT" cp "$ISO_RAW" "$ISO_OUT"
touch "${FULL_BUILD_MARKER}" touch "${FULL_BUILD_MARKER}"

View File

@@ -1,5 +1,7 @@
set default=1 set default=1
set timeout=10 set timeout=10
set color_normal=yellow/black
set color_highlight=white/brown
if [ x$feature_default_font_path = xy ] ; then if [ x$feature_default_font_path = xy ] ; then
font=unicode font=unicode
@@ -26,6 +28,3 @@ insmod gfxterm
terminal_input console serial terminal_input console serial
terminal_output gfxterm serial terminal_output gfxterm serial
insmod tga
source /boot/grub/theme.cfg

View File

@@ -1,15 +1,20 @@
source /boot/grub/config.cfg source /boot/grub/config.cfg
menuentry "EASY-BEE" { menuentry "EASY-BEE v@VERSION@" {
linux @KERNEL_LIVE@ @APPEND_LIVE@ nomodeset bee.nvidia.mode=normal pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup linux @KERNEL_LIVE@ @APPEND_LIVE@ nomodeset bee.nvidia.mode=normal pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
initrd @INITRD_LIVE@ initrd @INITRD_LIVE@
} }
menuentry "EASY-BEE -- load to RAM (toram)" { menuentry "EASY-BEE v@VERSION@ -- load to RAM (toram)" {
linux @KERNEL_LIVE@ @APPEND_LIVE@ toram nomodeset bee.nvidia.mode=normal pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup linux @KERNEL_LIVE@ @APPEND_LIVE@ toram nomodeset bee.nvidia.mode=normal pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
initrd @INITRD_LIVE@ initrd @INITRD_LIVE@
} }
menuentry "EASY-BEE v@VERSION@ -- no GUI / no X11" {
linux @KERNEL_LIVE@ @APPEND_LIVE@ nomodeset bee.gui=off bee.nvidia.mode=gsp-off pci=realloc net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
initrd @INITRD_LIVE@
}
if [ "${grub_platform}" = "efi" ]; then if [ "${grub_platform}" = "efi" ]; then
menuentry "Memory Test (memtest86+)" { menuentry "Memory Test (memtest86+)" {

View File

@@ -1,16 +1,22 @@
label live-@FLAVOUR@-normal label live-@FLAVOUR@-normal
menu label ^EASY-BEE menu label ^EASY-BEE v@VERSION@
linux @LINUX@ linux @LINUX@
initrd @INITRD@ initrd @INITRD@
append @APPEND_LIVE@ nomodeset bee.nvidia.mode=normal net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup append @APPEND_LIVE@ nomodeset bee.nvidia.mode=normal net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
label live-@FLAVOUR@-toram label live-@FLAVOUR@-toram
menu label EASY-BEE (^load to RAM) menu label EASY-BEE v@VERSION@ (^load to RAM)
menu default menu default
linux @LINUX@ linux @LINUX@
initrd @INITRD@ initrd @INITRD@
append @APPEND_LIVE@ toram nomodeset bee.nvidia.mode=normal net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup append @APPEND_LIVE@ toram nomodeset bee.nvidia.mode=normal net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
label live-@FLAVOUR@-console
menu label EASY-BEE v@VERSION@ (^no GUI / no X11)
linux @LINUX@
initrd @INITRD@
append @APPEND_LIVE@ nomodeset bee.gui=off bee.nvidia.mode=gsp-off net.ifnames=0 biosdevname=0 mitigations=off transparent_hugepage=always numa_balancing=disable pcie_aspm=off intel_idle.max_cstate=1 processor.max_cstate=1 nowatchdog nosoftlockup
label live-@FLAVOUR@-gsp-off label live-@FLAVOUR@-gsp-off
menu label EASY-BEE (^NVIDIA GSP=off) menu label EASY-BEE (^NVIDIA GSP=off)
linux @LINUX@ linux @LINUX@

View File

@@ -67,6 +67,7 @@ chmod +x /usr/local/bin/bee-log-run 2>/dev/null || true
chmod +x /usr/local/bin/bee-selfheal 2>/dev/null || true chmod +x /usr/local/bin/bee-selfheal 2>/dev/null || true
chmod +x /usr/local/bin/bee-boot-status 2>/dev/null || true chmod +x /usr/local/bin/bee-boot-status 2>/dev/null || true
chmod +x /usr/local/bin/bee-install 2>/dev/null || true chmod +x /usr/local/bin/bee-install 2>/dev/null || true
chmod +x /usr/local/bin/bee-gui-gate 2>/dev/null || true
chmod +x /usr/local/bin/bee-remount-medium 2>/dev/null || true chmod +x /usr/local/bin/bee-remount-medium 2>/dev/null || true
if [ "$GPU_VENDOR" = "nvidia" ]; then if [ "$GPU_VENDOR" = "nvidia" ]; then
chmod +x /usr/local/bin/bee-nvidia-load 2>/dev/null || true chmod +x /usr/local/bin/bee-nvidia-load 2>/dev/null || true

View File

@@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Bee: hardware audit Description=Bee: hardware audit
After=bee-preflight.service bee-network.service bee-nvidia.service bee-blackbox.service After=bee-preflight.service bee-nvidia.service bee-blackbox.service
[Service] [Service]
Type=oneshot Type=oneshot

View File

@@ -1,7 +1,6 @@
[Unit] [Unit]
Description=Bee: bring up network interfaces via DHCP Description=Bee: bring up network interfaces via DHCP
After=local-fs.target bee-blackbox.service After=bee-web.service bee-audit.service
Before=network-online.target bee-audit.service
[Service] [Service]
Type=oneshot Type=oneshot

View File

@@ -1,6 +1,6 @@
[Unit] [Unit]
Description=Bee: runtime preflight self-check Description=Bee: runtime preflight self-check
After=bee-network.service bee-nvidia.service bee-blackbox.service After=bee-nvidia.service bee-blackbox.service
Before=bee-audit.service Before=bee-audit.service
[Service] [Service]

View File

@@ -0,0 +1,2 @@
[Service]
ExecCondition=/usr/local/bin/bee-gui-gate

View File

@@ -0,0 +1,27 @@
#!/bin/sh
# bee-gui-gate — skip starting the local GUI when bee.gui=off is set.
set -eu
cmdline_param() {
key="$1"
for token in $(cat /proc/cmdline 2>/dev/null); do
case "$token" in
"$key"=*)
echo "${token#*=}"
return 0
;;
esac
done
return 1
}
mode="$(cmdline_param bee.gui || true)"
case "${mode}" in
off|false|0|tty|console|text|nogui)
echo "bee-gui-gate: bee.gui=${mode}; skipping lightdm"
exit 1
;;
esac
exit 0

View File

@@ -8,7 +8,7 @@
# Layout (UEFI): GPT, /dev/sdX1=EFI 512MB vfat, /dev/sdX2=root ext4 # Layout (UEFI): GPT, /dev/sdX1=EFI 512MB vfat, /dev/sdX2=root ext4
# Layout (BIOS): MBR, /dev/sdX1=root ext4 # Layout (BIOS): MBR, /dev/sdX1=root ext4
# #
# Squashfs source: /run/live/medium/live/filesystem.squashfs # Squashfs sources: /run/live/medium/live/*.squashfs
set -euo pipefail set -euo pipefail
@@ -62,9 +62,9 @@ for tool in parted mkfs.vfat mkfs.ext4 unsquashfs grub-install update-grub; do
fi fi
done done
SQUASHFS="/run/live/medium/live/filesystem.squashfs" mapfile -t SQUASHFS_FILES < <(find /run/live/medium/live -maxdepth 1 -type f -name '*.squashfs' | sort)
if [ ! -f "$SQUASHFS" ]; then if [ "${#SQUASHFS_FILES[@]}" -eq 0 ]; then
echo "ERROR: squashfs not found at $SQUASHFS" >&2 echo "ERROR: no squashfs files found under /run/live/medium/live" >&2
echo " The live medium may have been disconnected." >&2 echo " The live medium may have been disconnected." >&2
echo " Reconnect the disc and run: bee-remount-medium --wait" >&2 echo " Reconnect the disc and run: bee-remount-medium --wait" >&2
echo " Then re-run bee-install." >&2 echo " Then re-run bee-install." >&2
@@ -106,7 +106,10 @@ log "=== BEE DISK INSTALLER ==="
log "Target device : $DEVICE" log "Target device : $DEVICE"
log "Root partition: $PART_ROOT" log "Root partition: $PART_ROOT"
[ "$UEFI" = "1" ] && log "EFI partition : $PART_EFI" [ "$UEFI" = "1" ] && log "EFI partition : $PART_EFI"
log "Squashfs : $SQUASHFS ($(du -sh "$SQUASHFS" | cut -f1))" log "Squashfs : ${#SQUASHFS_FILES[@]} layer(s)"
for sf in "${SQUASHFS_FILES[@]}"; do
log " - $sf ($(du -sh "$sf" | cut -f1))"
done
log "Log : $LOGFILE" log "Log : $LOGFILE"
log "" log ""
@@ -163,7 +166,9 @@ log " Mounted."
# ------------------------------------------------------------------ # ------------------------------------------------------------------
log "--- Step 5/7: Unpacking filesystem (this takes 10-20 minutes) ---" log "--- Step 5/7: Unpacking filesystem (this takes 10-20 minutes) ---"
log " Source: $SQUASHFS" for sf in "${SQUASHFS_FILES[@]}"; do
log " Source: $sf"
done
log " Target: $MOUNT_ROOT" log " Target: $MOUNT_ROOT"
# unsquashfs does not support resume, so retry the entire unpack step if the # unsquashfs does not support resume, so retry the entire unpack step if the
@@ -177,9 +182,9 @@ while true; do
fi fi
[ "$UNPACK_ATTEMPTS" -gt 1 ] && log " Retry attempt $UNPACK_ATTEMPTS / $UNPACK_MAX ..." [ "$UNPACK_ATTEMPTS" -gt 1 ] && log " Retry attempt $UNPACK_ATTEMPTS / $UNPACK_MAX ..."
# Re-check squashfs is reachable before each attempt mapfile -t SQUASHFS_FILES < <(find /run/live/medium/live -maxdepth 1 -type f -name '*.squashfs' | sort)
if [ ! -f "$SQUASHFS" ]; then if [ "${#SQUASHFS_FILES[@]}" -eq 0 ]; then
log " SOURCE LOST: $SQUASHFS not found." log " SOURCE LOST: no squashfs files found under /run/live/medium/live."
log " Reconnect the disc and run 'bee-remount-medium --wait' in another terminal," log " Reconnect the disc and run 'bee-remount-medium --wait' in another terminal,"
log " then press Enter here to retry." log " then press Enter here to retry."
read -r _ read -r _
@@ -194,12 +199,17 @@ while true; do
fi fi
UNPACK_OK=0 UNPACK_OK=0
unsquashfs -f -d "$MOUNT_ROOT" "$SQUASHFS" 2>&1 | \ for sf in "${SQUASHFS_FILES[@]}"; do
grep -E '^\[|^inod|^created|^extract|^ERROR|failed' | \ log " Unpacking $(basename "$sf") ..."
while IFS= read -r line; do log " $line"; done || UNPACK_OK=$? unsquashfs -f -d "$MOUNT_ROOT" "$sf" 2>&1 | \
grep -E '^\[|^inod|^created|^extract|^ERROR|failed' | \
while IFS= read -r line; do log " $line"; done || UNPACK_OK=$?
[ "$UNPACK_OK" -eq 0 ] || break
done
# Check squashfs is still reachable (gone = disc pulled during copy) # Check squashfs is still reachable (gone = disc pulled during copy)
if [ ! -f "$SQUASHFS" ]; then mapfile -t SQUASHFS_FILES < <(find /run/live/medium/live -maxdepth 1 -type f -name '*.squashfs' | sort)
if [ "${#SQUASHFS_FILES[@]}" -eq 0 ]; then
log " WARNING: source medium lost during unpack — will retry after remount." log " WARNING: source medium lost during unpack — will retry after remount."
log " Run 'bee-remount-medium --wait' in another terminal, then press Enter." log " Run 'bee-remount-medium --wait' in another terminal, then press Enter."
read -r _ read -r _

View File

@@ -1,8 +1,9 @@
#!/bin/sh #!/bin/sh
# bee-network.sh — bring up all physical network interfaces via DHCP # bee-network.sh — bring up all physical network interfaces via DHCP
# Unattended: runs silently, logs results, never blocks. # Unattended: starts later in boot, runs quietly, and gives up after a bounded timeout.
LOG_PREFIX="bee-network" LOG_PREFIX="bee-network"
DHCP_TIMEOUT_SECS=300
log() { echo "[$LOG_PREFIX] $*"; } log() { echo "[$LOG_PREFIX] $*"; }
@@ -19,9 +20,50 @@ if command -v udevadm >/dev/null 2>&1; then
udevadm settle --timeout=5 >/dev/null 2>&1 || log "WARN: udevadm settle timed out" udevadm settle --timeout=5 >/dev/null 2>&1 || log "WARN: udevadm settle timed out"
fi fi
start_dhcp() {
iface="$1"
if ! ip link set "$iface" up; then
log "WARN: could not bring up $iface"
return 1
fi
carrier=$(cat "/sys/class/net/$iface/carrier" 2>/dev/null || true)
if [ "$carrier" = "1" ]; then
log "carrier detected on $iface"
else
log "carrier not detected on $iface"
fi
dhclient -r "$iface" >/dev/null 2>&1 || true
if timeout "${DHCP_TIMEOUT_SECS}" dhclient -4 -q -1 "$iface" >/dev/null 2>&1; then
addr="$(ip -4 -o addr show dev "$iface" scope global 2>/dev/null | awk '{print $4}' | head -1)"
if [ -n "$addr" ]; then
log "DHCP lease acquired on $iface ($addr)"
else
log "DHCP lease acquired on $iface"
fi
return 0
fi
rc=$?
case "$rc" in
124)
log "DHCP timed out on $iface after ${DHCP_TIMEOUT_SECS}s"
;;
*)
log "DHCP failed on $iface (exit $rc)"
;;
esac
dhclient -r "$iface" >/dev/null 2>&1 || true
return 1
}
started_ifaces="" started_ifaces=""
started_count=0 started_count=0
scan_pass=1 scan_pass=1
pids=""
pid_ifaces=""
# Some server NICs appear a bit later after module/firmware init. Do a small # Some server NICs appear a bit later after module/firmware init. Do a small
# bounded rescan window without turning network bring-up into a boot blocker. # bounded rescan window without turning network bring-up into a boot blocker.
@@ -34,22 +76,11 @@ while [ "$scan_pass" -le 3 ]; do
*" $iface "*) continue ;; *" $iface "*) continue ;;
esac esac
log "bringing up $iface" log "starting DHCP on $iface (timeout ${DHCP_TIMEOUT_SECS}s)"
if ! ip link set "$iface" up; then start_dhcp "$iface" &
log "WARN: could not bring up $iface" pid="$!"
continue pids="$pids $pid"
fi pid_ifaces="$pid_ifaces $pid:$iface"
carrier=$(cat "/sys/class/net/$iface/carrier" 2>/dev/null || true)
if [ "$carrier" = "1" ]; then
log "carrier detected on $iface"
else
log "carrier not detected yet on $iface"
fi
# DHCP in background — non-blocking, keep dhclient verbose output in the service log.
dhclient -4 -v -nw "$iface" &
log "DHCP started for $iface (pid $!)"
started_ifaces="$started_ifaces $iface" started_ifaces="$started_ifaces $iface"
started_count=$((started_count + 1)) started_count=$((started_count + 1))
@@ -68,4 +99,15 @@ if [ "$started_count" -eq 0 ]; then
exit 0 exit 0
fi fi
log "done (interfaces started: $started_count)" success_count=0
for pid_iface in $pid_ifaces; do
pid="${pid_iface%%:*}"
iface="${pid_iface#*:}"
if wait "$pid"; then
success_count=$((success_count + 1))
else
log "DHCP did not complete successfully on $iface"
fi
done
log "done (interfaces scanned: $started_count, leases acquired: $success_count)"

View File

@@ -2,7 +2,7 @@
# bee-remount-medium — find and remount the live ISO medium to /run/live/medium # bee-remount-medium — find and remount the live ISO medium to /run/live/medium
# #
# Run this after reconnecting the ISO source disc (USB/CD) if the live medium # Run this after reconnecting the ISO source disc (USB/CD) if the live medium
# was lost and /run/live/medium/live/filesystem.squashfs is missing. # was lost and /run/live/medium/live/*.squashfs are missing.
# #
# Usage: bee-remount-medium [--wait] # Usage: bee-remount-medium [--wait]
# --wait keep retrying every 5 seconds until the medium is found (useful # --wait keep retrying every 5 seconds until the medium is found (useful
@@ -11,7 +11,7 @@
set -euo pipefail set -euo pipefail
MEDIUM_DIR="/run/live/medium" MEDIUM_DIR="/run/live/medium"
SQUASHFS_REL="live/filesystem.squashfs" SQUASHFS_GLOB="live/*.squashfs"
WAIT_MODE=0 WAIT_MODE=0
for arg in "$@"; do for arg in "$@"; do
@@ -56,7 +56,7 @@ try_mount() {
local tmpdir local tmpdir
tmpdir=$(mktemp -d /tmp/bee-probe-XXXXXX) tmpdir=$(mktemp -d /tmp/bee-probe-XXXXXX)
if mount -o ro "$dev" "$tmpdir" 2>/dev/null; then if mount -o ro "$dev" "$tmpdir" 2>/dev/null; then
if [ -f "${tmpdir}/${SQUASHFS_REL}" ]; then if find "${tmpdir}/live" -maxdepth 1 -type f -name '*.squashfs' 2>/dev/null | grep -q .; then
# Unmount probe mount and mount properly onto live path # Unmount probe mount and mount properly onto live path
umount "$tmpdir" 2>/dev/null || true umount "$tmpdir" 2>/dev/null || true
rmdir "$tmpdir" 2>/dev/null || true rmdir "$tmpdir" 2>/dev/null || true
@@ -82,8 +82,9 @@ attempt() {
for dev in $(find_candidates); do for dev in $(find_candidates); do
log " Trying $dev ..." log " Trying $dev ..."
if try_mount "$dev"; then if try_mount "$dev"; then
local sq="${MEDIUM_DIR}/${SQUASHFS_REL}" local count
log "SUCCESS: squashfs available at $sq ($(du -sh "$sq" | cut -f1))" count=$(find "${MEDIUM_DIR}/live" -maxdepth 1 -type f -name '*.squashfs' 2>/dev/null | wc -l | tr -d ' ')
log "SUCCESS: ${count} squashfs layer(s) available under ${MEDIUM_DIR}/live"
return 0 return 0
fi fi
done done
@@ -100,5 +101,5 @@ if [ "$WAIT_MODE" = "1" ]; then
sleep 5 sleep 5
done done
else else
attempt || die "No ISO medium with ${SQUASHFS_REL} found. Reconnect the disc and re-run, or use --wait." attempt || die "No ISO medium with ${SQUASHFS_GLOB} found. Reconnect the disc and re-run, or use --wait."
fi fi