Squashfs versioning: - ISO now contains filesystem-v<VERSION>.squashfs instead of the generic filesystem.squashfs, making it immediately visible which build is running (visible in /run/live/medium/live/ at boot time). - Full build path: rename filesystem.squashfs → filesystem-v*.squashfs after lb build, before lb binary_checksums/binary_iso. - Fast path: find and unpack whatever filesystem*.squashfs exists, repack as the new versioned name, remove the old file, update the ISO. - needs_full_build: accept any filesystem*.squashfs so version changes alone don't force a full rebuild. Media selection hardening: - Add live-media=/dev/disk/by-label/<LABEL> to the kernel boot line in addition to the existing live-media-label=<LABEL>. live-boot will now open exactly the labeled device rather than scanning all block devices, preventing accidental use of squashfs files from local disks or stale virtual media attached via IPMI. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1765 lines
63 KiB
Bash
Executable File
1765 lines
63 KiB
Bash
Executable File
#!/bin/sh
|
|
# build.sh — internal ISO build entrypoint executed inside the builder container.
|
|
|
|
set -e
|
|
|
|
if [ "${BEE_CONTAINER_BUILD:-0}" != "1" ]; then
|
|
echo "build.sh must run inside iso/builder/build-in-container.sh" >&2
|
|
exit 1
|
|
fi
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
BUILDER_DIR="${REPO_ROOT}/iso/builder"
|
|
OVERLAY_DIR="${REPO_ROOT}/iso/overlay"
|
|
DIST_DIR="${REPO_ROOT}/dist"
|
|
VENDOR_DIR="${REPO_ROOT}/iso/vendor"
|
|
CACHE_ROOT="${BEE_CACHE_DIR:-${DIST_DIR}/cache}"
|
|
AUTH_KEYS=""
|
|
BUILD_VARIANT="nvidia"
|
|
BEE_GPU_VENDOR="nvidia"
|
|
BEE_NVIDIA_MODULE_FLAVOR="open"
|
|
|
|
# parse args
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--authorized-keys) AUTH_KEYS="$2"; shift 2 ;;
|
|
--variant) BUILD_VARIANT="$2"; shift 2 ;;
|
|
*) echo "unknown arg: $1"; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
case "$BUILD_VARIANT" in
|
|
nvidia)
|
|
BEE_GPU_VENDOR="nvidia"
|
|
BEE_NVIDIA_MODULE_FLAVOR="open"
|
|
;;
|
|
nvidia-legacy)
|
|
BEE_GPU_VENDOR="nvidia"
|
|
BEE_NVIDIA_MODULE_FLAVOR="proprietary"
|
|
;;
|
|
amd)
|
|
BEE_GPU_VENDOR="amd"
|
|
BEE_NVIDIA_MODULE_FLAVOR=""
|
|
;;
|
|
nogpu)
|
|
BEE_GPU_VENDOR="nogpu"
|
|
BEE_NVIDIA_MODULE_FLAVOR=""
|
|
;;
|
|
*)
|
|
echo "unknown variant: $BUILD_VARIANT (expected nvidia, nvidia-legacy, amd, or nogpu)" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
BUILD_WORK_DIR="${DIST_DIR}/live-build-work-${BUILD_VARIANT}"
|
|
OVERLAY_STAGE_DIR="${DIST_DIR}/overlay-stage-${BUILD_VARIANT}"
|
|
|
|
export BEE_GPU_VENDOR BEE_NVIDIA_MODULE_FLAVOR BUILD_VARIANT
|
|
|
|
. "${BUILDER_DIR}/VERSIONS"
|
|
export MEMTEST_VERSION
|
|
export PATH="$PATH:/usr/local/go/bin"
|
|
: "${BEE_REQUIRE_MEMTEST:=0}"
|
|
|
|
# Allow git to read the bind-mounted repo (different UID inside container).
|
|
git config --global safe.directory "${REPO_ROOT}"
|
|
mkdir -p "${DIST_DIR}"
|
|
mkdir -p "${CACHE_ROOT}"
|
|
: "${GOCACHE:=${CACHE_ROOT}/go-build}"
|
|
: "${GOMODCACHE:=${CACHE_ROOT}/go-mod}"
|
|
export GOCACHE GOMODCACHE
|
|
|
|
resolve_project_version() {
|
|
if [ -n "${BEE_VERSION:-}" ]; then
|
|
echo "${BEE_VERSION}"
|
|
return 0
|
|
fi
|
|
|
|
if [ -n "${BEE_AUDIT_VERSION:-}" ] && [ -n "${BEE_ISO_VERSION:-}" ] && [ "${BEE_AUDIT_VERSION}" != "${BEE_ISO_VERSION}" ]; then
|
|
echo "ERROR: BEE_AUDIT_VERSION (${BEE_AUDIT_VERSION}) and BEE_ISO_VERSION (${BEE_ISO_VERSION}) differ; versioning must stay synchronized" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ -n "${BEE_AUDIT_VERSION:-}" ]; then
|
|
echo "${BEE_AUDIT_VERSION}"
|
|
return 0
|
|
fi
|
|
|
|
if [ -n "${BEE_ISO_VERSION:-}" ]; then
|
|
echo "${BEE_ISO_VERSION}"
|
|
return 0
|
|
fi
|
|
|
|
tag="$(git -C "${REPO_ROOT}" describe --tags --match 'v[0-9]*' --abbrev=7 --dirty 2>/dev/null || true)"
|
|
case "${tag}" in
|
|
v*)
|
|
echo "${tag#v}"
|
|
return 0
|
|
;;
|
|
"")
|
|
;;
|
|
*)
|
|
echo "${tag}"
|
|
return 0
|
|
;;
|
|
esac
|
|
|
|
if [ -n "${AUDIT_VERSION:-}" ]; then
|
|
echo "${AUDIT_VERSION}"
|
|
return 0
|
|
fi
|
|
|
|
date +%Y%m%d
|
|
}
|
|
|
|
sync_builder_workdir() {
|
|
src_dir="$1"
|
|
dst_dir="$2"
|
|
|
|
mkdir -p "$dst_dir"
|
|
|
|
# Historical bug: old workdirs could keep config/bootloaders/grub-pc even
|
|
# after the source tree moved to grub-efi only. Remove bootloaders eagerly
|
|
# so reused workdirs cannot leak stale templates into a new ISO build.
|
|
rm -rf "$dst_dir/config/bootloaders"
|
|
|
|
rsync -a --delete \
|
|
--exclude='cache/' \
|
|
--exclude='chroot/' \
|
|
--exclude='.build/' \
|
|
--exclude='*.iso' \
|
|
--exclude='*.packages' \
|
|
--exclude='*.contents' \
|
|
--exclude='*.files' \
|
|
"$src_dir/" "$dst_dir/"
|
|
|
|
if [ ! -f "$dst_dir/config/bootloaders/grub-efi/grub.cfg" ]; then
|
|
echo "ERROR: staged workdir is missing config/bootloaders/grub-efi/grub.cfg" >&2
|
|
exit 1
|
|
fi
|
|
if [ -e "$dst_dir/config/bootloaders/grub-pc" ]; then
|
|
echo "ERROR: stale config/bootloaders/grub-pc remained in staged workdir" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
iso_list_files() {
|
|
iso_path="$1"
|
|
|
|
if command -v bsdtar >/dev/null 2>&1; then
|
|
bsdtar -tf "$iso_path"
|
|
return $?
|
|
fi
|
|
|
|
if command -v xorriso >/dev/null 2>&1; then
|
|
xorriso -indev "$iso_path" -find / -type f -print 2>/dev/null | sed 's#^/##'
|
|
return $?
|
|
fi
|
|
|
|
return 127
|
|
}
|
|
|
|
iso_extract_file() {
|
|
iso_path="$1"
|
|
iso_member="$2"
|
|
|
|
if command -v bsdtar >/dev/null 2>&1; then
|
|
bsdtar -xOf "$iso_path" "$iso_member"
|
|
return $?
|
|
fi
|
|
|
|
if command -v xorriso >/dev/null 2>&1; then
|
|
xorriso -osirrox on -indev "$iso_path" -cat "/$iso_member" 2>/dev/null
|
|
return $?
|
|
fi
|
|
|
|
return 127
|
|
}
|
|
|
|
iso_read_file_list() {
|
|
iso_path="$1"
|
|
out_path="$2"
|
|
|
|
iso_list_files "$iso_path" > "$out_path" || return 1
|
|
[ -s "$out_path" ] || return 1
|
|
return 0
|
|
}
|
|
|
|
iso_read_member() {
|
|
iso_path="$1"
|
|
iso_member="$2"
|
|
out_path="$3"
|
|
|
|
iso_extract_file "$iso_path" "$iso_member" > "$out_path" || return 1
|
|
[ -s "$out_path" ] || return 1
|
|
return 0
|
|
}
|
|
|
|
require_iso_reader() {
|
|
command -v bsdtar >/dev/null 2>&1 && return 0
|
|
command -v xorriso >/dev/null 2>&1 && return 0
|
|
memtest_fail "ISO reader is required for validation/debug (expected bsdtar or xorriso)" "${1:-}"
|
|
}
|
|
|
|
dump_memtest_debug() {
|
|
phase="$1"
|
|
lb_dir="${2:-}"
|
|
iso_path="${3:-}"
|
|
phase_slug="$(printf '%s' "${phase}" | tr ' /' '__')"
|
|
memtest_log="${LOG_DIR:-}/memtest-${phase_slug}.log"
|
|
|
|
(
|
|
echo "=== memtest debug: ${phase} ==="
|
|
|
|
echo "-- auto/config --"
|
|
if [ -f "${BUILDER_DIR}/auto/config" ]; then
|
|
grep -n -- '--memtest' "${BUILDER_DIR}/auto/config" || echo " (no --memtest line found)"
|
|
else
|
|
echo " (missing ${BUILDER_DIR}/auto/config)"
|
|
fi
|
|
|
|
echo "-- source bootloader templates --"
|
|
for cfg in \
|
|
"${BUILDER_DIR}/config/bootloaders/grub-efi/grub.cfg" \
|
|
"${BUILDER_DIR}/config/bootloaders/isolinux/live.cfg.in"; do
|
|
if [ -f "$cfg" ]; then
|
|
echo " file: $cfg"
|
|
grep -n 'Memory Test\|memtest' "$cfg" || echo " (no memtest lines)"
|
|
fi
|
|
done
|
|
|
|
echo "-- source binary hooks --"
|
|
for hook in \
|
|
"${BUILDER_DIR}/config/hooks/normal/9100-memtest.hook.binary"; do
|
|
if [ -f "$hook" ]; then
|
|
echo " hook: $hook"
|
|
else
|
|
echo " (missing $hook)"
|
|
fi
|
|
done
|
|
|
|
if [ -n "$lb_dir" ] && [ -d "$lb_dir" ]; then
|
|
echo "-- live-build workdir package lists --"
|
|
for pkg in \
|
|
"$lb_dir/config/package-lists/bee.list.chroot" \
|
|
"$lb_dir/config/package-lists/bee-gpu.list.chroot" \
|
|
"$lb_dir/config/package-lists/bee-nvidia.list.chroot"; do
|
|
if [ -f "$pkg" ]; then
|
|
echo " file: $pkg"
|
|
grep -n 'memtest' "$pkg" || echo " (no memtest lines)"
|
|
fi
|
|
done
|
|
|
|
echo "-- live-build chroot/boot --"
|
|
if [ -d "$lb_dir/chroot/boot" ]; then
|
|
find "$lb_dir/chroot/boot" -maxdepth 1 -name 'memtest*' -print | sed 's/^/ /' || true
|
|
else
|
|
echo " (missing $lb_dir/chroot/boot)"
|
|
fi
|
|
|
|
echo "-- live-build binary/boot --"
|
|
if [ -d "$lb_dir/binary/boot" ]; then
|
|
find "$lb_dir/binary/boot" -maxdepth 1 -name 'memtest*' -print | sed 's/^/ /' || true
|
|
else
|
|
echo " (missing $lb_dir/binary/boot)"
|
|
fi
|
|
|
|
echo "-- live-build binary grub cfg --"
|
|
if [ -f "$lb_dir/binary/boot/grub/grub.cfg" ]; then
|
|
grep -n 'Memory Test\|memtest' "$lb_dir/binary/boot/grub/grub.cfg" || echo " (no memtest lines)"
|
|
else
|
|
echo " (missing $lb_dir/binary/boot/grub/grub.cfg)"
|
|
fi
|
|
|
|
echo "-- live-build binary isolinux cfg --"
|
|
if [ -f "$lb_dir/binary/isolinux/live.cfg" ]; then
|
|
grep -n 'Memory Test\|memtest' "$lb_dir/binary/isolinux/live.cfg" || echo " (no memtest lines)"
|
|
else
|
|
echo " (missing $lb_dir/binary/isolinux/live.cfg)"
|
|
fi
|
|
|
|
echo "-- live-build package cache --"
|
|
if [ -d "$lb_dir/cache/packages.chroot" ]; then
|
|
find "$lb_dir/cache/packages.chroot" -maxdepth 1 -name 'memtest86+*.deb' -print | sed 's/^/ /' || true
|
|
else
|
|
echo " (missing $lb_dir/cache/packages.chroot)"
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$iso_path" ] && [ -f "$iso_path" ]; then
|
|
iso_files="$(mktemp)"
|
|
iso_grub_cfg="$(mktemp)"
|
|
iso_isolinux_cfg="$(mktemp)"
|
|
|
|
echo "-- ISO memtest files --"
|
|
if iso_read_file_list "$iso_path" "$iso_files"; then
|
|
grep 'memtest' "$iso_files" | sed 's/^/ /' || echo " (no memtest files in ISO)"
|
|
else
|
|
echo " (failed to list ISO contents)"
|
|
fi
|
|
|
|
echo "-- ISO GRUB memtest lines --"
|
|
if iso_read_member "$iso_path" boot/grub/grub.cfg "$iso_grub_cfg"; then
|
|
grep -n 'Memory Test\|memtest' "$iso_grub_cfg" || echo " (no memtest lines in boot/grub/grub.cfg)"
|
|
else
|
|
echo " (failed to read boot/grub/grub.cfg from ISO)"
|
|
fi
|
|
|
|
echo "-- ISO isolinux memtest lines --"
|
|
if iso_read_member "$iso_path" isolinux/live.cfg "$iso_isolinux_cfg"; then
|
|
grep -n 'Memory Test\|memtest' "$iso_isolinux_cfg" || echo " (no memtest lines in isolinux/live.cfg)"
|
|
else
|
|
echo " (failed to read isolinux/live.cfg from ISO)"
|
|
fi
|
|
|
|
rm -f "$iso_files" "$iso_grub_cfg" "$iso_isolinux_cfg"
|
|
fi
|
|
|
|
echo "=== end memtest debug: ${phase} ==="
|
|
) | {
|
|
if [ -n "${LOG_DIR:-}" ] && [ -d "${LOG_DIR}" ]; then
|
|
tee "${memtest_log}"
|
|
else
|
|
cat
|
|
fi
|
|
}
|
|
}
|
|
|
|
memtest_fail() {
|
|
msg="$1"
|
|
iso_path="${2:-}"
|
|
level="WARNING"
|
|
if [ "${BEE_REQUIRE_MEMTEST:-0}" = "1" ]; then
|
|
level="ERROR"
|
|
fi
|
|
echo "${level}: ${msg}" >&2
|
|
dump_memtest_debug "failure" "${LB_DIR:-}" "$iso_path" >&2
|
|
if [ "${BEE_REQUIRE_MEMTEST:-0}" = "1" ]; then
|
|
exit 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
nvidia_runtime_fail() {
|
|
msg="$1"
|
|
echo "ERROR: ${msg}" >&2
|
|
exit 1
|
|
}
|
|
|
|
iso_memtest_present() {
|
|
iso_path="$1"
|
|
iso_files="$(mktemp)"
|
|
|
|
[ -f "$iso_path" ] || return 1
|
|
|
|
if command -v bsdtar >/dev/null 2>&1; then
|
|
:
|
|
elif command -v xorriso >/dev/null 2>&1; then
|
|
:
|
|
else
|
|
return 2
|
|
fi
|
|
|
|
iso_read_file_list "$iso_path" "$iso_files" || {
|
|
rm -f "$iso_files"
|
|
return 2
|
|
}
|
|
|
|
grep -q '^boot/memtest86+x64\.bin$' "$iso_files" || {
|
|
rm -f "$iso_files"
|
|
return 1
|
|
}
|
|
grep -q '^boot/memtest86+x64\.efi$' "$iso_files" || {
|
|
rm -f "$iso_files"
|
|
return 1
|
|
}
|
|
|
|
grub_cfg="$(mktemp)"
|
|
isolinux_cfg="$(mktemp)"
|
|
|
|
iso_read_member "$iso_path" boot/grub/grub.cfg "$grub_cfg" || {
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 2
|
|
}
|
|
iso_read_member "$iso_path" isolinux/live.cfg "$isolinux_cfg" || {
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 2
|
|
}
|
|
|
|
grep -q 'Memory Test (memtest86+)' "$grub_cfg" || {
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 1
|
|
}
|
|
grep -q '/boot/memtest86+x64\.efi' "$grub_cfg" || {
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 1
|
|
}
|
|
grep -q '/boot/memtest86+x64\.bin' "$grub_cfg" || {
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 1
|
|
}
|
|
grep -q 'Memory Test (memtest86+)' "$isolinux_cfg" || {
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 1
|
|
}
|
|
grep -q '/boot/memtest86+x64\.bin' "$isolinux_cfg" || {
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 1
|
|
}
|
|
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 0
|
|
}
|
|
|
|
validate_iso_memtest() {
|
|
iso_path="$1"
|
|
echo "=== validating memtest in ISO ==="
|
|
|
|
[ -f "$iso_path" ] || {
|
|
memtest_fail "ISO not found for validation: $iso_path" "$iso_path"
|
|
return 0
|
|
}
|
|
require_iso_reader "$iso_path" || return 0
|
|
|
|
iso_files="$(mktemp)"
|
|
iso_read_file_list "$iso_path" "$iso_files" || {
|
|
memtest_fail "failed to list ISO contents while validating memtest" "$iso_path"
|
|
rm -f "$iso_files"
|
|
return 0
|
|
}
|
|
|
|
grep -q '^boot/memtest86+x64\.bin$' "$iso_files" || {
|
|
memtest_fail "memtest BIOS binary missing in ISO: boot/memtest86+x64.bin" "$iso_path"
|
|
rm -f "$iso_files"
|
|
return 0
|
|
}
|
|
grep -q '^boot/memtest86+x64\.efi$' "$iso_files" || {
|
|
memtest_fail "memtest EFI binary missing in ISO: boot/memtest86+x64.efi" "$iso_path"
|
|
rm -f "$iso_files"
|
|
return 0
|
|
}
|
|
|
|
grub_cfg="$(mktemp)"
|
|
isolinux_cfg="$(mktemp)"
|
|
|
|
iso_read_member "$iso_path" boot/grub/grub.cfg "$grub_cfg" || {
|
|
memtest_fail "failed to read boot/grub/grub.cfg from ISO" "$iso_path"
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 0
|
|
}
|
|
iso_read_member "$iso_path" isolinux/live.cfg "$isolinux_cfg" || {
|
|
memtest_fail "failed to read isolinux/live.cfg from ISO" "$iso_path"
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 0
|
|
}
|
|
|
|
grep -q 'Memory Test (memtest86+)' "$grub_cfg" || {
|
|
memtest_fail "GRUB menu entry for memtest is missing" "$iso_path"
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 0
|
|
}
|
|
grep -q '/boot/memtest86+x64\.efi' "$grub_cfg" || {
|
|
memtest_fail "GRUB memtest EFI path is missing" "$iso_path"
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 0
|
|
}
|
|
grep -q '/boot/memtest86+x64\.bin' "$grub_cfg" || {
|
|
memtest_fail "GRUB memtest BIOS path is missing" "$iso_path"
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 0
|
|
}
|
|
grep -q 'Memory Test (memtest86+)' "$isolinux_cfg" || {
|
|
memtest_fail "isolinux menu entry for memtest is missing" "$iso_path"
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 0
|
|
}
|
|
grep -q '/boot/memtest86+x64\.bin' "$isolinux_cfg" || {
|
|
memtest_fail "isolinux memtest path is missing" "$iso_path"
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
return 0
|
|
}
|
|
|
|
rm -f "$iso_files" "$grub_cfg" "$isolinux_cfg"
|
|
echo "=== memtest validation OK ==="
|
|
}
|
|
|
|
validate_iso_live_boot_entries() {
|
|
iso_path="$1"
|
|
echo "=== validating live boot entries in ISO ==="
|
|
|
|
[ -f "$iso_path" ] || {
|
|
echo "ERROR: ISO not found for live boot validation: $iso_path" >&2
|
|
exit 1
|
|
}
|
|
require_iso_reader "$iso_path" >/dev/null 2>&1 || {
|
|
echo "ERROR: ISO reader unavailable for live boot validation" >&2
|
|
exit 1
|
|
}
|
|
|
|
grub_cfg="$(mktemp)"
|
|
isolinux_cfg="$(mktemp)"
|
|
|
|
iso_read_member "$iso_path" boot/grub/grub.cfg "$grub_cfg" || {
|
|
echo "ERROR: failed to read boot/grub/grub.cfg from ISO" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
iso_read_member "$iso_path" isolinux/live.cfg "$isolinux_cfg" || {
|
|
echo "ERROR: failed to read isolinux/live.cfg from ISO" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
|
|
if grep -q '@APPEND_LIVE@\|@KERNEL_LIVE@\|@INITRD_LIVE@' "$grub_cfg" "$isolinux_cfg"; then
|
|
echo "ERROR: unresolved live-build placeholders remain in ISO bootloader config" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
fi
|
|
|
|
grep -q 'menuentry "EASY-BEE v' "$grub_cfg" || {
|
|
echo "ERROR: GRUB default EASY-BEE entry is missing" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
grep -q 'menuentry "EASY-BEE v.* -- load to RAM (toram)"' "$grub_cfg" || {
|
|
echo "ERROR: GRUB toram entry is missing" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
grep -q 'linux .*boot=live ' "$grub_cfg" || {
|
|
echo "ERROR: GRUB live entry is missing boot=live" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
grep -q 'linux .*boot=live .*toram ' "$grub_cfg" || {
|
|
echo "ERROR: GRUB toram entry is missing boot=live or toram" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
grep -q 'linux .*live-media-label=EASY_BEE_' "$grub_cfg" || {
|
|
echo "ERROR: GRUB live entry is missing live-media-label pinning" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
|
|
grep -q 'append .*boot=live ' "$isolinux_cfg" || {
|
|
echo "ERROR: isolinux live entry is missing boot=live" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
grep -q 'append .*boot=live .*toram ' "$isolinux_cfg" || {
|
|
echo "ERROR: isolinux toram entry is missing boot=live or toram" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
grep -q 'append .*live-media-label=EASY_BEE_' "$isolinux_cfg" || {
|
|
echo "ERROR: isolinux live entry is missing live-media-label pinning" >&2
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
exit 1
|
|
}
|
|
|
|
rm -f "$grub_cfg" "$isolinux_cfg"
|
|
echo "=== live boot validation OK ==="
|
|
}
|
|
|
|
validate_iso_grub_assets() {
|
|
iso_path="$1"
|
|
echo "=== validating GRUB assets in ISO ==="
|
|
|
|
[ -f "$iso_path" ] || {
|
|
echo "ERROR: ISO not found for GRUB asset validation: $iso_path" >&2
|
|
exit 1
|
|
}
|
|
require_iso_reader "$iso_path" >/dev/null 2>&1 || {
|
|
echo "ERROR: ISO reader unavailable for GRUB asset validation" >&2
|
|
exit 1
|
|
}
|
|
|
|
iso_files="$(mktemp)"
|
|
iso_list_files "$iso_path" > "$iso_files" || {
|
|
echo "ERROR: failed to list ISO files for GRUB asset validation" >&2
|
|
rm -f "$iso_files"
|
|
exit 1
|
|
}
|
|
|
|
for required in \
|
|
boot/grub/config.cfg \
|
|
boot/grub/grub.cfg; do
|
|
grep -q "^${required}$" "$iso_files" || {
|
|
echo "ERROR: missing GRUB asset in ISO: ${required}" >&2
|
|
rm -f "$iso_files"
|
|
exit 1
|
|
}
|
|
done
|
|
|
|
rm -f "$iso_files"
|
|
echo "=== GRUB asset validation OK ==="
|
|
}
|
|
|
|
validate_iso_nvidia_runtime() {
|
|
iso_path="$1"
|
|
[ "$BEE_GPU_VENDOR" = "nvidia" ] || return 0
|
|
|
|
echo "=== validating NVIDIA runtime in ISO ==="
|
|
|
|
[ -f "$iso_path" ] || nvidia_runtime_fail "ISO not found for NVIDIA runtime validation: $iso_path"
|
|
require_iso_reader "$iso_path" >/dev/null 2>&1 || nvidia_runtime_fail "ISO reader unavailable for NVIDIA runtime validation"
|
|
command -v unsquashfs >/dev/null 2>&1 || nvidia_runtime_fail "unsquashfs is required for NVIDIA runtime validation"
|
|
|
|
squashfs_tmp="$(mktemp)"
|
|
squashfs_list="$(mktemp)"
|
|
iso_files="$(mktemp)"
|
|
iso_list_files "$iso_path" > "$iso_files" || {
|
|
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
|
nvidia_runtime_fail "failed to list ISO files for NVIDIA runtime validation"
|
|
}
|
|
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" || {
|
|
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
|
nvidia_runtime_fail "dcgmi missing from final NVIDIA ISO"
|
|
}
|
|
grep -Eq 'usr/bin/nv-hostengine$' "$squashfs_list" || {
|
|
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
|
nvidia_runtime_fail "nv-hostengine missing from final NVIDIA ISO"
|
|
}
|
|
grep -Eq 'usr/bin/dcgmproftester([0-9]+)?$' "$squashfs_list" || {
|
|
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
|
nvidia_runtime_fail "dcgmproftester missing from final NVIDIA ISO"
|
|
}
|
|
|
|
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
|
echo "=== NVIDIA runtime validation OK ==="
|
|
}
|
|
|
|
append_memtest_grub_entry() {
|
|
grub_cfg="$1"
|
|
[ -f "$grub_cfg" ] || return 1
|
|
grep -q 'Memory Test (memtest86+)' "$grub_cfg" && return 0
|
|
grep -q '### BEE MEMTEST ###' "$grub_cfg" && return 0
|
|
|
|
cat >> "$grub_cfg" <<'EOF'
|
|
|
|
### BEE MEMTEST ###
|
|
if [ "${grub_platform}" = "efi" ]; then
|
|
menuentry "Memory Test (memtest86+)" {
|
|
chainloader /boot/memtest86+x64.efi
|
|
}
|
|
else
|
|
menuentry "Memory Test (memtest86+)" {
|
|
linux16 /boot/memtest86+x64.bin
|
|
}
|
|
fi
|
|
### /BEE MEMTEST ###
|
|
EOF
|
|
}
|
|
|
|
append_memtest_isolinux_entry() {
|
|
isolinux_cfg="$1"
|
|
[ -f "$isolinux_cfg" ] || return 1
|
|
grep -q 'Memory Test (memtest86+)' "$isolinux_cfg" && return 0
|
|
grep -q '### BEE MEMTEST ###' "$isolinux_cfg" && return 0
|
|
|
|
cat >> "$isolinux_cfg" <<'EOF'
|
|
|
|
# ### BEE MEMTEST ###
|
|
label memtest
|
|
menu label ^Memory Test (memtest86+)
|
|
linux /boot/memtest86+x64.bin
|
|
# ### /BEE MEMTEST ###
|
|
EOF
|
|
}
|
|
|
|
extract_live_grub_entry() {
|
|
cfg="$1"
|
|
live_linux="$(awk '/^[[:space:]]*linux[[:space:]]+\/live\// { print; exit }' "$cfg")"
|
|
live_initrd="$(awk '/^[[:space:]]*initrd[[:space:]]+\/live\// { print; exit }' "$cfg")"
|
|
[ -n "$live_linux" ] || return 1
|
|
[ -n "$live_initrd" ] || return 1
|
|
|
|
grub_kernel="$(printf '%s\n' "$live_linux" | awk '{print $2}')"
|
|
grub_append="$(printf '%s\n' "$live_linux" | cut -d' ' -f3-)"
|
|
grub_initrd="$(printf '%s\n' "$live_initrd" | awk '{print $2}')"
|
|
[ -n "$grub_kernel" ] || return 1
|
|
[ -n "$grub_append" ] || return 1
|
|
[ -n "$grub_initrd" ] || return 1
|
|
return 0
|
|
}
|
|
|
|
load_live_build_append() {
|
|
lb_dir="$1"
|
|
binary_cfg="$lb_dir/config/binary"
|
|
[ -f "$binary_cfg" ] || return 1
|
|
|
|
# config/binary is generated by live-build and contains shell variable
|
|
# assignments such as LB_BOOTAPPEND_LIVE="boot=live ...".
|
|
# shellcheck disable=SC1090
|
|
. "$binary_cfg"
|
|
|
|
[ -n "${LB_BOOTAPPEND_LIVE:-}" ] || return 1
|
|
live_build_append="$LB_BOOTAPPEND_LIVE"
|
|
return 0
|
|
}
|
|
|
|
extract_live_isolinux_entry() {
|
|
cfg="$1"
|
|
isolinux_linux="$(awk '/^[[:space:]]*linux[[:space:]]+\/live\// { print; exit }' "$cfg")"
|
|
isolinux_initrd="$(awk '/^[[:space:]]*initrd[[:space:]]+\/live\// { print; exit }' "$cfg")"
|
|
isolinux_append="$(awk '/^[[:space:]]*append[[:space:]]+/ { sub(/^[[:space:]]*append[[:space:]]+/, ""); print; exit }' "$cfg")"
|
|
[ -n "$isolinux_linux" ] || return 1
|
|
[ -n "$isolinux_initrd" ] || return 1
|
|
[ -n "$isolinux_append" ] || return 1
|
|
|
|
isolinux_kernel="$(printf '%s\n' "$isolinux_linux" | awk '{print $2}')"
|
|
isolinux_initrd_path="$(printf '%s\n' "$isolinux_initrd" | awk '{print $2}')"
|
|
[ -n "$isolinux_kernel" ] || return 1
|
|
[ -n "$isolinux_initrd_path" ] || return 1
|
|
return 0
|
|
}
|
|
|
|
write_canonical_grub_cfg() {
|
|
cfg="$1"
|
|
kernel="$2"
|
|
append_live="$3"
|
|
initrd="$4"
|
|
version_label="${PROJECT_VERSION_EFFECTIVE}"
|
|
|
|
cat > "$cfg" <<EOF
|
|
source /boot/grub/config.cfg
|
|
|
|
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
|
|
initrd ${initrd}
|
|
}
|
|
|
|
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
|
|
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
|
|
menuentry "Memory Test (memtest86+)" {
|
|
chainloader /boot/memtest86+x64.efi
|
|
}
|
|
else
|
|
menuentry "Memory Test (memtest86+)" {
|
|
linux16 /boot/memtest86+x64.bin
|
|
}
|
|
fi
|
|
|
|
if [ "\${grub_platform}" = "efi" ]; then
|
|
menuentry "UEFI Firmware Settings" {
|
|
fwsetup
|
|
}
|
|
fi
|
|
EOF
|
|
}
|
|
|
|
write_canonical_isolinux_cfg() {
|
|
cfg="$1"
|
|
kernel="$2"
|
|
initrd="$3"
|
|
append_live="$4"
|
|
version_label="${PROJECT_VERSION_EFFECTIVE}"
|
|
|
|
cat > "$cfg" <<EOF
|
|
label live-@FLAVOUR@-normal
|
|
menu label ^EASY-BEE v${version_label}
|
|
linux ${kernel}
|
|
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
|
|
|
|
label live-@FLAVOUR@-toram
|
|
menu label EASY-BEE v${version_label} (^load to RAM)
|
|
menu default
|
|
linux ${kernel}
|
|
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
|
|
|
|
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
|
|
menu label EASY-BEE (^NVIDIA GSP=off)
|
|
linux ${kernel}
|
|
initrd ${initrd}
|
|
append ${append_live} nomodeset 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@-kms
|
|
menu label EASY-BEE (^KMS, no nomodeset)
|
|
linux ${kernel}
|
|
initrd ${initrd}
|
|
append ${append_live} 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@-kms-gsp-off
|
|
menu label EASY-BEE (KMS, ^GSP=off)
|
|
linux ${kernel}
|
|
initrd ${initrd}
|
|
append ${append_live} 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@-failsafe
|
|
menu label EASY-BEE (^fail-safe)
|
|
linux ${kernel}
|
|
initrd ${initrd}
|
|
append ${append_live} nomodeset bee.nvidia.mode=gsp-off noapic noapm nodma nomce nolapic nosmp vga=normal net.ifnames=0 biosdevname=0
|
|
|
|
label memtest
|
|
menu label ^Memory Test (memtest86+)
|
|
linux /boot/memtest86+x64.bin
|
|
EOF
|
|
}
|
|
|
|
enforce_live_build_bootloader_assets() {
|
|
lb_dir="$1"
|
|
grub_cfg="$lb_dir/binary/boot/grub/grub.cfg"
|
|
grub_dir="$lb_dir/binary/boot/grub"
|
|
isolinux_cfg="$lb_dir/binary/isolinux/live.cfg"
|
|
|
|
if ! load_live_build_append "$lb_dir"; then
|
|
echo "bootloader sync: WARNING: could not load LB_BOOTAPPEND_LIVE from $lb_dir/config/binary" >&2
|
|
live_build_append=""
|
|
fi
|
|
|
|
if [ -f "$grub_cfg" ]; then
|
|
if extract_live_grub_entry "$grub_cfg"; then
|
|
cp "${BUILDER_DIR}/config/bootloaders/grub-efi/config.cfg" "$grub_dir/config.cfg"
|
|
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"
|
|
else
|
|
echo "bootloader sync: WARNING: could not extract live entry from $grub_cfg" >&2
|
|
fi
|
|
fi
|
|
|
|
if [ -f "$isolinux_cfg" ]; then
|
|
if extract_live_isolinux_entry "$isolinux_cfg"; then
|
|
write_canonical_isolinux_cfg "$isolinux_cfg" "$isolinux_kernel" "$isolinux_initrd_path" "${live_build_append:-$isolinux_append}"
|
|
echo "bootloader sync: rewrote binary/isolinux/live.cfg with canonical EASY-BEE menu"
|
|
else
|
|
echo "bootloader sync: WARNING: could not extract live entry from $isolinux_cfg" >&2
|
|
fi
|
|
fi
|
|
}
|
|
|
|
copy_memtest_from_deb() {
|
|
deb="$1"
|
|
dst_boot="$2"
|
|
tmpdir="$(mktemp -d)"
|
|
|
|
dpkg-deb -x "$deb" "$tmpdir"
|
|
for f in memtest86+x64.bin memtest86+x64.efi; do
|
|
if [ -f "$tmpdir/boot/$f" ]; then
|
|
cp "$tmpdir/boot/$f" "$dst_boot/$f"
|
|
fi
|
|
done
|
|
rm -rf "$tmpdir"
|
|
}
|
|
|
|
reset_live_build_stage() {
|
|
lb_dir="$1"
|
|
stage="$2"
|
|
|
|
for root in \
|
|
"$lb_dir/.build" \
|
|
"$lb_dir/.stage" \
|
|
"$lb_dir/auto"; do
|
|
[ -d "$root" ] || continue
|
|
find "$root" -maxdepth 1 \( -name "${stage}" -o -name "${stage}.*" -o -name "*${stage}*" \) -exec rm -rf {} + 2>/dev/null || true
|
|
done
|
|
}
|
|
|
|
# Marker written after every successful full lb build for this variant
|
|
FULL_BUILD_MARKER="${BUILD_WORK_DIR}/.bee-full-build-marker"
|
|
|
|
# Returns 0 if full lb build is needed, 1 if fast-path is safe.
|
|
# Fast-path is safe when only light files changed since the last full build
|
|
# (Go source, overlay scripts/configs). Heavy changes (VERSIONS, package lists,
|
|
# hooks, archives, Dockerfile, auto/config) require a full lb build.
|
|
needs_full_build() {
|
|
[ -f "${FULL_BUILD_MARKER}" ] || return 0
|
|
[ -f "${BUILD_WORK_DIR}/live-image-amd64.hybrid.iso" ] || return 0
|
|
# Accept any versioned squashfs (filesystem-v*.squashfs or legacy filesystem.squashfs)
|
|
_any_sq=$(find "${BUILD_WORK_DIR}/binary/live" -maxdepth 1 \
|
|
-name 'filesystem*.squashfs' 2>/dev/null | head -1)
|
|
[ -n "$_any_sq" ] || return 0
|
|
|
|
_heavy=$(find \
|
|
"${BUILDER_DIR}/VERSIONS" \
|
|
"${BUILDER_DIR}/auto/config" \
|
|
"${BUILDER_DIR}/Dockerfile" \
|
|
"${BUILDER_DIR}/config/package-lists" \
|
|
"${BUILDER_DIR}/config/hooks" \
|
|
"${BUILDER_DIR}/config/archives" \
|
|
"${BUILDER_DIR}/config/bootloaders" \
|
|
-newer "${FULL_BUILD_MARKER}" 2>/dev/null | head -1)
|
|
|
|
if [ -n "$_heavy" ]; then
|
|
echo "=== full build required: heavy config changed: $(basename "$_heavy") ==="
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
# Fast-path: unsquash existing filesystem, rsync overlay on top, repack.
|
|
# Requires ~10 GB free in BEE_CACHE_DIR for the unpacked squashfs.
|
|
fast_path_repack_squashfs() {
|
|
_old_sq=$(find "${BUILD_WORK_DIR}/binary/live" -maxdepth 1 \
|
|
-name 'filesystem*.squashfs' | sort | head -1)
|
|
_sq="${BUILD_WORK_DIR}/binary/live/${SQUASHFS_FILENAME}"
|
|
_tmp="${BEE_CACHE_DIR}/fast-unsquash-${BUILD_VARIANT}"
|
|
echo "=== fast-path: unsquash $(basename "$_old_sq") ($(du -sh "$_old_sq" | cut -f1) compressed) ==="
|
|
rm -rf "$_tmp"
|
|
unsquashfs -d "$_tmp" "$_old_sq"
|
|
echo "=== fast-path: syncing overlay stage ==="
|
|
rsync -a --checksum "${OVERLAY_STAGE_DIR}/" "$_tmp/"
|
|
echo "=== fast-path: repacking as ${SQUASHFS_FILENAME} ==="
|
|
_sq_new="${_sq}.new"
|
|
rm -f "$_sq_new"
|
|
mksquashfs "$_tmp" "$_sq_new" -comp zstd -b 1048576 -noappend -no-progress -no-xattrs
|
|
mv "$_sq_new" "$_sq"
|
|
rm -rf "$_tmp"
|
|
[ "$_old_sq" != "$_sq" ] && rm -f "$_old_sq"
|
|
echo "=== fast-path: squashfs repacked ($(du -sh "$_sq" | cut -f1)) ==="
|
|
}
|
|
|
|
# Fast-path: rebuild ISO replacing the squashfs via xorriso.
|
|
# Boot structure (El Torito, EFI, MBR hybrid) is replayed from the prior ISO.
|
|
fast_path_rebuild_iso() {
|
|
_sq="${BUILD_WORK_DIR}/binary/live/${SQUASHFS_FILENAME}"
|
|
_prior="${BUILD_WORK_DIR}/live-image-amd64.hybrid.iso"
|
|
_new="${BUILD_WORK_DIR}/live-image-amd64.hybrid.iso.new"
|
|
echo "=== fast-path: rebuilding ISO with xorriso ==="
|
|
rm -f "$_new"
|
|
# Remove any old squashfs entries from the prior ISO before adding the new one
|
|
_old_entries=$(xorriso -indev "$_prior" -find /live -name 'filesystem*.squashfs' -- 2>/dev/null \
|
|
| grep -E '^/live/filesystem.*\.squashfs$' || true)
|
|
_rm_args=""
|
|
for _e in $_old_entries; do
|
|
_rm_args="$_rm_args -rm $_e --"
|
|
done
|
|
# shellcheck disable=SC2086
|
|
xorriso \
|
|
-indev "$_prior" \
|
|
-outdev "$_new" \
|
|
${_rm_args} \
|
|
-map "$_sq" /live/${SQUASHFS_FILENAME} \
|
|
-boot_image any replay \
|
|
-commit
|
|
mv "$_new" "$_prior"
|
|
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 -no-xattrs >/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 -no-xattrs >/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 -no-xattrs >/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() {
|
|
lb_dir="$1"
|
|
iso_path="$2"
|
|
binary_boot="$lb_dir/binary/boot"
|
|
grub_cfg="$lb_dir/binary/boot/grub/grub.cfg"
|
|
isolinux_cfg="$lb_dir/binary/isolinux/live.cfg"
|
|
|
|
echo "=== attempting memtest recovery in binary tree ==="
|
|
|
|
mkdir -p "$binary_boot"
|
|
|
|
for root in \
|
|
"$lb_dir/chroot/boot" \
|
|
"/boot"; do
|
|
for f in memtest86+x64.bin memtest86+x64.efi; do
|
|
if [ ! -f "$binary_boot/$f" ] && [ -f "$root/$f" ]; then
|
|
cp "$root/$f" "$binary_boot/$f"
|
|
echo "memtest recovery: copied $f from $root"
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [ ! -f "$binary_boot/memtest86+x64.bin" ] || [ ! -f "$binary_boot/memtest86+x64.efi" ]; then
|
|
for dir in \
|
|
"$lb_dir/cache/packages.binary" \
|
|
"$lb_dir/cache/packages.chroot" \
|
|
"$lb_dir/chroot/var/cache/apt/archives" \
|
|
"${BEE_CACHE_DIR:-${DIST_DIR}/cache}/lb-packages" \
|
|
"/var/cache/apt/archives"; do
|
|
[ -d "$dir" ] || continue
|
|
deb="$(find "$dir" -maxdepth 1 -type f -name 'memtest86+*.deb' 2>/dev/null | head -1)"
|
|
[ -n "$deb" ] || continue
|
|
echo "memtest recovery: extracting payload from $deb"
|
|
copy_memtest_from_deb "$deb" "$binary_boot"
|
|
break
|
|
done
|
|
fi
|
|
|
|
if [ ! -f "$binary_boot/memtest86+x64.bin" ] || [ ! -f "$binary_boot/memtest86+x64.efi" ]; then
|
|
tmpdl="$(mktemp -d)"
|
|
if (
|
|
cd "$tmpdl" && apt-get download memtest86+ >/dev/null 2>&1
|
|
); then
|
|
deb="$(find "$tmpdl" -maxdepth 1 -type f -name 'memtest86+*.deb' 2>/dev/null | head -1)"
|
|
if [ -n "$deb" ]; then
|
|
echo "memtest recovery: downloaded $deb"
|
|
copy_memtest_from_deb "$deb" "$binary_boot"
|
|
fi
|
|
fi
|
|
rm -rf "$tmpdl"
|
|
fi
|
|
|
|
if [ -f "$grub_cfg" ]; then
|
|
append_memtest_grub_entry "$grub_cfg" && echo "memtest recovery: ensured GRUB entry"
|
|
else
|
|
echo "memtest recovery: WARNING: missing $grub_cfg"
|
|
fi
|
|
|
|
if [ -f "$isolinux_cfg" ]; then
|
|
append_memtest_isolinux_entry "$isolinux_cfg" && echo "memtest recovery: ensured isolinux entry"
|
|
else
|
|
echo "memtest recovery: WARNING: missing $isolinux_cfg"
|
|
fi
|
|
|
|
reset_live_build_stage "$lb_dir" "binary_checksums"
|
|
reset_live_build_stage "$lb_dir" "binary_iso"
|
|
reset_live_build_stage "$lb_dir" "binary_zsync"
|
|
|
|
run_optional_step_sh "rebuild live-build checksums after memtest recovery" "91-lb-checksums" "lb binary_checksums 2>&1"
|
|
run_optional_step_sh "rebuild ISO after memtest recovery" "92-lb-binary-iso" "rm -f '$iso_path' && lb binary_iso 2>&1"
|
|
run_optional_step_sh "rebuild zsync after memtest recovery" "93-lb-zsync" "lb binary_zsync 2>&1"
|
|
|
|
if [ ! -f "$iso_path" ]; then
|
|
memtest_fail "ISO rebuild was skipped or failed after memtest recovery: $iso_path" "$iso_path"
|
|
fi
|
|
}
|
|
|
|
PROJECT_VERSION_EFFECTIVE="$(resolve_project_version)"
|
|
SQUASHFS_FILENAME="filesystem-v${PROJECT_VERSION_EFFECTIVE}.squashfs"
|
|
ISO_BASENAME="easy-bee-${BUILD_VARIANT}-v${PROJECT_VERSION_EFFECTIVE}-amd64"
|
|
# Versioned output directory: dist/easy-bee-v4.1/ — all final artefacts live here.
|
|
OUT_DIR="${DIST_DIR}/easy-bee-v${PROJECT_VERSION_EFFECTIVE}"
|
|
ISO_VERSION_LABEL_TOKEN="$(printf '%s' "${PROJECT_VERSION_EFFECTIVE}" | tr '[:lower:].-' '[:upper:]__')"
|
|
mkdir -p "${OUT_DIR}"
|
|
LOG_DIR="${OUT_DIR}/${ISO_BASENAME}.logs"
|
|
LOG_ARCHIVE="${OUT_DIR}/${ISO_BASENAME}.logs.tar.gz"
|
|
ISO_OUT="${OUT_DIR}/${ISO_BASENAME}.iso"
|
|
LOG_OUT="${LOG_DIR}/build.log"
|
|
|
|
cleanup_build_log() {
|
|
status="${1:-$?}"
|
|
trap - EXIT INT TERM HUP
|
|
|
|
if [ "${STEP_LOG_ACTIVE:-0}" = "1" ]; then
|
|
cleanup_step_log "${status}" || true
|
|
fi
|
|
|
|
if [ "${BUILD_LOG_ACTIVE:-0}" = "1" ]; then
|
|
BUILD_LOG_ACTIVE=0
|
|
exec 1>&3 2>&4
|
|
exec 3>&- 4>&-
|
|
if [ -n "${BUILD_TEE_PID:-}" ]; then
|
|
wait "${BUILD_TEE_PID}" 2>/dev/null || true
|
|
fi
|
|
rm -f "${BUILD_LOG_PIPE}"
|
|
fi
|
|
|
|
if [ -n "${LOG_DIR:-}" ] && [ -d "${LOG_DIR}" ] && command -v tar >/dev/null 2>&1; then
|
|
rm -f "${LOG_ARCHIVE}"
|
|
tar -czf "${LOG_ARCHIVE}" -C "$(dirname "${LOG_DIR}")" "$(basename "${LOG_DIR}")" 2>/dev/null || true
|
|
rm -rf "${LOG_DIR}"
|
|
fi
|
|
|
|
exit "${status}"
|
|
}
|
|
|
|
start_build_log() {
|
|
command -v tee >/dev/null 2>&1 || {
|
|
echo "ERROR: tee is required for build logging" >&2
|
|
exit 1
|
|
}
|
|
|
|
rm -rf "${LOG_DIR}"
|
|
rm -f "${LOG_ARCHIVE}"
|
|
mkdir -p "${LOG_DIR}"
|
|
BUILD_LOG_PIPE="$(mktemp -u "${TMPDIR:-/tmp}/bee-build-log.XXXXXX")"
|
|
mkfifo "${BUILD_LOG_PIPE}"
|
|
|
|
exec 3>&1 4>&2
|
|
tee "${LOG_OUT}" < "${BUILD_LOG_PIPE}" &
|
|
BUILD_TEE_PID=$!
|
|
exec > "${BUILD_LOG_PIPE}" 2>&1
|
|
BUILD_LOG_ACTIVE=1
|
|
|
|
trap 'cleanup_build_log "$?"' EXIT INT TERM HUP
|
|
|
|
echo "=== build log dir: ${LOG_DIR} ==="
|
|
echo "=== build log: ${LOG_OUT} ==="
|
|
echo "=== build log archive: ${LOG_ARCHIVE} ==="
|
|
}
|
|
|
|
cleanup_step_log() {
|
|
status="${1:-$?}"
|
|
|
|
if [ "${STEP_LOG_ACTIVE:-0}" = "1" ]; then
|
|
STEP_LOG_ACTIVE=0
|
|
exec 1>&5 2>&6
|
|
exec 5>&- 6>&-
|
|
if [ -n "${STEP_TEE_PID:-}" ]; then
|
|
wait "${STEP_TEE_PID}" 2>/dev/null || true
|
|
fi
|
|
rm -f "${STEP_LOG_PIPE}"
|
|
fi
|
|
|
|
return "${status}"
|
|
}
|
|
|
|
run_step() {
|
|
step_name="$1"
|
|
step_slug="$2"
|
|
shift 2
|
|
|
|
step_log="${LOG_DIR}/${step_slug}.log"
|
|
echo ""
|
|
echo "=== step: ${step_name} ==="
|
|
echo "=== step log: ${step_log} ==="
|
|
|
|
STEP_LOG_PIPE="$(mktemp -u "${TMPDIR:-/tmp}/bee-step-log.XXXXXX")"
|
|
mkfifo "${STEP_LOG_PIPE}"
|
|
|
|
exec 5>&1 6>&2
|
|
tee "${step_log}" < "${STEP_LOG_PIPE}" >&5 &
|
|
STEP_TEE_PID=$!
|
|
exec > "${STEP_LOG_PIPE}" 2>&1
|
|
STEP_LOG_ACTIVE=1
|
|
|
|
set +e
|
|
"$@"
|
|
step_status=$?
|
|
set -e
|
|
|
|
cleanup_step_log "${step_status}"
|
|
if [ "${step_status}" -ne 0 ]; then
|
|
echo "ERROR: step failed: ${step_name} (see ${step_log})" >&2
|
|
exit "${step_status}"
|
|
fi
|
|
|
|
echo "=== step OK: ${step_name} ==="
|
|
}
|
|
|
|
run_step_sh() {
|
|
step_name="$1"
|
|
step_slug="$2"
|
|
step_script="$3"
|
|
|
|
run_step "${step_name}" "${step_slug}" sh -c "${step_script}"
|
|
}
|
|
|
|
run_optional_step_sh() {
|
|
step_name="$1"
|
|
step_slug="$2"
|
|
step_script="$3"
|
|
|
|
if [ "${BEE_REQUIRE_MEMTEST:-0}" = "1" ]; then
|
|
run_step_sh "${step_name}" "${step_slug}" "${step_script}"
|
|
return 0
|
|
fi
|
|
|
|
mkdir -p "${LOG_DIR}" 2>/dev/null || true
|
|
step_log="${LOG_DIR}/${step_slug}.log"
|
|
echo ""
|
|
echo "=== optional step: ${step_name} ==="
|
|
echo "=== optional step log: ${step_log} ==="
|
|
set +e
|
|
sh -c "${step_script}" > "${step_log}" 2>&1
|
|
step_status=$?
|
|
set -e
|
|
cat "${step_log}"
|
|
if [ "${step_status}" -ne 0 ]; then
|
|
echo "WARNING: optional step failed: ${step_name} (see ${step_log})" >&2
|
|
else
|
|
echo "=== optional step OK: ${step_name} ==="
|
|
fi
|
|
}
|
|
|
|
start_build_log
|
|
|
|
# Auto-detect kernel ABI: refresh apt index, then query current linux-image-amd64 dependency.
|
|
# If headers for the detected ABI are not yet installed (kernel updated since image build),
|
|
# install them on the fly so NVIDIA modules and ISO kernel always match.
|
|
if [ -z "${DEBIAN_KERNEL_ABI}" ] || [ "${DEBIAN_KERNEL_ABI}" = "auto" ]; then
|
|
echo "=== refreshing apt index to detect current kernel ABI ==="
|
|
apt-get update -qq || echo "WARNING: apt-get update failed, trying cached index"
|
|
DEBIAN_KERNEL_ABI=$(apt-cache depends linux-image-amd64 2>/dev/null \
|
|
| awk '/Depends:.*linux-image-[0-9]/{print $2}' \
|
|
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+-[0-9]+' \
|
|
| head -1)
|
|
if [ -z "${DEBIAN_KERNEL_ABI}" ]; then
|
|
echo "ERROR: could not auto-detect kernel ABI from apt-cache" >&2
|
|
echo "Hint: set DEBIAN_KERNEL_ABI=x.y.z-N in iso/builder/VERSIONS to skip auto-detection" >&2
|
|
exit 1
|
|
fi
|
|
echo "=== kernel ABI: ${DEBIAN_KERNEL_ABI} ==="
|
|
fi
|
|
|
|
# Export detected ABI so that auto/config can pin the exact kernel package
|
|
# (prevents NVIDIA module/kernel mismatch if linux-image-amd64 meta-package
|
|
# gets updated between build.sh start and lb build chroot step)
|
|
export BEE_KERNEL_ABI="${DEBIAN_KERNEL_ABI}"
|
|
|
|
KVER="${DEBIAN_KERNEL_ABI}-amd64"
|
|
if [ ! -d "/usr/src/linux-headers-${KVER}" ]; then
|
|
echo "=== installing linux-headers-${KVER} (kernel updated since image build) ==="
|
|
apt-get install -y "linux-headers-${KVER}"
|
|
fi
|
|
|
|
echo "=== bee ISO build (variant: ${BUILD_VARIANT}) ==="
|
|
echo "Debian: ${DEBIAN_VERSION}, Kernel ABI: ${DEBIAN_KERNEL_ABI}, Go: ${GO_VERSION}"
|
|
echo "Project version: ${PROJECT_VERSION_EFFECTIVE}"
|
|
echo ""
|
|
|
|
run_step "sync git submodules" "05-git-submodules" \
|
|
git -C "${REPO_ROOT}" submodule update --init --recursive
|
|
|
|
# --- compile bee binary (static, Linux amd64) ---
|
|
# Shared between variants — built once, reused on second pass.
|
|
BEE_BIN="${DIST_DIR}/bee-linux-amd64"
|
|
NEED_BUILD=1
|
|
if [ -f "$BEE_BIN" ]; then
|
|
NEWEST_SRC=$(find "${REPO_ROOT}/audit" -name '*.go' -newer "$BEE_BIN" | head -1)
|
|
[ -z "$NEWEST_SRC" ] && NEED_BUILD=0
|
|
fi
|
|
|
|
if [ "$NEED_BUILD" = "1" ]; then
|
|
run_step_sh "build bee binary" "10-build-bee" \
|
|
"cd '${REPO_ROOT}/audit' && \
|
|
env GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
|
|
go build \
|
|
-ldflags '-s -w -X main.Version=${PROJECT_VERSION_EFFECTIVE}' \
|
|
-o '${BEE_BIN}' \
|
|
./cmd/bee"
|
|
echo "binary: $BEE_BIN"
|
|
if command -v stat >/dev/null 2>&1; then
|
|
BEE_SIZE_BYTES="$(stat -c '%s' "$BEE_BIN" 2>/dev/null || stat -f '%z' "$BEE_BIN")"
|
|
else
|
|
BEE_SIZE_BYTES="$(wc -c < "$BEE_BIN" | tr -d ' ')"
|
|
fi
|
|
if command -v numfmt >/dev/null 2>&1; then
|
|
echo "size: $(numfmt --to=iec --suffix=B "$BEE_SIZE_BYTES")"
|
|
else
|
|
echo "size: ${BEE_SIZE_BYTES} bytes"
|
|
fi
|
|
else
|
|
echo "=== bee binary up to date, skipping build ==="
|
|
fi
|
|
|
|
# --- NVIDIA-only build steps ---
|
|
GPU_BURN_WORKER_BIN="${DIST_DIR}/bee-gpu-burn-worker-linux-amd64"
|
|
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
|
|
run_step "download cuBLAS/cuBLASLt/cudart ${NCCL_CUDA_VERSION} userspace" "20-cublas" \
|
|
sh "${BUILDER_DIR}/build-cublas.sh" \
|
|
"${CUBLAS_VERSION}" \
|
|
"${CUDA_USERSPACE_VERSION}" \
|
|
"${NCCL_CUDA_VERSION}" \
|
|
"${DIST_DIR}"
|
|
|
|
CUBLAS_CACHE="${DIST_DIR}/cublas-${CUBLAS_VERSION}+cuda${NCCL_CUDA_VERSION}"
|
|
|
|
echo "=== bee-gpu-burn FP4 header probe ==="
|
|
fp4_type_match="$(grep -Rsnm 1 'CUDA_R_4F_E2M1' "${CUBLAS_CACHE}/include" 2>/dev/null || true)"
|
|
fp4_scale_match="$(grep -Rsnm 1 'CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3' "${CUBLAS_CACHE}/include" 2>/dev/null || true)"
|
|
if [ -n "$fp4_type_match" ]; then
|
|
echo "fp4_header_symbol=present"
|
|
echo "$fp4_type_match"
|
|
else
|
|
echo "fp4_header_symbol=missing"
|
|
fi
|
|
if [ -n "$fp4_scale_match" ]; then
|
|
echo "fp4_scale_mode_symbol=present"
|
|
echo "$fp4_scale_match"
|
|
else
|
|
echo "fp4_scale_mode_symbol=missing"
|
|
fi
|
|
|
|
GPU_STRESS_NEED_BUILD=1
|
|
if [ -f "$GPU_BURN_WORKER_BIN" ]; then
|
|
GPU_STRESS_NEED_BUILD=0
|
|
for dep in \
|
|
"${BUILDER_DIR}/bee-gpu-stress.c" \
|
|
"${BUILDER_DIR}/VERSIONS"; do
|
|
if [ "$dep" -nt "$GPU_BURN_WORKER_BIN" ]; then
|
|
GPU_STRESS_NEED_BUILD=1
|
|
break
|
|
fi
|
|
done
|
|
if [ "$GPU_STRESS_NEED_BUILD" = "0" ] && \
|
|
find "${CUBLAS_CACHE}/include" "${CUBLAS_CACHE}/lib" -type f -newer "$GPU_BURN_WORKER_BIN" | grep -q .; then
|
|
GPU_STRESS_NEED_BUILD=1
|
|
fi
|
|
fi
|
|
|
|
if [ "$GPU_STRESS_NEED_BUILD" = "1" ]; then
|
|
run_step "build bee-gpu-burn worker" "21-gpu-burn-worker" \
|
|
gcc -O2 -s -Wall -Wextra \
|
|
-I"${CUBLAS_CACHE}/include" \
|
|
-o "$GPU_BURN_WORKER_BIN" \
|
|
"${BUILDER_DIR}/bee-gpu-stress.c" \
|
|
-ldl -lm
|
|
echo "binary: $GPU_BURN_WORKER_BIN"
|
|
else
|
|
echo "=== bee-gpu-burn worker up to date, skipping build ==="
|
|
fi
|
|
echo "=== bee-gpu-burn compiled profile probe ==="
|
|
if grep -aq 'fp4_e2m1' "$GPU_BURN_WORKER_BIN"; then
|
|
echo "fp4_profile_string=present"
|
|
else
|
|
echo "fp4_profile_string=missing"
|
|
fi
|
|
fi
|
|
|
|
echo "=== preparing staged overlay (${BUILD_VARIANT}) ==="
|
|
mkdir -p "${BUILD_WORK_DIR}" "${OVERLAY_STAGE_DIR}"
|
|
|
|
# Sync builder config into variant work dir, preserving lb cache.
|
|
sync_builder_workdir "${BUILDER_DIR}" "${BUILD_WORK_DIR}"
|
|
|
|
# Share deb package cache across variants.
|
|
# Restore: populate work dir cache from shared cache before build.
|
|
# Persist: sync back after build (done after lb build below).
|
|
LB_PKG_CACHE="${CACHE_ROOT}/lb-packages"
|
|
mkdir -p "${LB_PKG_CACHE}"
|
|
if [ -d "${BUILD_WORK_DIR}/cache/packages.chroot" ]; then
|
|
rsync -a --delete "${BUILD_WORK_DIR}/cache/packages.chroot/" "${LB_PKG_CACHE}/"
|
|
elif [ -d "${LB_PKG_CACHE}" ] && [ "$(ls -A "${LB_PKG_CACHE}" 2>/dev/null)" ]; then
|
|
mkdir -p "${BUILD_WORK_DIR}/cache/packages.chroot"
|
|
rsync -a "${LB_PKG_CACHE}/" "${BUILD_WORK_DIR}/cache/packages.chroot/"
|
|
fi
|
|
|
|
rsync -a "${OVERLAY_DIR}/" "${OVERLAY_STAGE_DIR}/"
|
|
rm -f \
|
|
"${OVERLAY_STAGE_DIR}/etc/bee-ssh-password-fallback" \
|
|
"${OVERLAY_STAGE_DIR}/etc/bee-release" \
|
|
"${OVERLAY_STAGE_DIR}/root/.ssh/authorized_keys" \
|
|
"${OVERLAY_STAGE_DIR}/usr/local/bin/bee" \
|
|
"${OVERLAY_STAGE_DIR}/usr/local/bin/john" \
|
|
"${OVERLAY_STAGE_DIR}/usr/local/lib/bee/bee-gpu-burn-worker" \
|
|
"${OVERLAY_STAGE_DIR}/usr/local/bin/bee-smoketest" \
|
|
"${OVERLAY_STAGE_DIR}/usr/local/bin/all_reduce_perf"
|
|
rm -rf \
|
|
"${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john"
|
|
|
|
# Remove NVIDIA-specific overlay files for non-nvidia variants
|
|
if [ "$BEE_GPU_VENDOR" != "nvidia" ]; then
|
|
rm -f "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-nvidia-load"
|
|
rm -f "${OVERLAY_STAGE_DIR}/etc/systemd/system/bee-nvidia.service"
|
|
fi
|
|
|
|
# --- inject authorized_keys for SSH access ---
|
|
AUTHORIZED_KEYS_FILE="${OVERLAY_STAGE_DIR}/root/.ssh/authorized_keys"
|
|
mkdir -p "${OVERLAY_STAGE_DIR}/root/.ssh"
|
|
|
|
if [ -n "$AUTH_KEYS" ]; then
|
|
cp "$AUTH_KEYS" "$AUTHORIZED_KEYS_FILE"
|
|
chmod 600 "$AUTHORIZED_KEYS_FILE"
|
|
echo "SSH authorized_keys: installed from $AUTH_KEYS"
|
|
else
|
|
> "$AUTHORIZED_KEYS_FILE"
|
|
FOUND=0
|
|
for ssh_pub in "$HOME"/.keys/*.key.pub; do
|
|
[ -f "$ssh_pub" ] || continue
|
|
cat "$ssh_pub" >> "$AUTHORIZED_KEYS_FILE"
|
|
echo "SSH: added $(basename "$ssh_pub" .key.pub)"
|
|
FOUND=$((FOUND + 1))
|
|
done
|
|
if [ "$FOUND" -gt 0 ]; then
|
|
chmod 600 "$AUTHORIZED_KEYS_FILE"
|
|
echo "SSH authorized_keys: $FOUND key(s) from ~/.keys/*.key.pub"
|
|
else
|
|
echo "WARNING: no SSH public keys found — falling back to password auth"
|
|
echo " SSH login: bee / eeb"
|
|
USE_PASSWORD_FALLBACK=1
|
|
fi
|
|
fi
|
|
|
|
if [ "${USE_PASSWORD_FALLBACK:-0}" = "1" ]; then
|
|
touch "${OVERLAY_STAGE_DIR}/etc/bee-ssh-password-fallback"
|
|
else
|
|
rm -f "${OVERLAY_STAGE_DIR}/etc/bee-ssh-password-fallback"
|
|
fi
|
|
|
|
# --- copy bee binary into overlay ---
|
|
mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/bin"
|
|
cp "${DIST_DIR}/bee-linux-amd64" "${OVERLAY_STAGE_DIR}/usr/local/bin/bee"
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee"
|
|
|
|
if [ "$BEE_GPU_VENDOR" = "nvidia" ] && [ -f "$GPU_BURN_WORKER_BIN" ]; then
|
|
mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/lib/bee" "${OVERLAY_STAGE_DIR}/usr/local/bin"
|
|
cp "${GPU_BURN_WORKER_BIN}" "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/bee-gpu-burn-worker"
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/bee-gpu-burn-worker"
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-gpu-burn" 2>/dev/null || true
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-john-gpu-stress" 2>/dev/null || true
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-nccl-gpu-stress" 2>/dev/null || true
|
|
fi
|
|
|
|
# --- inject smoketest into overlay so it runs directly on the live CD ---
|
|
cp "${BUILDER_DIR}/smoketest.sh" "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-smoketest"
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/bee-smoketest"
|
|
|
|
# --- vendor utilities (optional pre-fetched binaries) ---
|
|
for tool in storcli64 sas2ircu sas3ircu arcconf ssacli; do
|
|
if [ -f "${VENDOR_DIR}/${tool}" ]; then
|
|
cp "${VENDOR_DIR}/${tool}" "${OVERLAY_STAGE_DIR}/usr/local/bin/${tool}"
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/${tool}" || true
|
|
echo "vendor tool: ${tool} (included)"
|
|
else
|
|
echo "vendor tool: ${tool} (not found, skipped)"
|
|
fi
|
|
done
|
|
|
|
# --- NVIDIA kernel modules and userspace libs ---
|
|
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
|
|
run_step "build NVIDIA ${NVIDIA_DRIVER_VERSION} modules" "40-nvidia-module" \
|
|
sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}" "${DEBIAN_KERNEL_ABI}" "${BEE_NVIDIA_MODULE_FLAVOR}"
|
|
|
|
KVER="${DEBIAN_KERNEL_ABI}-amd64"
|
|
NVIDIA_CACHE="${DIST_DIR}/nvidia-${BEE_NVIDIA_MODULE_FLAVOR}-${NVIDIA_DRIVER_VERSION}-${KVER}"
|
|
|
|
# Inject .ko files into overlay at /usr/local/lib/nvidia/
|
|
OVERLAY_KMOD_DIR="${OVERLAY_STAGE_DIR}/usr/local/lib/nvidia"
|
|
mkdir -p "${OVERLAY_KMOD_DIR}"
|
|
cp "${NVIDIA_CACHE}/modules/"*.ko "${OVERLAY_KMOD_DIR}/"
|
|
|
|
# Inject nvidia-smi and libnvidia-ml
|
|
mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/bin" "${OVERLAY_STAGE_DIR}/usr/lib"
|
|
cp "${NVIDIA_CACHE}/bin/nvidia-smi" "${OVERLAY_STAGE_DIR}/usr/local/bin/"
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/nvidia-smi"
|
|
cp "${NVIDIA_CACHE}/bin/nvidia-bug-report.sh" "${OVERLAY_STAGE_DIR}/usr/local/bin/" 2>/dev/null || true
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/nvidia-bug-report.sh" 2>/dev/null || true
|
|
cp "${NVIDIA_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/" 2>/dev/null || true
|
|
mkdir -p "${OVERLAY_STAGE_DIR}/etc/OpenCL/vendors"
|
|
printf 'libnvidia-opencl.so.1\n' > "${OVERLAY_STAGE_DIR}/etc/OpenCL/vendors/nvidia.icd"
|
|
|
|
# Inject GSP firmware into /lib/firmware/nvidia/<version>/
|
|
if [ -d "${NVIDIA_CACHE}/firmware" ] && [ "$(ls -A "${NVIDIA_CACHE}/firmware" 2>/dev/null)" ]; then
|
|
mkdir -p "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}"
|
|
cp "${NVIDIA_CACHE}/firmware/"* "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}/"
|
|
echo "=== firmware: $(ls "${OVERLAY_STAGE_DIR}/lib/firmware/nvidia/${NVIDIA_DRIVER_VERSION}/" | wc -l) files injected ==="
|
|
fi
|
|
|
|
# --- build / download NCCL ---
|
|
run_step "download NCCL ${NCCL_VERSION}+cuda${NCCL_CUDA_VERSION}" "50-nccl" \
|
|
sh "${BUILDER_DIR}/build-nccl.sh" "${NCCL_VERSION}" "${NCCL_CUDA_VERSION}" "${DIST_DIR}" "${NCCL_SHA256:-}"
|
|
|
|
NCCL_CACHE="${DIST_DIR}/nccl-${NCCL_VERSION}+cuda${NCCL_CUDA_VERSION}"
|
|
|
|
# Inject libnccl.so.* into overlay alongside other NVIDIA userspace libs
|
|
cp "${NCCL_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/"
|
|
echo "=== NCCL: $(ls "${NCCL_CACHE}/lib/" | wc -l) files injected into /usr/lib/ ==="
|
|
|
|
# Inject cuBLAS/cuBLASLt/cudart runtime libs used by the bee-gpu-burn worker tensor-core GEMM path
|
|
cp "${CUBLAS_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/"
|
|
echo "=== cuBLAS: $(ls "${CUBLAS_CACHE}/lib/" | wc -l) files injected into /usr/lib/ ==="
|
|
|
|
# --- build nccl-tests ---
|
|
run_step "build nccl-tests ${NCCL_TESTS_VERSION}" "60-nccl-tests" \
|
|
sh "${BUILDER_DIR}/build-nccl-tests.sh" \
|
|
"${NCCL_TESTS_VERSION}" \
|
|
"${NCCL_VERSION}" \
|
|
"${NCCL_CUDA_VERSION}" \
|
|
"${DIST_DIR}" \
|
|
"${NVCC_VERSION}" \
|
|
"${DEBIAN_VERSION}"
|
|
|
|
NCCL_TESTS_CACHE="${DIST_DIR}/nccl-tests-${NCCL_TESTS_VERSION}"
|
|
cp "${NCCL_TESTS_CACHE}/bin/all_reduce_perf" "${OVERLAY_STAGE_DIR}/usr/local/bin/all_reduce_perf"
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/bin/all_reduce_perf"
|
|
cp "${NCCL_TESTS_CACHE}/lib/"* "${OVERLAY_STAGE_DIR}/usr/lib/" 2>/dev/null || true
|
|
echo "=== all_reduce_perf injected ==="
|
|
|
|
run_step "build john jumbo ${JOHN_JUMBO_COMMIT}" "70-john" \
|
|
sh "${BUILDER_DIR}/build-john.sh" "${JOHN_JUMBO_COMMIT}" "${DIST_DIR}"
|
|
JOHN_CACHE="${DIST_DIR}/john-${JOHN_JUMBO_COMMIT}"
|
|
mkdir -p "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john"
|
|
rsync -a --delete "${JOHN_CACHE}/run/" "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john/run/"
|
|
ln -sfn ../lib/bee/john/run/john "${OVERLAY_STAGE_DIR}/usr/local/bin/john"
|
|
chmod +x "${OVERLAY_STAGE_DIR}/usr/local/lib/bee/john/run/john"
|
|
echo "=== john injected ==="
|
|
fi
|
|
|
|
# --- embed build metadata ---
|
|
mkdir -p "${OVERLAY_STAGE_DIR}/etc"
|
|
BUILD_DATE="$(date +%Y-%m-%d)"
|
|
GIT_COMMIT="$(git -C "${REPO_ROOT}" rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
|
|
|
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
|
|
GPU_VERSION_LINE="NVIDIA_DRIVER_VERSION=${NVIDIA_DRIVER_VERSION}
|
|
NVIDIA_KERNEL_MODULES_FLAVOR=${BEE_NVIDIA_MODULE_FLAVOR}
|
|
NCCL_VERSION=${NCCL_VERSION}
|
|
NCCL_CUDA_VERSION=${NCCL_CUDA_VERSION}
|
|
CUBLAS_VERSION=${CUBLAS_VERSION}
|
|
CUDA_USERSPACE_VERSION=${CUDA_USERSPACE_VERSION}
|
|
NCCL_TESTS_VERSION=${NCCL_TESTS_VERSION}
|
|
JOHN_JUMBO_COMMIT=${JOHN_JUMBO_COMMIT}"
|
|
GPU_BUILD_INFO="nvidia-${BEE_NVIDIA_MODULE_FLAVOR}:${NVIDIA_DRIVER_VERSION}"
|
|
elif [ "$BEE_GPU_VENDOR" = "amd" ]; then
|
|
GPU_VERSION_LINE="ROCM_VERSION=${ROCM_VERSION}"
|
|
GPU_BUILD_INFO="rocm:${ROCM_VERSION}"
|
|
else
|
|
GPU_VERSION_LINE=""
|
|
GPU_BUILD_INFO="nogpu"
|
|
fi
|
|
|
|
cat > "${OVERLAY_STAGE_DIR}/etc/bee-release" <<EOF
|
|
BEE_VERSION=${PROJECT_VERSION_EFFECTIVE}
|
|
export BEE_VERSION
|
|
BEE_ISO_VERSION=${PROJECT_VERSION_EFFECTIVE}
|
|
BEE_AUDIT_VERSION=${PROJECT_VERSION_EFFECTIVE}
|
|
BEE_BUILD_VARIANT=${BUILD_VARIANT}
|
|
BEE_GPU_VENDOR=${BEE_GPU_VENDOR}
|
|
BUILD_DATE=${BUILD_DATE}
|
|
GIT_COMMIT=${GIT_COMMIT}
|
|
DEBIAN_VERSION=${DEBIAN_VERSION}
|
|
DEBIAN_KERNEL_ABI=${DEBIAN_KERNEL_ABI}
|
|
${GPU_VERSION_LINE}
|
|
EOF
|
|
|
|
# Write GPU vendor marker for hooks
|
|
echo "${BEE_GPU_VENDOR}" > "${OVERLAY_STAGE_DIR}/etc/bee-gpu-vendor"
|
|
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
|
|
echo "${BEE_NVIDIA_MODULE_FLAVOR}" > "${OVERLAY_STAGE_DIR}/etc/bee-nvidia-modules-flavor"
|
|
else
|
|
rm -f "${OVERLAY_STAGE_DIR}/etc/bee-nvidia-modules-flavor"
|
|
fi
|
|
|
|
# Patch motd with build info
|
|
BEE_BUILD_INFO="${BUILD_DATE} git:${GIT_COMMIT} debian:${DEBIAN_VERSION} ${GPU_BUILD_INFO}"
|
|
if [ -f "${OVERLAY_STAGE_DIR}/etc/motd" ]; then
|
|
sed "s/%%BUILD_INFO%%/${BEE_BUILD_INFO}/" "${OVERLAY_STAGE_DIR}/etc/motd" \
|
|
> "${OVERLAY_STAGE_DIR}/etc/motd.patched"
|
|
mv "${OVERLAY_STAGE_DIR}/etc/motd.patched" "${OVERLAY_STAGE_DIR}/etc/motd"
|
|
fi
|
|
|
|
# --- copy variant-specific package list, remove all other variant lists ---
|
|
# live-build picks up ALL .list.chroot files — delete other variants to avoid conflicts.
|
|
cp "${BUILD_WORK_DIR}/config/package-lists/bee-${BEE_GPU_VENDOR}.list.chroot" \
|
|
"${BUILD_WORK_DIR}/config/package-lists/bee-gpu.list.chroot"
|
|
rm -f "${BUILD_WORK_DIR}/config/package-lists/bee-nvidia.list.chroot" \
|
|
"${BUILD_WORK_DIR}/config/package-lists/bee-amd.list.chroot" \
|
|
"${BUILD_WORK_DIR}/config/package-lists/bee-nogpu.list.chroot"
|
|
|
|
# --- remove archives for the other vendor(s) ---
|
|
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
|
|
rm -f "${BUILD_WORK_DIR}/config/archives/rocm.list.chroot" \
|
|
"${BUILD_WORK_DIR}/config/archives/rocm.key.chroot"
|
|
elif [ "$BEE_GPU_VENDOR" = "amd" ]; then
|
|
rm -f "${BUILD_WORK_DIR}/config/archives/nvidia-cuda.list.chroot" \
|
|
"${BUILD_WORK_DIR}/config/archives/nvidia-cuda.key.chroot"
|
|
else
|
|
# nogpu: remove both
|
|
rm -f "${BUILD_WORK_DIR}/config/archives/rocm.list.chroot" \
|
|
"${BUILD_WORK_DIR}/config/archives/rocm.key.chroot" \
|
|
"${BUILD_WORK_DIR}/config/archives/nvidia-cuda.list.chroot" \
|
|
"${BUILD_WORK_DIR}/config/archives/nvidia-cuda.key.chroot"
|
|
fi
|
|
|
|
# --- substitute version placeholders in package list and archive ---
|
|
if [ "$BEE_GPU_VENDOR" = "nvidia" ]; then
|
|
sed -i \
|
|
-e "s/%%NVIDIA_FABRICMANAGER_VERSION%%/${NVIDIA_FABRICMANAGER_VERSION}/g" \
|
|
-e "s/%%DCGM_VERSION%%/${DCGM_VERSION}/g" \
|
|
"${BUILD_WORK_DIR}/config/package-lists/bee-gpu.list.chroot"
|
|
elif [ "$BEE_GPU_VENDOR" = "amd" ]; then
|
|
sed -i \
|
|
-e "s/%%ROCM_VERSION%%/${ROCM_VERSION}/g" \
|
|
-e "s/%%ROCM_SMI_VERSION%%/${ROCM_SMI_VERSION}/g" \
|
|
-e "s/%%ROCM_BANDWIDTH_TEST_VERSION%%/${ROCM_BANDWIDTH_TEST_VERSION}/g" \
|
|
-e "s/%%ROCM_VALIDATION_SUITE_VERSION%%/${ROCM_VALIDATION_SUITE_VERSION}/g" \
|
|
-e "s/%%ROCBLAS_VERSION%%/${ROCBLAS_VERSION}/g" \
|
|
-e "s/%%ROCRAND_VERSION%%/${ROCRAND_VERSION}/g" \
|
|
-e "s/%%HIP_RUNTIME_AMD_VERSION%%/${HIP_RUNTIME_AMD_VERSION}/g" \
|
|
-e "s/%%HIPBLASLT_VERSION%%/${HIPBLASLT_VERSION}/g" \
|
|
-e "s/%%COMGR_VERSION%%/${COMGR_VERSION}/g" \
|
|
"${BUILD_WORK_DIR}/config/package-lists/bee-gpu.list.chroot"
|
|
if [ -f "${BUILD_WORK_DIR}/config/archives/rocm.list.chroot" ]; then
|
|
sed -i \
|
|
-e "s/%%ROCM_VERSION%%/${ROCM_VERSION}/g" \
|
|
"${BUILD_WORK_DIR}/config/archives/rocm.list.chroot"
|
|
fi
|
|
fi
|
|
|
|
# --- sync overlay into live-build includes.chroot ---
|
|
LB_DIR="${BUILD_WORK_DIR}"
|
|
LB_INCLUDES="${LB_DIR}/config/includes.chroot"
|
|
mkdir -p "${LB_INCLUDES}"
|
|
rsync -a "${OVERLAY_STAGE_DIR}/" "${LB_INCLUDES}/"
|
|
|
|
# Ensure SSH authorized_keys perms are correct (rsync may alter)
|
|
if [ -f "${LB_INCLUDES}/root/.ssh/authorized_keys" ]; then
|
|
chmod 700 "${LB_INCLUDES}/root/.ssh"
|
|
chmod 600 "${LB_INCLUDES}/root/.ssh/authorized_keys"
|
|
fi
|
|
|
|
# --- auto fast-path: squashfs surgery if only light files changed ---
|
|
if ! needs_full_build; then
|
|
echo "=== fast-path build (no heavy config changes since last full build) ==="
|
|
fast_path_repack_squashfs
|
|
fast_path_rebuild_iso
|
|
ISO_RAW="${LB_DIR}/live-image-amd64.hybrid.iso"
|
|
validate_iso_live_boot_entries "$ISO_RAW"
|
|
validate_iso_grub_assets "$ISO_RAW"
|
|
validate_iso_nvidia_runtime "$ISO_RAW"
|
|
cp "$ISO_RAW" "$ISO_OUT"
|
|
echo ""
|
|
echo "=== done (${BUILD_VARIANT}, fast-path) ==="
|
|
echo "ISO: $ISO_OUT"
|
|
exit 0
|
|
fi
|
|
|
|
# --- build ISO using live-build ---
|
|
echo ""
|
|
echo "=== building ISO (variant: ${BUILD_VARIANT}) ==="
|
|
|
|
# Export for auto/config
|
|
BEE_GPU_VENDOR_UPPER="$(echo "${BUILD_VARIANT}" | tr 'a-z-' 'A-Z_')"
|
|
BEE_ISO_VOLUME="EASY_BEE_${BEE_GPU_VENDOR_UPPER}_V${ISO_VERSION_LABEL_TOKEN}"
|
|
export BEE_GPU_VENDOR_UPPER BEE_ISO_VOLUME
|
|
|
|
cd "${LB_DIR}"
|
|
run_step_sh "live-build clean" "80-lb-clean" "lb clean --all 2>&1 | tail -3"
|
|
run_step_sh "live-build config" "81-lb-config" "lb config 2>&1 | tail -5"
|
|
dump_memtest_debug "pre-build" "${LB_DIR}"
|
|
export MKSQUASHFS_OPTIONS="-no-xattrs"
|
|
run_step_sh "live-build build" "90-lb-build" "lb build 2>&1"
|
|
echo "=== enforcing canonical bootloader assets ==="
|
|
enforce_live_build_bootloader_assets "${LB_DIR}"
|
|
# Rename lb's default filesystem.squashfs to the versioned filename so the
|
|
# ISO contains a version-stamped squashfs (e.g. filesystem-v10.15.squashfs).
|
|
_std_sq="${LB_DIR}/binary/live/filesystem.squashfs"
|
|
_ver_sq="${LB_DIR}/binary/live/${SQUASHFS_FILENAME}"
|
|
if [ -f "${_std_sq}" ] && [ "${_std_sq}" != "${_ver_sq}" ]; then
|
|
mv "${_std_sq}" "${_ver_sq}"
|
|
echo "=== squashfs renamed: filesystem.squashfs → ${SQUASHFS_FILENAME} ==="
|
|
fi
|
|
reset_live_build_stage "${LB_DIR}" "binary_checksums"
|
|
reset_live_build_stage "${LB_DIR}" "binary_iso"
|
|
reset_live_build_stage "${LB_DIR}" "binary_zsync"
|
|
run_step_sh "rebuild live-build checksums after bootloader sync" "91b-lb-checksums" "lb binary_checksums 2>&1"
|
|
run_step_sh "rebuild ISO after bootloader sync" "91c-lb-binary-iso" "lb binary_iso 2>&1"
|
|
run_step_sh "rebuild zsync after bootloader sync" "91d-lb-zsync" "lb binary_zsync 2>&1"
|
|
|
|
# --- persist deb package cache back to shared location ---
|
|
# This allows the second variant to reuse all downloaded packages.
|
|
if [ -d "${BUILD_WORK_DIR}/cache/packages.chroot" ]; then
|
|
rsync -a "${BUILD_WORK_DIR}/cache/packages.chroot/" "${LB_PKG_CACHE}/"
|
|
echo "=== package cache synced to ${LB_PKG_CACHE} ==="
|
|
fi
|
|
|
|
# live-build outputs live-image-amd64.hybrid.iso in LB_DIR
|
|
ISO_RAW="${LB_DIR}/live-image-amd64.hybrid.iso"
|
|
if [ -f "$ISO_RAW" ]; then
|
|
dump_memtest_debug "post-build" "${LB_DIR}" "$ISO_RAW"
|
|
if iso_memtest_present "$ISO_RAW"; then
|
|
:
|
|
else
|
|
memtest_status=$?
|
|
if [ "$memtest_status" -eq 1 ]; then
|
|
recover_iso_memtest "${LB_DIR}" "$ISO_RAW"
|
|
dump_memtest_debug "post-recovery" "${LB_DIR}" "$ISO_RAW"
|
|
elif [ "$memtest_status" -eq 2 ]; then
|
|
memtest_fail "failed to inspect ISO for memtest before recovery" "$ISO_RAW"
|
|
fi
|
|
fi
|
|
validate_iso_memtest "$ISO_RAW"
|
|
validate_iso_live_boot_entries "$ISO_RAW"
|
|
validate_iso_grub_assets "$ISO_RAW"
|
|
validate_iso_nvidia_runtime "$ISO_RAW"
|
|
cp "$ISO_RAW" "$ISO_OUT"
|
|
touch "${FULL_BUILD_MARKER}"
|
|
echo ""
|
|
echo "=== done (${BUILD_VARIANT}) ==="
|
|
echo "ISO: $ISO_OUT"
|
|
if command -v stat >/dev/null 2>&1; then
|
|
ISO_SIZE_BYTES="$(stat -c '%s' "$ISO_OUT" 2>/dev/null || stat -f '%z' "$ISO_OUT")"
|
|
else
|
|
ISO_SIZE_BYTES="$(wc -c < "$ISO_OUT" | tr -d ' ')"
|
|
fi
|
|
if command -v numfmt >/dev/null 2>&1; then
|
|
echo "Size: $(numfmt --to=iec --suffix=B "$ISO_SIZE_BYTES")"
|
|
else
|
|
echo "Size: ${ISO_SIZE_BYTES} bytes"
|
|
fi
|
|
else
|
|
echo "ERROR: ISO not found at $ISO_RAW"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "Boot via BMC virtual media and SSH to the server IP on port 22 as root."
|