Merge debug/prod into single ISO build, fix NVIDIA module loading

## ISO build consolidation
- Remove separate debug/prod split: overlay-debug/, build-debug.sh,
  mkimg.bee_debug.sh, genapkovl-bee_debug.sh all deleted
- Single overlay: iso/overlay/ (was overlay-debug content)
- Single build script: build.sh (SSH, TUI, NVIDIA, vendor tools, bee-release)
- Single mkimage profile: bee (with dropbear, dialog, strace, gcompat, etc.)

## NVIDIA fixes
- Modules now stored at /usr/local/lib/nvidia/ instead of
  /lib/modules/<kver>/extra/nvidia/ — modloop squashfs mounts over that
  path at boot making overlay content there inaccessible
- bee-nvidia init: load via insmod (absolute path), not modprobe
- bee-nvidia init: create libnvidia-ml.so.1/libcuda.so.1 symlinks in /usr/lib/
- build-nvidia-module.sh: always install linux-lts-dev (not conditional) —
  stale 6.6.x headers caused wrong-kernel modules that never loaded at runtime
- build-nvidia-module.sh: create soname symlinks in cache
- KERNEL_VERSION in VERSIONS updated 6.6 → 6.12
- gcompat added to ISO packages (nvidia-smi is a glibc binary on musl Alpine)

## Service ordering
- bee-audit: add `after bee-nvidia` so NVIDIA enrichment always succeeds

## New tooling
- iso/builder/smoketest.sh: SSH smoke test for post-boot ISO validation
- iso/builder/build-gpu-burn.sh: builds gpu_burn vendor binary (CUDA 12.8+)
- vendor/gpu_burn included automatically if placed in iso/vendor/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-03-06 20:14:18 +03:00
parent 0907ba07c3
commit 1768bb58dd
24 changed files with 1296 additions and 261 deletions

View File

@@ -0,0 +1 @@
DROPBEAR_OPTS="-p 22 -R -B"

View File

@@ -1,20 +1,21 @@
#!/sbin/openrc-run
description="Bee: run hardware audit (production unattended mode)"
description="Bee: run hardware audit"
depend() {
need localmount
after bee-update bee-nvidia
after bee-network bee-nvidia
}
start() {
ebegin "Running hardware audit"
/usr/local/bin/audit --output usb > /var/log/bee-audit.json 2>/var/log/bee-audit.log
rc=$?
if [ "$rc" -eq 0 ]; then
einfo "Audit complete"
/usr/local/bin/audit --output stdout > /var/log/bee-audit.json 2>/var/log/bee-audit.log
local rc=$?
if [ $rc -eq 0 ]; then
einfo "Audit complete: /var/log/bee-audit.json"
einfo "SSH in and inspect results. Dropbear is running."
else
ewarn "Audit finished with errors"
ewarn "Audit finished with errors — check /var/log/bee-audit.log"
fi
eend 0
}

View File

