refactor: modularize audit and harden build validation
This commit is contained in:
Executable
+146
@@ -0,0 +1,146 @@
|
||||
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}"
|
||||
|
||||
template="${BUILDER_DIR}/config/bootloaders/grub-efi/grub.cfg"
|
||||
tmp_cfg="${cfg}.new"
|
||||
render_bootloader_template "$template" "$tmp_cfg" \
|
||||
"$version_label" "@KERNEL_LIVE@" "$kernel" "$append_live" "@INITRD_LIVE@" "$initrd"
|
||||
mv "$tmp_cfg" "$cfg"
|
||||
}
|
||||
|
||||
write_canonical_isolinux_cfg() {
|
||||
cfg="$1"
|
||||
kernel="$2"
|
||||
initrd="$3"
|
||||
append_live="$4"
|
||||
version_label="${PROJECT_VERSION_EFFECTIVE}"
|
||||
|
||||
template="${BUILDER_DIR}/config/bootloaders/isolinux/live.cfg.in"
|
||||
tmp_cfg="${cfg}.new"
|
||||
render_bootloader_template "$template" "$tmp_cfg" \
|
||||
"$version_label" "@LINUX@" "$kernel" "$append_live" "@INITRD@" "$initrd"
|
||||
mv "$tmp_cfg" "$cfg"
|
||||
}
|
||||
|
||||
# Render literal placeholders without sed replacement-string semantics. Values
|
||||
# containing '&', backslashes, or the sed delimiter must be copied unchanged.
|
||||
render_bootloader_template() {
|
||||
template="$1"
|
||||
output="$2"
|
||||
version="$3"
|
||||
kernel_placeholder="$4"
|
||||
kernel="$5"
|
||||
append_live="$6"
|
||||
initrd_placeholder="$7"
|
||||
initrd="$8"
|
||||
|
||||
BEE_RENDER_VERSION="$version" \
|
||||
BEE_RENDER_KERNEL_PLACEHOLDER="$kernel_placeholder" \
|
||||
BEE_RENDER_KERNEL="$kernel" \
|
||||
BEE_RENDER_APPEND="$append_live" \
|
||||
BEE_RENDER_INITRD_PLACEHOLDER="$initrd_placeholder" \
|
||||
BEE_RENDER_INITRD="$initrd" \
|
||||
awk '
|
||||
function replace_literal(text, needle, replacement, at) {
|
||||
while ((at = index(text, needle)) != 0) {
|
||||
text = substr(text, 1, at - 1) replacement substr(text, at + length(needle))
|
||||
}
|
||||
return text
|
||||
}
|
||||
{
|
||||
version = ENVIRON["BEE_RENDER_VERSION"]
|
||||
kernel_placeholder = ENVIRON["BEE_RENDER_KERNEL_PLACEHOLDER"]
|
||||
kernel = ENVIRON["BEE_RENDER_KERNEL"]
|
||||
append_live = ENVIRON["BEE_RENDER_APPEND"]
|
||||
initrd_placeholder = ENVIRON["BEE_RENDER_INITRD_PLACEHOLDER"]
|
||||
initrd = ENVIRON["BEE_RENDER_INITRD"]
|
||||
line = replace_literal($0, "@VERSION@", version)
|
||||
line = replace_literal(line, kernel_placeholder, kernel)
|
||||
line = replace_literal(line, "@APPEND_LIVE@", append_live)
|
||||
line = replace_literal(line, initrd_placeholder, initrd)
|
||||
print line
|
||||
}
|
||||
' "$template" > "$output"
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
Executable
+137
@@ -0,0 +1,137 @@
|
||||
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 -rf "${BUILD_LOG_TMPDIR}"
|
||||
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_TMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/bee-build-log.XXXXXX")"
|
||||
BUILD_LOG_PIPE="${BUILD_LOG_TMPDIR}/pipe"
|
||||
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 -rf "${STEP_LOG_TMPDIR}"
|
||||
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_TMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/bee-step-log.XXXXXX")"
|
||||
STEP_LOG_PIPE="${STEP_LOG_TMPDIR}/pipe"
|
||||
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
|
||||
}
|
||||
Executable
+206
@@ -0,0 +1,206 @@
|
||||
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
|
||||
}
|
||||
|
||||
# State written after every successful full lb build for this variant. Keep it
|
||||
# outside the rsync-managed live-build workdir so source synchronization cannot
|
||||
# delete the state that decides whether the fast path is safe.
|
||||
FULL_BUILD_STATE_DIR="${CACHE_ROOT}/full-build-state-${BUILD_VARIANT}"
|
||||
mkdir -p "${FULL_BUILD_STATE_DIR}"
|
||||
FULL_BUILD_MARKER="${FULL_BUILD_STATE_DIR}/complete"
|
||||
FULL_BUILD_HASH_FILE="${FULL_BUILD_STATE_DIR}/heavy-config.sha256"
|
||||
FULL_BUILD_ABI_FILE="${FULL_BUILD_STATE_DIR}/kernel-abi"
|
||||
FULL_BUILD_OVERLAY_MANIFEST="${FULL_BUILD_STATE_DIR}/overlay.manifest"
|
||||
|
||||
# Hashes the content of every "heavy" config input (VERSIONS, package lists,
|
||||
# hooks, archives, auto/config, Dockerfile). Bootloader templates are excluded:
|
||||
# the fast path regenerates the complete outer ISO layer from them. Deliberately content-
|
||||
# based rather than mtime-based: mtimes get reset by git checkouts, rsync, and
|
||||
# retried builds in ways that don't track "did this content actually change
|
||||
# since the last full build", which previously let needs_full_build() silently
|
||||
# take the fast path (reusing an old squashfs built against different package
|
||||
# pins) with no error.
|
||||
hash_heavy_config() {
|
||||
(
|
||||
cd "${BUILDER_DIR}"
|
||||
find \
|
||||
VERSIONS auto/config Dockerfile \
|
||||
config/package-lists config/hooks config/archives \
|
||||
-type f -print0 2>/dev/null |
|
||||
sort -z |
|
||||
xargs -0 -r sha256sum
|
||||
) | sha256sum | awk '{print $1}'
|
||||
}
|
||||
|
||||
write_overlay_manifest() {
|
||||
out_path="$1"
|
||||
(
|
||||
cd "${OVERLAY_STAGE_DIR}"
|
||||
find . -mindepth 1 -printf '%y %P\n' | sort
|
||||
) > "$out_path"
|
||||
}
|
||||
|
||||
overlay_paths_were_removed() {
|
||||
[ -f "${FULL_BUILD_OVERLAY_MANIFEST}" ] || return 0
|
||||
current_manifest="$(mktemp)"
|
||||
write_overlay_manifest "$current_manifest"
|
||||
if comm -23 "${FULL_BUILD_OVERLAY_MANIFEST}" "$current_manifest" | grep -q .; then
|
||||
rm -f "$current_manifest"
|
||||
return 0
|
||||
fi
|
||||
rm -f "$current_manifest"
|
||||
return 1
|
||||
}
|
||||
|
||||
# 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 "${FULL_BUILD_HASH_FILE}" ] || return 0
|
||||
[ -f "${FULL_BUILD_ABI_FILE}" ] || return 0
|
||||
[ -f "${FULL_BUILD_OVERLAY_MANIFEST}" ] || 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
|
||||
|
||||
_old_abi="$(cat "${FULL_BUILD_ABI_FILE}" 2>/dev/null)"
|
||||
if [ "${DEBIAN_KERNEL_ABI}" != "$_old_abi" ]; then
|
||||
echo "=== full build required: kernel ABI changed (${_old_abi:-unknown} -> ${DEBIAN_KERNEL_ABI}) ==="
|
||||
return 0
|
||||
fi
|
||||
|
||||
if overlay_paths_were_removed; then
|
||||
echo "=== full build required: overlay paths were removed or changed type ==="
|
||||
return 0
|
||||
fi
|
||||
|
||||
_new_hash="$(hash_heavy_config)"
|
||||
_old_hash="$(cat "${FULL_BUILD_HASH_FILE}" 2>/dev/null)"
|
||||
|
||||
if [ "$_new_hash" != "$_old_hash" ]; then
|
||||
echo "=== full build required: heavy config content changed since last full build ==="
|
||||
return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Fast path: unsquash existing filesystem, rsync overlay on top, repack.
|
||||
# CACHE_ROOT must have enough free space for the extracted root filesystem.
|
||||
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_parent="$(mktemp -d "${CACHE_ROOT}/fast-unsquash-${BUILD_VARIANT}.XXXXXX")"
|
||||
_tmp="${_tmp_parent}/root"
|
||||
trap 'rm -rf "$_tmp_parent"' EXIT
|
||||
echo "=== fast-path: unsquash $(basename "$_old_sq") ($(du -sh "$_old_sq" | cut -f1) compressed) ==="
|
||||
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_parent"
|
||||
for _candidate in "${BUILD_WORK_DIR}/binary/live/"filesystem*.squashfs; do
|
||||
[ -e "$_candidate" ] || continue
|
||||
[ "$_candidate" = "$_sq" ] || rm -f "$_candidate"
|
||||
done
|
||||
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.
|
||||
recover_iso_memtest() {
|
||||
lb_dir="$1"
|
||||
iso_path="$2"
|
||||
binary_boot="$lb_dir/binary/boot"
|
||||
|
||||
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
|
||||
|
||||
enforce_live_build_bootloader_assets "$lb_dir"
|
||||
|
||||
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
|
||||
}
|
||||
Executable
+636
@@ -0,0 +1,636 @@
|
||||
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_live_cmdline_params() {
|
||||
cfg="$1"
|
||||
command="$2"
|
||||
bootloader="$3"
|
||||
expected_label="$4"
|
||||
|
||||
awk -v command="$command" -v bootloader="$bootloader" -v expected_label="$expected_label" '
|
||||
function has(token, i) {
|
||||
for (i = 1; i <= NF; i++) {
|
||||
if ($i == token) {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function reject(message) {
|
||||
printf "ERROR: %s live entry at %s:%d: %s\n", bootloader, FILENAME, NR, message
|
||||
bad = 1
|
||||
}
|
||||
|
||||
$1 == command && has("boot=live") {
|
||||
live_entries++
|
||||
|
||||
if (!has("udev.children_max=1")) {
|
||||
reject("missing udev.children_max=1")
|
||||
}
|
||||
if (!has("intel_iommu=on")) {
|
||||
reject("missing intel_iommu=on")
|
||||
}
|
||||
if (!has("iommu.passthrough=0")) {
|
||||
reject("missing iommu.passthrough=0")
|
||||
}
|
||||
if (!has("efi=disable_early_pci_dma")) {
|
||||
reject("missing efi=disable_early_pci_dma")
|
||||
}
|
||||
if (!has("live-media-label=" expected_label)) {
|
||||
reject("missing expected live-media-label=" expected_label)
|
||||
}
|
||||
if (has("iommu=pt")) {
|
||||
reject("contains forbidden iommu=pt")
|
||||
}
|
||||
|
||||
if (has("pci=realloc")) {
|
||||
failsafe_entries++
|
||||
if (!has("iommu.strict=1")) {
|
||||
reject("pci=realloc entry is missing iommu.strict=1")
|
||||
}
|
||||
} else if (has("iommu.strict=1")) {
|
||||
reject("iommu.strict=1 is allowed only in the pci=realloc fail-safe entry")
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
if (live_entries == 0) {
|
||||
printf "ERROR: %s config has no live boot entries\n", bootloader
|
||||
bad = 1
|
||||
}
|
||||
if (failsafe_entries != 1) {
|
||||
printf "ERROR: %s config has %d pci=realloc fail-safe entries, expected 1\n", bootloader, failsafe_entries
|
||||
bad = 1
|
||||
}
|
||||
exit bad ? 1 : 0
|
||||
}
|
||||
' "$cfg"
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if grep -q 'iommu=pt' "$grub_cfg" "$isolinux_cfg"; then
|
||||
echo "ERROR: forbidden iommu=pt remains in ISO bootloader config" >&2
|
||||
rm -f "$grub_cfg" "$isolinux_cfg"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! validate_live_cmdline_params "$grub_cfg" linux GRUB "${BEE_ISO_VOLUME}"; then
|
||||
rm -f "$grub_cfg" "$isolinux_cfg"
|
||||
exit 1
|
||||
fi
|
||||
if ! validate_live_cmdline_params "$isolinux_cfg" append isolinux "${BEE_ISO_VOLUME}"; then
|
||||
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 -Fq "menuentry \"EASY-BEE v${PROJECT_VERSION_EFFECTIVE}\"" "$grub_cfg" || {
|
||||
echo "ERROR: GRUB version does not match ${PROJECT_VERSION_EFFECTIVE}" >&2
|
||||
rm -f "$grub_cfg" "$isolinux_cfg"
|
||||
exit 1
|
||||
}
|
||||
grep -Fq "menu label ^EASY-BEE v${PROJECT_VERSION_EFFECTIVE}" "$isolinux_cfg" || {
|
||||
echo "ERROR: isolinux version does not match ${PROJECT_VERSION_EFFECTIVE}" >&2
|
||||
rm -f "$grub_cfg" "$isolinux_cfg"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if command -v xorriso >/dev/null 2>&1; then
|
||||
iso_volume="$(xorriso -indev "$iso_path" -pvd_info 2>/dev/null | awk -F: '/Volume [Ii]d/ { sub(/^[[:space:]'\''"]+/, "", $2); sub(/[[:space:]'\''"]+$/, "", $2); print $2; exit }')"
|
||||
if [ "$iso_volume" != "${BEE_ISO_VOLUME}" ]; then
|
||||
echo "ERROR: ISO volume ID is ${iso_volume:-unknown}, expected ${BEE_ISO_VOLUME}" >&2
|
||||
rm -f "$grub_cfg" "$isolinux_cfg"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
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)"
|
||||
dpkg_status_dir="$(mktemp -d)"
|
||||
iso_list_files "$iso_path" > "$iso_files" || {
|
||||
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
||||
rm -rf "$dpkg_status_dir"
|
||||
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"
|
||||
rm -rf "$dpkg_status_dir"
|
||||
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"
|
||||
rm -rf "$dpkg_status_dir"
|
||||
nvidia_runtime_fail "failed to inspect $squashfs_member from ISO"
|
||||
}
|
||||
# var/lib/dpkg/status lives in whichever squashfs layer has the base
|
||||
# rootfs (not the usr/firmware split-off layers); harmless no-op on
|
||||
# the others.
|
||||
unsquashfs -d "${dpkg_status_dir}/extract" -f "$squashfs_tmp" var/lib/dpkg/status >/dev/null 2>&1 || true
|
||||
: > "$squashfs_tmp"
|
||||
done
|
||||
|
||||
grep -Eq 'usr/bin/dcgmi$' "$squashfs_list" || {
|
||||
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
||||
rm -rf "$dpkg_status_dir"
|
||||
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"
|
||||
rm -rf "$dpkg_status_dir"
|
||||
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"
|
||||
rm -rf "$dpkg_status_dir"
|
||||
nvidia_runtime_fail "dcgmproftester missing from final NVIDIA ISO"
|
||||
}
|
||||
|
||||
# Cross-check the DCGM package version actually baked into the squashfs
|
||||
# against VERSIONS. dcgmi/nv-hostengine/dcgmproftester presence alone
|
||||
# doesn't catch a stale squashfs served by a mis-detected fast-path build
|
||||
# (dcgmi stays present across DCGM versions); this does.
|
||||
dpkg_status_file="${dpkg_status_dir}/extract/var/lib/dpkg/status"
|
||||
if [ -f "$dpkg_status_file" ]; then
|
||||
_installed_dcgm_version="$(awk '
|
||||
/^Package: datacenter-gpu-manager-4-core$/ { in_pkg=1; next }
|
||||
/^Package: / { in_pkg=0 }
|
||||
in_pkg && /^Version: / { sub(/^Version: /, ""); print; exit }
|
||||
' "$dpkg_status_file")"
|
||||
if [ -z "$_installed_dcgm_version" ]; then
|
||||
echo "=== WARNING: datacenter-gpu-manager-4-core not found in ISO dpkg status; skipping DCGM version check ==="
|
||||
else
|
||||
_installed_dcgm_no_epoch="${_installed_dcgm_version#*:}"
|
||||
if [ "$_installed_dcgm_no_epoch" != "${DCGM_VERSION}" ]; then
|
||||
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
||||
rm -rf "$dpkg_status_dir"
|
||||
nvidia_runtime_fail "DCGM version mismatch: VERSIONS pins ${DCGM_VERSION} but ISO has ${_installed_dcgm_version} (stale squashfs; retry with --clean-build)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "=== WARNING: could not read dpkg status from ISO; skipping DCGM version check ==="
|
||||
fi
|
||||
|
||||
rm -f "$squashfs_tmp" "$squashfs_list" "$iso_files"
|
||||
rm -rf "$dpkg_status_dir"
|
||||
echo "=== NVIDIA runtime validation OK ==="
|
||||
}
|
||||
Reference in New Issue
Block a user