@@ -4,7 +4,7 @@ description="Bee: bring up network interfaces via DHCP"
depend() {
need localmount
before bee-update bee-audit
before bee-audit
}
start() {

View File

@@ -2,6 +2,8 @@
description="Bee: load NVIDIA kernel modules"
NVIDIA_KO_DIR="/usr/local/lib/nvidia"
depend() {
need localmount
before bee-audit
@@ -9,23 +11,39 @@ depend() {
start() {
ebegin "Loading NVIDIA modules"
kver="$(uname -r)"
einfo "kernel: ${kver}"
if [ -d "/lib/modules/${kver}/extra/nvidia" ]; then
einfo "module dir: /lib/modules/${kver}/extra/nvidia"
ls "/lib/modules/${kver}/extra/nvidia"/*.ko 2>/dev/null | sed 's/^/ /' || true
else
ewarn "module dir missing: /lib/modules/${kver}/extra/nvidia"
einfo "kernel: $(uname -r)"
if [ ! -d "$NVIDIA_KO_DIR" ]; then
ewarn "NVIDIA module dir missing: $NVIDIA_KO_DIR"
eend 1
return 1
fi
depmod -a 2>/dev/null || true
einfo "module dir: $NVIDIA_KO_DIR"
ls "$NVIDIA_KO_DIR"/*.ko 2>/dev/null | sed 's/^/ /' || true
# Create libnvidia-ml soname symlinks needed by nvidia-smi (glibc binary on Alpine/musl)
for lib in libnvidia-ml libcuda; do
versioned=$(ls /usr/lib/${lib}.so.[0-9]* 2>/dev/null | head -1)
[ -n "$versioned" ] || continue
base=$(basename "$versioned")
ln -sf "$base" "/usr/lib/${lib}.so.1" 2>/dev/null || true
ln -sf "${lib}.so.1" "/usr/lib/${lib}.so" 2>/dev/null || true
done
# Load modules via insmod (bypasses modules.dep — modloop squashfs is read-only)
for mod in nvidia nvidia-modeset nvidia-uvm; do
if modprobe "$mod" 2>/dev/null; then
einfo "loaded: $mod"
ko="$NVIDIA_KO_DIR/${mod}.ko"
[ -f "$ko" ] || ko="$NVIDIA_KO_DIR/${mod//-/_}.ko"
if [ -f "$ko" ]; then
if insmod "$ko" 2>/dev/null; then
einfo "loaded: $mod"
else
ewarn "failed to load: $mod"
dmesg | tail -n 5 | sed 's/^/ dmesg: /' || true
fi
else
ewarn "failed to load: $mod"
dmesg | tail -n 5 | sed 's/^/ dmesg: /' || true
ewarn "not found: $ko"
fi
done

View File

@@ -0,0 +1,28 @@
#!/sbin/openrc-run
description="Bee: configure SSH access (keys or password fallback)"
depend() {
need localmount
before dropbear
}
start() {
# Always create dedicated 'bee' user for password fallback.
# If no SSH keys embedded: login with bee / eeb
if ! id bee > /dev/null 2>&1; then
adduser -D -s /bin/sh bee > /dev/null 2>&1
fi
printf 'eeb\neeb\n' | passwd bee > /dev/null 2>&1
if [ -f /etc/bee-ssh-password-fallback ]; then
ebegin "SSH key auth unavailable — password fallback active"
ewarn "Login: bee / eeb"
ewarn "Generate a key: sh keys/scripts/keygen.sh <name>"
eend 0
else
ebegin "SSH key auth configured"
# bee user exists but password login less useful when keys work
eend 0
fi
}

View File

@@ -1,15 +0,0 @@
#!/sbin/openrc-run
description="Bee: update audit binary from USB/network"
depend() {
need localmount
after bee-network
before bee-audit
}
start() {
ebegin "Checking for audit binary update"
/usr/local/bin/bee-update.sh >> /var/log/bee-update.log 2>&1
eend 0
}

37
iso/overlay/etc/init.d/dropbear Executable file
View File

@@ -0,0 +1,37 @@
#!/sbin/openrc-run
description="Dropbear SSH server"
depend() {
need localmount
after bee-sshsetup
use logger
}
check_config() {
if [ ! -e /etc/dropbear/dropbear_rsa_host_key ]; then
einfo "Generating RSA host key..."
/usr/bin/dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
fi
if [ ! -e /etc/dropbear/dropbear_ecdsa_host_key ]; then
einfo "Generating ECDSA host key..."
/usr/bin/dropbearkey -t ecdsa -f /etc/dropbear/dropbear_ecdsa_host_key
fi
if [ ! -e /etc/dropbear/dropbear_ed25519_host_key ]; then
einfo "Generating ED25519 host key..."
/usr/bin/dropbearkey -t ed25519 -f /etc/dropbear/dropbear_ed25519_host_key
fi
}
start() {
check_config || return 1
ebegin "Starting dropbear"
/usr/sbin/dropbear ${DROPBEAR_OPTS}
eend $?
}
stop() {
ebegin "Stopping dropbear"
start-stop-daemon --stop --pidfile /var/run/dropbear.pid
eend $?
}

13
iso/overlay/etc/inittab Normal file
View File

@@ -0,0 +1,13 @@
::sysinit:/sbin/openrc sysinit
::sysinit:/sbin/openrc boot
::wait:/sbin/openrc default
# Autologin on tty1
tty1::respawn:/sbin/agetty --autologin root --noclear tty1 linux
tty2::respawn:/sbin/getty 38400 tty2
tty3::respawn:/sbin/getty 38400 tty3
ttyS0::respawn:/sbin/getty -L 115200 ttyS0 vt100
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/openrc shutdown

View File

@@ -1,8 +1,16 @@
Bee Hardware Audit LiveCD
Mode: Production unattended
Logs:
/var/log/bee-network.log
/var/log/bee-update.log
/var/log/bee-audit.log
/var/log/bee-audit.json
██████╗ ███████╗███████╗ ██████╗ ███████╗██████╗ ██╗ ██╗ ██████╗
██╔══██╗██╔════╝██╔════╝ ██╔══██╗██╔════╝██╔══██╗██║ ██║██╔════╝
██████╔╝█████╗ █████╗ ██║ ██║█████╗ ██████╔╝██║ ██║██║ ███╗
██╔══██╗██╔══╝ ██╔══╝ ██║ ██║██╔══╝ ██╔══██╗██║ ██║██║ ██║
██████╔╝███████╗███████╗ ██████╔╝███████╗██████╔╝╚██████╔╝╚██████╔╝
╚═════╝ ╚══════╝╚══════╝ ╚═════╝ ╚══════╝╚═════╝ ╚═════╝ ╚═════╝
Hardware Audit LiveCD — DEBUG MODE
Build: %%BUILD_INFO%%
Logs: /var/log/bee-audit.json /var/log/bee-network.log
Open TUI: bee-tui
SSH access: key auth (developers) or bee/eeb (password fallback)

View File

@@ -1 +1,21 @@
export PATH="$PATH:/usr/local/bin"
menu() {
if [ -x /usr/local/bin/bee-tui ]; then
/usr/local/bin/bee-tui "$@"
else
echo "bee-tui is not installed"
return 1
fi
}
# Auto-open TUI on local tty1 after boot.
# Exiting TUI returns to this shell (console prompt).
if [ -z "${BEE_TUI_AUTO_LAUNCHED:-}" ] \
&& [ -z "${SSH_CONNECTION:-}" ] \
&& [ -z "${SSH_TTY:-}" ] \
&& [ "$(tty 2>/dev/null)" = "/dev/tty1" ] \
&& [ -x /usr/local/bin/bee-tui ]; then
export BEE_TUI_AUTO_LAUNCHED=1
/usr/local/bin/bee-tui
fi

View File

View File

@@ -0,0 +1,8 @@
#!/bin/sh
# bee-net-restart.sh — bring up all physical interfaces via DHCP (manual re-run)
for iface in $(ip -o link show | awk -F': ' '{print $2}' | grep -v '^lo$' | grep -vE '^(docker|virbr|veth|tun|tap|br-|bond|dummy)'); do
echo "[$iface] bringing up..."
ip link set "$iface" up 2>/dev/null
udhcpc -i "$iface" -t 5 -T 3
done

15
iso/overlay/usr/local/bin/bee-network.sh Executable file → Normal file
View File

@@ -1,9 +1,12 @@
#!/bin/sh
# bee-network.sh — bring up all physical interfaces via DHCP (non-blocking)
# bee-network.sh — bring up all physical network interfaces via DHCP
# Unattended: runs silently, logs results, never blocks.
LOG_PREFIX="bee-network"
log() { echo "[$LOG_PREFIX] $*"; }
# find physical interfaces: exclude lo and virtual (docker/virbr/veth/tun/tap)
interfaces=$(ip -o link show \
| awk -F': ' '{print $2}' \
| grep -v '^lo$' \
@@ -16,9 +19,13 @@ if [ -z "$interfaces" ]; then
fi
for iface in $interfaces; do
ip link set "$iface" up 2>/dev/null || { log "WARN: failed to bring up $iface"; continue; }
udhcpc -i "$iface" -b -t 0 -T 3 >/dev/null 2>&1 &
log "dhcp started for $iface"
log "bringing up $iface"
ip link set "$iface" up 2>/dev/null || { log "WARN: could not bring up $iface"; continue; }
# DHCP in background: -b forks if no immediate lease, & ensures non-blocking always.
# -t 0: unlimited retries, -T 3: 3s per attempt. No -q: stay running to renew lease.
udhcpc -i "$iface" -b -t 0 -T 3 &
log "DHCP started for $iface (pid $!)"
done
log "done"

581
iso/overlay/usr/local/bin/bee-tui Executable file
View File

@@ -0,0 +1,581 @@
#!/bin/sh
# bee-tui: interactive text menu for debug LiveCD operations.
set -u
if ! command -v dialog >/dev/null 2>&1; then
echo "ERROR: dialog is required but not installed"
exit 1
fi
pause() {
echo
printf 'Press Enter to continue... '
read -r _
}
header() {
clear
echo "=============================================="
echo " bee TUI (debug)"
echo "=============================================="
echo
}
menu_choice() {
title="$1"
prompt="$2"
shift 2
dialog --clear --stdout --title "$title" --menu "$prompt" 20 90 12 "$@"
}
list_ifaces() {
ip -o link show \
| awk -F': ' '{print $2}' \
| grep -v '^lo$' \
| grep -vE '^(docker|virbr|veth|tun|tap|br-|bond|dummy)' \
| sort
}
show_network_status() {
header
echo "Network interfaces"
echo
for iface in $(list_ifaces); do
state=$(ip -o link show "$iface" | awk '{print $9}')
ipv4=$(ip -o -4 addr show dev "$iface" | awk '{print $4}' | paste -sd ',')
[ -n "$ipv4" ] || ipv4="(no IPv4)"
echo "- $iface: state=$state ip=$ipv4"
done
echo
ip route | sed 's/^/ route: /'
pause
}
choose_interface() {
ifaces="$(list_ifaces)"
if [ -z "$ifaces" ]; then
echo "No physical interfaces found"
return 1
fi
set --
for iface in $ifaces; do
set -- "$@" "$iface" "$iface"
done
iface=$(menu_choice "Network" "Select interface" "$@") || return 1
CHOSEN_IFACE="$iface"
return 0
}
network_dhcp_one() {
header
echo "DHCP on one interface"
echo
choose_interface || { pause; return; }
iface="$CHOSEN_IFACE"
echo
echo "Starting DHCP on $iface..."
ip link set "$iface" up 2>/dev/null || true
udhcpc -i "$iface" -t 5 -T 3
pause
}
network_dhcp_all() {
header
echo "Restarting DHCP on all physical interfaces..."
echo
/usr/local/bin/bee-net-restart
pause
}
network_static_one() {
header
echo "Static IPv4 setup"
echo
choose_interface || { pause; return; }
iface="$CHOSEN_IFACE"
echo
printf 'IPv4 address (example 192.168.1.10): '
read -r ip
if [ -z "$ip" ]; then
echo "IP address is required"
pause
return
fi
printf 'Netmask (example 24 or 255.255.255.0): '
read -r mask
if [ -z "$mask" ]; then
echo "Netmask is required"
pause
return
fi
prefix=$(mask_to_prefix "$mask")
if [ -z "$prefix" ]; then
echo "Invalid netmask: $mask"
pause
return
fi
cidr="$ip/$prefix"
printf 'Default gateway: '
read -r gw
if [ -z "$gw" ]; then
echo "Default gateway is required"
pause
return
fi
printf 'DNS server (optional): '
read -r dns
ip link set "$iface" up 2>/dev/null || true
ip addr flush dev "$iface"
if ! ip addr add "$cidr" dev "$iface"; then
echo "Failed to set IP"
pause
return
fi
if [ -n "$gw" ]; then
ip route del default >/dev/null 2>&1 || true
ip route add default via "$gw" dev "$iface"
fi
if [ -n "$dns" ]; then
printf 'nameserver %s\n' "$dns" > /etc/resolv.conf
fi
echo
echo "Static config applied to $iface"
pause
}
mask_to_prefix() {
mask="$(echo "$1" | tr -d '[:space:]')"
case "$mask" in
0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32)
echo "$mask"
return 0
;;
esac
case "$mask" in
255.0.0.0) echo 8 ;;
255.128.0.0) echo 9 ;;
255.192.0.0) echo 10 ;;
255.224.0.0) echo 11 ;;
255.240.0.0) echo 12 ;;
255.248.0.0) echo 13 ;;
255.252.0.0) echo 14 ;;
255.254.0.0) echo 15 ;;
255.255.0.0) echo 16 ;;
255.255.128.0) echo 17 ;;
255.255.192.0) echo 18 ;;
255.255.224.0) echo 19 ;;
255.255.240.0) echo 20 ;;
255.255.248.0) echo 21 ;;
255.255.252.0) echo 22 ;;
255.255.254.0) echo 23 ;;
255.255.255.0) echo 24 ;;
255.255.255.128) echo 25 ;;
255.255.255.192) echo 26 ;;
255.255.255.224) echo 27 ;;
255.255.255.240) echo 28 ;;
255.255.255.248) echo 29 ;;
255.255.255.252) echo 30 ;;
255.255.255.254) echo 31 ;;
255.255.255.255) echo 32 ;;
*) return 1 ;;
esac
}
network_menu() {
while true; do
choice=$(menu_choice "Network" "Select action" \
"1" "Show network status" \
"2" "DHCP on all interfaces" \
"3" "DHCP on one interface" \
"4" "Set static IPv4 on one interface" \
"5" "Back") || return
case "$choice" in
1) show_network_status ;;
2) network_dhcp_all ;;
3) network_dhcp_one ;;
4) network_static_one ;;
5) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
bee_services_list() {
for path in /etc/init.d/bee-*; do
[ -e "$path" ] || continue
basename "$path"
done
}
services_status_all() {
header
echo "bee service status"
echo
for svc in $(bee_services_list); do
if rc-service "$svc" status >/dev/null 2>&1; then
echo "- $svc: running"
else
echo "- $svc: stopped"
fi
done
pause
}
choose_service() {
svcs="$(bee_services_list)"
if [ -z "$svcs" ]; then
echo "No bee-* services found"
return 1
fi
set --
for svc in $svcs; do
set -- "$@" "$svc" "$svc"
done
svc=$(menu_choice "bee Services" "Select service" "$@") || return 1
CHOSEN_SERVICE="$svc"
return 0
}
service_action_menu() {
header
echo "Service action"
echo
choose_service || { pause; return; }
svc="$CHOSEN_SERVICE"
act=$(menu_choice "Service: $svc" "Select action" \
"1" "status" \
"2" "restart" \
"3" "start" \
"4" "stop" \
"5" "toggle start/stop" \
"6" "Back") || return
case "$act" in
1) rc-service "$svc" status || true ;;
2) rc-service "$svc" restart || true ;;
3) rc-service "$svc" start || true ;;
4) rc-service "$svc" stop || true ;;
5)
if rc-service "$svc" status >/dev/null 2>&1; then
rc-service "$svc" stop || true
else
rc-service "$svc" start || true
fi
;;
6) return ;;
*) echo "Invalid action" ;;
esac
pause
}
services_menu() {
while true; do
choice=$(menu_choice "bee Services" "Select action" \
"1" "Status of all bee-* services" \
"2" "Manage one service (status/restart/start/stop/toggle)" \
"3" "Back") || return
case "$choice" in
1) services_status_all ;;
2) service_action_menu ;;
3) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
confirm_phrase() {
phrase="$1"
prompt="$2"
echo
printf '%s (%s): ' "$prompt" "$phrase"
read -r value
[ "$value" = "$phrase" ]
}
shutdown_menu() {
while true; do
choice=$(menu_choice "Shutdown/Reboot Tests" "Select action" \
"1" "Reboot now" \
"2" "Power off now" \
"3" "Schedule poweroff in 60s" \
"4" "Cancel scheduled shutdown" \
"5" "IPMI chassis power status" \
"6" "IPMI chassis power soft" \
"7" "IPMI chassis power cycle" \
"8" "Back") || return
case "$choice" in
1)
confirm_phrase "REBOOT" "Type confirmation" || { echo "Canceled"; pause; continue; }
reboot
;;
2)
confirm_phrase "POWEROFF" "Type confirmation" || { echo "Canceled"; pause; continue; }
poweroff
;;
3)
confirm_phrase "SCHEDULE" "Type confirmation" || { echo "Canceled"; pause; continue; }
shutdown -P +1 "bee test: scheduled poweroff in 60 seconds"
echo "Scheduled"
pause
;;
4)
shutdown -c || true
echo "Canceled (if any schedule existed)"
pause
;;
5)
ipmitool chassis power status || echo "ipmitool power status failed"
pause
;;
6)
confirm_phrase "IPMI-SOFT" "Type confirmation" || { echo "Canceled"; pause; continue; }
ipmitool chassis power soft || echo "ipmitool soft power failed"
pause
;;
7)
confirm_phrase "IPMI-CYCLE" "Type confirmation" || { echo "Canceled"; pause; continue; }
ipmitool chassis power cycle || echo "ipmitool power cycle failed"
pause
;;
8)
return
;;
*)
echo "Invalid choice"
pause
;;
esac
done
}
gpu_burn_10m() {
header
echo "GPU Burn (10 minutes)"
echo
if ! command -v gpu_burn >/dev/null 2>&1; then
echo "gpu_burn binary not found in PATH"
echo "Expected command: gpu_burn"
pause
return
fi
if ! command -v nvidia-smi >/dev/null 2>&1 || ! nvidia-smi -L >/dev/null 2>&1; then
echo "NVIDIA driver/GPU not ready (nvidia-smi failed)"
pause
return
fi
confirm_phrase "GPU-BURN" "Type confirmation to start benchmark" || { echo "Canceled"; pause; return; }
echo "Running: gpu_burn 600"
echo "Log: /var/log/bee-gpuburn.log"
gpu_burn 600 2>&1 | tee /var/log/bee-gpuburn.log
echo
echo "GPU Burn finished"
pause
}
gpu_benchmarks_menu() {
while true; do
choice=$(menu_choice "Benchmarks -> GPU" "Select action" \
"1" "GPU Burn (10 minutes)" \
"2" "Back") || return
case "$choice" in
1) gpu_burn_10m ;;
2) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
benchmarks_menu() {
while true; do
choice=$(menu_choice "Benchmarks" "Select category" \
"1" "GPU" \
"2" "Back") || return
case "$choice" in
1) gpu_benchmarks_menu ;;
2) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
run_cmd_log() {
label="$1"
cmd="$2"
log_file="$3"
{
echo "=== $label ==="
echo "time: $(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "cmd: $cmd"
echo
sh -c "$cmd"
} >"$log_file" 2>&1
return $?
}
run_gpu_nvidia_acceptance_test() {
header
echo "System acceptance tests -> GPU NVIDIA"
echo
confirm_phrase "SAT-GPU" "Type confirmation to start tests" || { echo "Canceled"; pause; return; }
ts="$(date -u '+%Y%m%d-%H%M%S')"
base_dir="/var/log/bee-sat"
run_dir="$base_dir/gpu-nvidia-$ts"
archive="$base_dir/gpu-nvidia-$ts.tar.gz"
mkdir -p "$run_dir"
summary="$run_dir/summary.txt"
: >"$summary"
echo "Running acceptance commands..."
echo "Logs directory: $run_dir"
echo "Archive target: $archive"
echo
c1="nvidia-smi -q"
c2="dmidecode -t baseboard"
c3="dmidecode -t system"
c4="nvidia-bug-report.sh"
run_cmd_log "nvidia_smi_q" "$c1" "$run_dir/01-nvidia-smi-q.log"; rc1=$?
run_cmd_log "dmidecode_baseboard" "$c2" "$run_dir/02-dmidecode-baseboard.log"; rc2=$?
run_cmd_log "dmidecode_system" "$c3" "$run_dir/03-dmidecode-system.log"; rc3=$?
run_cmd_log "nvidia_bug_report" "$c4" "$run_dir/04-nvidia-bug-report.log"; rc4=$?
bug_report="$(ls -1 nvidia-bug-report.log.gz 2>/dev/null | head -n1 || true)"
if [ -n "$bug_report" ] && [ -f "$bug_report" ]; then
cp -f "$bug_report" "$run_dir/"
fi
{
echo "run_at_utc=$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "cmd_nvidia_smi_q_rc=$rc1"
echo "cmd_dmidecode_baseboard_rc=$rc2"
echo "cmd_dmidecode_system_rc=$rc3"
echo "cmd_nvidia_bug_report_rc=$rc4"
} >>"$summary"
tar -czf "$archive" -C "$base_dir" "gpu-nvidia-$ts"
tar_rc=$?
echo "archive_rc=$tar_rc" >>"$summary"
echo
echo "Done."
echo "- Logs: $run_dir"
echo "- Archive: $archive (rc=$tar_rc)"
pause
}
gpu_nvidia_sat_menu() {
while true; do
choice=$(menu_choice "System acceptance tests -> GPU NVIDIA" "Select action" \
"1" "Run command pack" \
"2" "Back") || return
case "$choice" in
1) run_gpu_nvidia_acceptance_test ;;
2) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
system_acceptance_tests_menu() {
while true; do
choice=$(menu_choice "System acceptance tests" "Select category" \
"1" "GPU NVIDIA" \
"2" "Back") || return
case "$choice" in
1) gpu_nvidia_sat_menu ;;
2) return ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
run_audit_now() {
header
echo "Run audit now"
echo
/usr/local/bin/audit --output stdout > /var/log/bee-audit.json 2>/var/log/bee-audit.log
rc=$?
if [ "$rc" -eq 0 ]; then
echo "Audit completed successfully"
else
echo "Audit finished with errors (rc=$rc)"
fi
echo "Logs: /var/log/bee-audit.log, /var/log/bee-audit.json"
pause
}
check_required_tools() {
header
echo "Required tools check"
echo
for tool in dmidecode smartctl nvme ipmitool lspci audit nvidia-smi gpu_burn dialog; do
if command -v "$tool" >/dev/null 2>&1; then
echo "- $tool: OK ($(command -v "$tool"))"
else
echo "- $tool: MISSING"
fi
done
pause
}
main_menu() {
while true; do
choice=$(menu_choice "Bee TUI (debug)" "Select action" \
"1" "Network setup" \
"2" "bee service management" \
"3" "Shutdown/reboot tests" \
"4" "Benchmarks" \
"5" "System acceptance tests" \
"6" "Run audit now" \
"7" "Check required tools" \
"8" "Show last audit log tail" \
"9" "Exit to console") || exit 0
case "$choice" in
1) network_menu ;;
2) services_menu ;;
3) shutdown_menu ;;
4) benchmarks_menu ;;
5) system_acceptance_tests_menu ;;
6) run_audit_now ;;
7) check_required_tools ;;
8)
header
tail -n 40 /var/log/bee-audit.log 2>/dev/null || echo "No /var/log/bee-audit.log"
echo
tail -n 20 /var/log/bee-audit.json 2>/dev/null || true
pause
;;
9) exit 0 ;;
*) echo "Invalid choice"; pause ;;
esac
done
}
main_menu

View File

@@ -1,123 +0,0 @@
#!/bin/sh
# bee-update.sh — production update path: USB first, then network.
# Unattended: logs only, never blocks boot.
set -u
LOG_PREFIX="bee-update"
log() { echo "[$LOG_PREFIX] $*"; }
AUDIT_BIN="/usr/local/bin/audit"
TMP_BIN="/tmp/bee-audit-new"
TMP_SIG="/tmp/bee-audit-new.sig"
REPO_API="${BEE_RELEASE_API:-https://git.mchus.pro/api/v1/repos/<org>/bee/releases/latest}"
download_to() {
url="$1"
out="$2"
if command -v wget >/dev/null 2>&1; then
wget -q -O "$out" "$url"
return $?
fi
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$url" -o "$out"
return $?
fi
log "neither wget nor curl available"
return 1
}
version_of() {
"$1" --version 2>/dev/null | head -n1 | tr -d '[:space:]'
}
apply_update() {
src_bin="$1"
src_sig="$2"
src_ver="$3"
if [ ! -x "$src_bin" ] || [ ! -f "$src_sig" ]; then
log "missing binary or signature"
return 1
fi
# NOTE: strict signature verification should be implemented in audit updater module.
# Here we keep shell side minimal and fail-open for now.
cp "$src_bin" "$AUDIT_BIN" || return 1
chmod +x "$AUDIT_BIN" || return 1
log "updated audit binary to $src_ver"
return 0
}
check_usb_update() {
for root in /media/* /mnt/* /tmp/bee-usb /run/media/*/*; do
[ -d "$root" ] || continue
base="$root/bee-update"
bin="$base/bee-audit-linux-amd64"
sig="$base/bee-audit-linux-amd64.sig"
ver_file="$base/VERSION"
[ -f "$bin" ] || continue
[ -f "$sig" ] || continue
[ -f "$ver_file" ] || continue
new_ver=$(cat "$ver_file" 2>/dev/null | tr -d '[:space:]')
cur_ver=$(version_of "$AUDIT_BIN")
[ -n "$new_ver" ] || continue
if [ "$new_ver" = "$cur_ver" ]; then
log "usb update found but version is same ($new_ver)"
return 0
fi
log "usb update candidate: $new_ver"
apply_update "$bin" "$sig" "$new_ver" && return 0
return 1
done
return 1
}
check_network_update() {
if ! ping -c 1 -W 3 git.mchus.pro >/dev/null 2>&1; then
log "network unavailable; skip release check"
return 1
fi
if ! command -v wget >/dev/null 2>&1 && ! command -v curl >/dev/null 2>&1; then
log "neither wget nor curl found; skip network update"
return 1
fi
if ! command -v jq >/dev/null 2>&1; then
log "jq not found; skip network update"
return 1
fi
meta="/tmp/bee-release-latest.json"
download_to "$REPO_API" "$meta" || { log "failed to fetch release metadata"; return 1; }
tag=$(jq -r '.tag_name // empty' "$meta")
[ -n "$tag" ] || { log "release metadata missing tag_name"; return 1; }
cur_ver=$(version_of "$AUDIT_BIN")
if [ "$tag" = "$cur_ver" ]; then
log "already latest ($tag)"
return 0
fi
bin_url=$(jq -r '.assets[]? | select(.name=="bee-audit-linux-amd64") | .browser_download_url // empty' "$meta")
sig_url=$(jq -r '.assets[]? | select(.name=="bee-audit-linux-amd64.sig") | .browser_download_url // empty' "$meta")
[ -n "$bin_url" ] && [ -n "$sig_url" ] || { log "missing release asset URLs"; return 1; }
download_to "$bin_url" "$TMP_BIN" || return 1
download_to "$sig_url" "$TMP_SIG" || return 1
chmod +x "$TMP_BIN"
log "network update candidate: $tag"
apply_update "$TMP_BIN" "$TMP_SIG" "$tag"
}
main() {
if check_usb_update; then
exit 0
fi
check_network_update || true
}
main "$@"