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:
@@ -1,64 +1,109 @@
|
|||||||
# Runtime Flows — bee
|
# Runtime Flows — bee
|
||||||
|
|
||||||
## Boot sequence (debug ISO)
|
## Boot sequence (single ISO)
|
||||||
|
|
||||||
OpenRC default runlevel, service start order:
|
OpenRC default runlevel, service start order:
|
||||||
|
|
||||||
```
|
```
|
||||||
localmount
|
localmount
|
||||||
└── bee-sshsetup (creates bee user, sets password fallback)
|
├── bee-sshsetup (creates bee user, sets password; runs before dropbear)
|
||||||
└── dropbear (SSH on port 22 — starts regardless of network)
|
│ └── dropbear (SSH on port 22 — starts without network)
|
||||||
└── bee-network (udhcpc -b on all physical interfaces, non-blocking)
|
├── bee-network (udhcpc -b on all physical interfaces, non-blocking)
|
||||||
└── bee-nvidia (depmod -a, modprobe nvidia nvidia-modeset nvidia-uvm)
|
│ └── bee-nvidia (insmod nvidia*.ko from /usr/local/lib/nvidia/,
|
||||||
└── bee-audit-debug (runs audit binary, logs to /var/log/bee-audit.json)
|
│ creates libnvidia-ml.so.1 symlinks in /usr/lib/)
|
||||||
|
│ └── bee-audit (runs audit binary → /var/log/bee-audit.json)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Critical invariants:**
|
**Critical invariants:**
|
||||||
- Dropbear MUST start without network. Custom init in overlay has `need localmount` only — NOT `need net`.
|
- Dropbear MUST start without network. `bee-sshsetup` has `need localmount` only.
|
||||||
- bee-network uses `udhcpc -b` (background daemon) so it retries indefinitely when cable connected later.
|
- `bee-network` uses `udhcpc -b` (background) — retries indefinitely if no cable.
|
||||||
- bee-audit-debug uses `eend 0` always — never fails boot even if audit errors.
|
- `bee-nvidia` loads modules via `insmod` with absolute paths — NOT `modprobe`.
|
||||||
|
Reason: modloop squashfs mounts over `/lib/modules/<kver>/` at boot, making it
|
||||||
|
read-only. The overlay's modules at that path are inaccessible. Modules are stored
|
||||||
|
at `/usr/local/lib/nvidia/` (overlay path, always writable).
|
||||||
|
- `bee-nvidia` creates `libnvidia-ml.so.1` symlinks in `/usr/lib/` — required because
|
||||||
|
`nvidia-smi` is a glibc binary that looks for the soname symlink, not the versioned file.
|
||||||
|
- `gcompat` package provides `/lib64/ld-linux-x86-64.so.2` for glibc compat on Alpine musl.
|
||||||
|
- `bee-audit` uses `after bee-nvidia` — ensures NVIDIA enrichment succeeds.
|
||||||
|
- `bee-audit` uses `eend 0` always — never fails boot even if audit errors.
|
||||||
|
|
||||||
## ISO build sequence
|
## ISO build sequence
|
||||||
|
|
||||||
```
|
```
|
||||||
build-debug.sh
|
build.sh [--authorized-keys /path/to/keys]
|
||||||
1. compile audit binary (skip if .go files older than binary)
|
1. compile audit binary (skip if .go files older than binary)
|
||||||
2. build-nvidia-module.sh:
|
2. inject authorized_keys into overlay/root/.ssh/ (or set password fallback)
|
||||||
a. download NVIDIA .run installer (sha256 verified, cached)
|
3. copy audit binary → overlay/usr/local/bin/audit
|
||||||
b. extract installer
|
4. copy vendor binaries from iso/vendor/ → overlay/usr/local/bin/
|
||||||
c. build kernel modules against linux-lts-dev headers
|
(storcli64, sas2ircu, sas3ircu, mstflint, gpu_burn — each optional)
|
||||||
d. extract nvidia-smi + libnvidia-ml from installer
|
5. build-nvidia-module.sh:
|
||||||
e. cache in dist/nvidia-<version>-<kver>/
|
a. apk add linux-lts-dev (always, to get current Alpine 3.21 kernel headers)
|
||||||
3. inject authorized_keys into overlay
|
b. detect KVER from /usr/src/linux-headers-*
|
||||||
4. inject audit binary → overlay/usr/local/bin/audit
|
c. download NVIDIA .run installer (sha256 verified, cached in dist/)
|
||||||
5. inject NVIDIA .ko → overlay/lib/modules/<kver>/extra/nvidia/
|
d. extract installer
|
||||||
6. inject nvidia-smi → overlay/usr/local/bin/nvidia-smi
|
e. build kernel modules against linux-lts headers
|
||||||
7. copy mkimg profile + genapkovl to ~/.mkimage/ AND /var/tmp/
|
f. create libnvidia-ml.so.1 / libcuda.so.1 symlinks in cache
|
||||||
8. mkimage.sh (from /var/tmp, TMPDIR=/var/tmp):
|
g. cache in dist/nvidia-<version>-<kver>/
|
||||||
kernel_* section — cached (linux-lts modloop, lz4 compressed)
|
6. inject NVIDIA .ko → overlay/usr/local/lib/nvidia/
|
||||||
apks_* section — cached (downloaded packages)
|
7. inject nvidia-smi → overlay/usr/local/bin/nvidia-smi
|
||||||
syslinux_* / grub_* — cached
|
8. inject libnvidia-ml + libcuda → overlay/usr/lib/
|
||||||
apkovl — always regenerated (genapkovl-bee_debug.sh)
|
9. write overlay/etc/bee-release (versions + git commit)
|
||||||
final ISO — always assembled
|
10. export BEE_BUILD_INFO for motd substitution
|
||||||
|
11. mkimage.sh (from /var/tmp, TMPDIR=/var/tmp):
|
||||||
|
kernel_* section — cached (linux-lts modloop)
|
||||||
|
apks_* section — cached (downloaded packages)
|
||||||
|
syslinux_* / grub_* — cached
|
||||||
|
apkovl — always regenerated (genapkovl-bee.sh)
|
||||||
|
final ISO — always assembled
|
||||||
```
|
```
|
||||||
|
|
||||||
**Critical invariants:**
|
**Critical invariants:**
|
||||||
- `genapkovl-bee_debug.sh` must be in `/var/tmp/` (CWD when mkimage runs), not only `~/.mkimage/`.
|
- `linux-lts-dev` is always installed (not conditional) — stale 6.6.x headers on the
|
||||||
- `TMPDIR=/var/tmp` required — tmpfs /tmp is only ~1GB, too small for kernel firmware.
|
builder would cause modules to be built for the wrong kernel and never load at runtime.
|
||||||
- Workdir cleanup preserves `apks_*`, `kernel_*`, `syslinux_*`, `grub_*` — only clears apkovl and final image.
|
- NVIDIA modules go to `overlay/usr/local/lib/nvidia/` — NOT `lib/modules/<kver>/extra/`.
|
||||||
- `run-builder.sh` runs build in `screen` session to survive SSH disconnects during long NVIDIA downloads.
|
- `genapkovl-bee.sh` must be copied to `/var/tmp/` (CWD when mkimage runs).
|
||||||
|
- `TMPDIR=/var/tmp` required — tmpfs `/tmp` is only ~1GB, too small for kernel firmware.
|
||||||
|
- Workdir cleanup preserves `apks_*`, `kernel_*`, `syslinux_*`, `grub_*` cache dirs.
|
||||||
|
|
||||||
|
## gpu_burn vendor binary
|
||||||
|
|
||||||
|
`gpu_burn` requires CUDA nvcc to build. It is NOT built as part of the main ISO build.
|
||||||
|
Build separately on the builder VM and place in `iso/vendor/gpu_burn`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sh iso/builder/build-gpu-burn.sh dist/
|
||||||
|
cp dist/gpu_burn iso/vendor/gpu_burn
|
||||||
|
cp dist/compare.ptx iso/vendor/compare.ptx
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires: CUDA 12.8+ (supports GCC 14, Alpine 3.21), libxml2, g++, make, git.
|
||||||
|
The `build.sh` will include it automatically if `iso/vendor/gpu_burn` exists.
|
||||||
|
|
||||||
|
## Post-boot smoke test
|
||||||
|
|
||||||
|
After booting a live ISO, run to verify all critical components:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh root@<ip> 'sh -s' < iso/builder/smoketest.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Exit code 0 = all required checks pass. All `FAIL` lines must be zero before shipping.
|
||||||
|
|
||||||
|
Key checks: NVIDIA modules loaded, nvidia-smi sees all GPUs, lib symlinks present,
|
||||||
|
gcompat installed, services running, audit completed with NVIDIA enrichment, internet.
|
||||||
|
|
||||||
## apkovl mechanism
|
## apkovl mechanism
|
||||||
|
|
||||||
The apkovl is a `.tar.gz` injected into the ISO at `/boot/`. Alpine's initramfs extracts it at boot, overlaying `/etc`, `/usr`, `/root` on the tmpfs root.
|
The apkovl is a `.tar.gz` injected into the ISO at `/boot/`. Alpine initramfs extracts
|
||||||
|
it at boot, overlaying `/etc`, `/usr`, `/root`, `/lib` on the tmpfs root.
|
||||||
|
|
||||||
`genapkovl-bee_debug.sh` generates the tarball containing:
|
`genapkovl-bee.sh` generates the tarball containing:
|
||||||
- `/etc/apk/world` — package list (apk installs these on first boot)
|
- `/etc/apk/world` — package list (apk installs on first boot)
|
||||||
- `/etc/runlevels/*/` — OpenRC service symlinks
|
- `/etc/runlevels/*/` — OpenRC service symlinks
|
||||||
- `/etc/conf.d/dropbear` — DROPBEAR_OPTS="-R -B"
|
- `/etc/conf.d/dropbear` — `DROPBEAR_OPTS="-R -B"`
|
||||||
- `/etc/network/interfaces` — lo only (bee-network handles DHCP)
|
- `/etc/network/interfaces` — lo only (bee-network handles DHCP)
|
||||||
- `/etc/hostname`
|
- `/etc/hostname`
|
||||||
- Everything from `iso/overlay-debug/` (init scripts, binaries, ssh keys)
|
- Everything from `iso/overlay/` (init scripts, binaries, ssh keys, tui)
|
||||||
|
|
||||||
## Collector flow
|
## Collector flow
|
||||||
|
|
||||||
@@ -70,8 +115,8 @@ audit binary start
|
|||||||
4. storage collector (lsblk -J, smartctl -j, nvme id-ctrl, nvme smart-log)
|
4. storage collector (lsblk -J, smartctl -j, nvme id-ctrl, nvme smart-log)
|
||||||
5. pcie collector (lspci -vmm -D, /sys/bus/pci/devices/)
|
5. pcie collector (lspci -vmm -D, /sys/bus/pci/devices/)
|
||||||
6. psu collector (ipmitool fru — silent if no /dev/ipmi0)
|
6. psu collector (ipmitool fru — silent if no /dev/ipmi0)
|
||||||
7. nvidia enrichment (nvidia-smi — skipped if driver not loaded)
|
7. nvidia enrichment (nvidia-smi — skipped if binary absent or driver not loaded)
|
||||||
8. output JSON to stdout / file / usb
|
8. output JSON → /var/log/bee-audit.json
|
||||||
9. QR summary to stdout (qrencode if available)
|
9. QR summary to stdout (qrencode if available)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,16 @@ Fills gaps where Redfish/logpile is blind:
|
|||||||
## In scope
|
## In scope
|
||||||
|
|
||||||
- Read-only hardware inventory: board, CPU, memory, storage, PCIe, PSU, GPU, NIC, RAID
|
- Read-only hardware inventory: board, CPU, memory, storage, PCIe, PSU, GPU, NIC, RAID
|
||||||
- Unattended operation — no user interaction at any stage
|
- Unattended operation — no user interaction required
|
||||||
- NVIDIA proprietary driver loaded at boot for GPU enrichment
|
- NVIDIA proprietary driver loaded at boot for GPU enrichment via `nvidia-smi`
|
||||||
- SSH access in debug ISO for development and testing
|
- SSH access (dropbear) always available for inspection and debugging
|
||||||
- Auto-update of audit binary from Gitea releases (production ISO)
|
- Interactive TUI (`bee-tui`) for network setup, service management, GPU tests
|
||||||
|
- GPU stress testing via `gpu_burn` (vendor binary, optional)
|
||||||
|
|
||||||
## Out of scope
|
## Out of scope
|
||||||
|
|
||||||
- Any writes to the server being audited
|
- Any writes to the server being audited
|
||||||
- Network configuration changes
|
- Network configuration changes (persistent)
|
||||||
- BMC/IPMI configuration
|
- BMC/IPMI configuration
|
||||||
- Anything requiring persistent storage on the audited machine
|
- Anything requiring persistent storage on the audited machine
|
||||||
- Windows support
|
- Windows support
|
||||||
@@ -38,11 +39,13 @@ Fills gaps where Redfish/logpile is blind:
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Audit binary | Go, static, `CGO_ENABLED=0` |
|
| Audit binary | Go, static, `CGO_ENABLED=0` |
|
||||||
| LiveCD | Alpine Linux 3.21, linux-lts 6.12.x |
|
| LiveCD | Alpine Linux 3.21, linux-lts 6.12.x |
|
||||||
| ISO build | Alpine mkimage + apkovl overlay |
|
| ISO build | Alpine mkimage + apkovl overlay (`iso/overlay/`) |
|
||||||
| Init system | OpenRC |
|
| Init system | OpenRC |
|
||||||
| SSH (debug) | Dropbear |
|
| SSH | Dropbear (always included) |
|
||||||
| NVIDIA driver | Proprietary `.run` installer, built against linux-lts headers |
|
| NVIDIA driver | Proprietary `.run` installer, built against linux-lts headers |
|
||||||
| Builder VM | Alpine 3.21, 172.27.0.4 |
|
| NVIDIA modules | Loaded via `insmod` from `/usr/local/lib/nvidia/` (not modloop path) |
|
||||||
|
| glibc compat | `gcompat` — required for `nvidia-smi` (glibc binary on musl Alpine) |
|
||||||
|
| Builder VM | Alpine 3.21 |
|
||||||
|
|
||||||
## Key paths
|
## Key paths
|
||||||
|
|
||||||
@@ -52,7 +55,9 @@ Fills gaps where Redfish/logpile is blind:
|
|||||||
| `audit/internal/collector/` | Per-subsystem collectors |
|
| `audit/internal/collector/` | Per-subsystem collectors |
|
||||||
| `audit/internal/schema/` | HardwareIngestRequest types |
|
| `audit/internal/schema/` | HardwareIngestRequest types |
|
||||||
| `iso/builder/` | ISO build scripts and mkimage profile |
|
| `iso/builder/` | ISO build scripts and mkimage profile |
|
||||||
| `iso/overlay-debug/` | Files injected into debug ISO via apkovl |
|
| `iso/overlay/` | Single overlay: files injected into ISO via apkovl |
|
||||||
| `iso/builder/VERSIONS` | Pinned versions: Alpine, Go, NVIDIA driver |
|
| `iso/vendor/` | Optional pre-built vendor binaries (storcli64, gpu_burn, …) |
|
||||||
|
| `iso/builder/VERSIONS` | Pinned versions: Alpine, Go, NVIDIA driver, kernel |
|
||||||
|
| `iso/builder/smoketest.sh` | Post-boot smoke test — run via SSH to verify live ISO |
|
||||||
| `dist/` | Build outputs (gitignored) |
|
| `dist/` | Build outputs (gitignored) |
|
||||||
| `iso/out/` | Downloaded ISO files (gitignored) |
|
| `iso/out/` | Downloaded ISO files (gitignored) |
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
ALPINE_VERSION=3.21
|
ALPINE_VERSION=3.21
|
||||||
KERNEL_VERSION=6.6
|
KERNEL_VERSION=6.12
|
||||||
NVIDIA_DRIVER_VERSION=590.48.01
|
NVIDIA_DRIVER_VERSION=590.48.01
|
||||||
GO_VERSION=1.23.6
|
GO_VERSION=1.23.6
|
||||||
|
|||||||
82
iso/builder/build-gpu-burn.sh
Normal file
82
iso/builder/build-gpu-burn.sh
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# build-gpu-burn.sh — build gpu_burn stress tool and output static-ish binary to DIST_DIR
|
||||||
|
#
|
||||||
|
# gpu_burn requires nvcc (CUDA toolkit). This script downloads a minimal CUDA toolkit
|
||||||
|
# runfile, extracts only nvcc + headers, builds gpu_burn, then cleans up the toolkit.
|
||||||
|
#
|
||||||
|
# Output: $DIST_DIR/gpu_burn (ready to copy into ISO vendor/)
|
||||||
|
#
|
||||||
|
# Usage: sh build-gpu-burn.sh <dist-dir>
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
DIST_DIR="$1"
|
||||||
|
[ -n "$DIST_DIR" ] || { echo "usage: $0 <dist-dir>"; exit 1; }
|
||||||
|
mkdir -p "$DIST_DIR"
|
||||||
|
|
||||||
|
OUTPUT="$DIST_DIR/gpu_burn"
|
||||||
|
if [ -f "$OUTPUT" ] && [ -s "$OUTPUT" ]; then
|
||||||
|
echo "=== gpu_burn cached: $OUTPUT ==="
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CUDA toolkit version for building — only nvcc + headers needed, not the full runtime.
|
||||||
|
# Must be <= max CUDA version supported by the NVIDIA driver in VERSIONS.
|
||||||
|
# Driver 590.48.01 supports up to CUDA 13.1; use 12.6 (stable, widely tested).
|
||||||
|
CUDA_VERSION="12.8.1"
|
||||||
|
CUDA_BUILD="570.124.06"
|
||||||
|
CUDA_RUN="/var/tmp/cuda-${CUDA_VERSION}.run"
|
||||||
|
CUDA_DIR="/var/tmp/cuda-toolkit-${CUDA_VERSION}"
|
||||||
|
|
||||||
|
echo "=== building gpu_burn (CUDA ${CUDA_VERSION}) ==="
|
||||||
|
|
||||||
|
# Install build dependencies
|
||||||
|
apk add --quiet gcc g++ make git wget libxml2
|
||||||
|
|
||||||
|
# Download CUDA toolkit runfile if not cached
|
||||||
|
if [ ! -s "$CUDA_RUN" ]; then
|
||||||
|
echo "=== downloading CUDA ${CUDA_VERSION} toolkit ==="
|
||||||
|
wget -q --show-progress -O "$CUDA_RUN" \
|
||||||
|
"https://developer.download.nvidia.com/compute/cuda/${CUDA_VERSION}/local_installers/cuda_${CUDA_VERSION}_${CUDA_BUILD}_linux.run"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract toolkit (nvcc + headers only — skip driver, samples, docs to save time/space)
|
||||||
|
if [ ! -d "$CUDA_DIR/bin/nvcc" ] && [ ! -f "$CUDA_DIR/bin/nvcc" ]; then
|
||||||
|
echo "=== extracting CUDA toolkit ==="
|
||||||
|
rm -rf "$CUDA_DIR"
|
||||||
|
sh "$CUDA_RUN" \
|
||||||
|
--silent \
|
||||||
|
--toolkit \
|
||||||
|
--toolkitpath="$CUDA_DIR" \
|
||||||
|
--no-opengl-libs \
|
||||||
|
--no-drm \
|
||||||
|
--override 2>&1 | tail -5
|
||||||
|
fi
|
||||||
|
|
||||||
|
NVCC="$CUDA_DIR/bin/nvcc"
|
||||||
|
[ -f "$NVCC" ] || { echo "ERROR: nvcc not found after extraction: $NVCC"; exit 1; }
|
||||||
|
echo "nvcc: $("$NVCC" --version | head -1)"
|
||||||
|
|
||||||
|
# Clone gpu_burn source
|
||||||
|
GPU_BURN_DIR="/var/tmp/gpu-burn-src"
|
||||||
|
if [ ! -d "$GPU_BURN_DIR/.git" ]; then
|
||||||
|
echo "=== cloning gpu-burn ==="
|
||||||
|
git clone --depth=1 https://github.com/wilicc/gpu-burn.git "$GPU_BURN_DIR"
|
||||||
|
else
|
||||||
|
echo "=== gpu-burn source already cloned ==="
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build
|
||||||
|
echo "=== building gpu_burn ==="
|
||||||
|
cd "$GPU_BURN_DIR"
|
||||||
|
make clean 2>/dev/null || true
|
||||||
|
CUDA_PATH="$CUDA_DIR" make 2>&1
|
||||||
|
|
||||||
|
[ -f "$GPU_BURN_DIR/gpu_burn" ] || { echo "ERROR: gpu_burn binary not produced"; exit 1; }
|
||||||
|
|
||||||
|
cp "$GPU_BURN_DIR/gpu_burn" "$OUTPUT"
|
||||||
|
cp "$GPU_BURN_DIR/compare.ptx" "$(dirname "$OUTPUT")/compare.ptx" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "=== gpu_burn build complete ==="
|
||||||
|
ls -lh "$OUTPUT"
|
||||||
|
echo "NOTE: compare.ptx must be present in same dir as gpu_burn at runtime"
|
||||||
@@ -20,6 +20,11 @@ DIST_DIR="$2"
|
|||||||
[ -n "$NVIDIA_VERSION" ] || { echo "usage: $0 <nvidia-version> <dist-dir>"; exit 1; }
|
[ -n "$NVIDIA_VERSION" ] || { echo "usage: $0 <nvidia-version> <dist-dir>"; exit 1; }
|
||||||
[ -n "$DIST_DIR" ] || { echo "usage: $0 <nvidia-version> <dist-dir>"; exit 1; }
|
[ -n "$DIST_DIR" ] || { echo "usage: $0 <nvidia-version> <dist-dir>"; exit 1; }
|
||||||
|
|
||||||
|
# Always install linux-lts-dev to ensure headers match the ISO's kernel (Alpine 3.21 = 6.12.x).
|
||||||
|
# Without this, a builder with stale 6.6.x headers produces modules for the wrong kernel version.
|
||||||
|
echo "=== installing linux-lts-dev ==="
|
||||||
|
apk add --quiet linux-lts-dev
|
||||||
|
|
||||||
# Detect kernel version from installed headers (pick highest version if multiple).
|
# Detect kernel version from installed headers (pick highest version if multiple).
|
||||||
detect_kver() {
|
detect_kver() {
|
||||||
ls /usr/src/ 2>/dev/null \
|
ls /usr/src/ 2>/dev/null \
|
||||||
@@ -30,11 +35,6 @@ detect_kver() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KVER="$(detect_kver)"
|
KVER="$(detect_kver)"
|
||||||
if [ -z "$KVER" ]; then
|
|
||||||
echo "=== installing linux-lts-dev ==="
|
|
||||||
apk add --quiet linux-lts-dev
|
|
||||||
KVER="$(detect_kver)"
|
|
||||||
fi
|
|
||||||
KDIR="/usr/src/linux-headers-${KVER}"
|
KDIR="/usr/src/linux-headers-${KVER}"
|
||||||
echo "=== NVIDIA ${NVIDIA_VERSION} (proprietary) for kernel ${KVER} ==="
|
echo "=== NVIDIA ${NVIDIA_VERSION} (proprietary) for kernel ${KVER} ==="
|
||||||
|
|
||||||
@@ -107,6 +107,15 @@ cp "$EXTRACT_DIR/libnvidia-ml.so."* "$CACHE_DIR/lib/" 2>/dev/null || true
|
|||||||
# libcuda stub needed by nvidia-smi at runtime
|
# libcuda stub needed by nvidia-smi at runtime
|
||||||
cp "$EXTRACT_DIR/libcuda.so."* "$CACHE_DIR/lib/" 2>/dev/null || true
|
cp "$EXTRACT_DIR/libcuda.so."* "$CACHE_DIR/lib/" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Create soname symlinks required by nvidia-smi on Alpine (musl/glibc via gcompat)
|
||||||
|
for lib in libnvidia-ml libcuda; do
|
||||||
|
versioned=$(ls "$CACHE_DIR/lib/${lib}.so."[0-9]* 2>/dev/null | head -1)
|
||||||
|
[ -n "$versioned" ] || continue
|
||||||
|
base=$(basename "$versioned")
|
||||||
|
ln -sf "$base" "$CACHE_DIR/lib/${lib}.so.1" 2>/dev/null || true
|
||||||
|
ln -sf "${lib}.so.1" "$CACHE_DIR/lib/${lib}.so" 2>/dev/null || true
|
||||||
|
done
|
||||||
|
|
||||||
echo "=== NVIDIA build complete ==="
|
echo "=== NVIDIA build complete ==="
|
||||||
echo "cache: $CACHE_DIR"
|
echo "cache: $CACHE_DIR"
|
||||||
echo "modules: $(ls "$CACHE_DIR/modules/"*.ko | wc -l) .ko files"
|
echo "modules: $(ls "$CACHE_DIR/modules/"*.ko | wc -l) .ko files"
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# build.sh — production ISO build (unattended mode)
|
# build.sh — build bee ISO
|
||||||
|
#
|
||||||
|
# Single build script. Produces a bootable live ISO with SSH access, TUI, NVIDIA drivers.
|
||||||
|
#
|
||||||
|
# Run on Alpine builder VM as root after setup-builder.sh.
|
||||||
|
# Usage:
|
||||||
|
# sh iso/builder/build.sh [--authorized-keys /path/to/authorized_keys]
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
@@ -8,29 +14,92 @@ BUILDER_DIR="${REPO_ROOT}/iso/builder"
|
|||||||
OVERLAY_DIR="${REPO_ROOT}/iso/overlay"
|
OVERLAY_DIR="${REPO_ROOT}/iso/overlay"
|
||||||
DIST_DIR="${REPO_ROOT}/dist"
|
DIST_DIR="${REPO_ROOT}/dist"
|
||||||
VENDOR_DIR="${REPO_ROOT}/iso/vendor"
|
VENDOR_DIR="${REPO_ROOT}/iso/vendor"
|
||||||
|
AUTH_KEYS=""
|
||||||
|
|
||||||
|
# parse args
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--authorized-keys) AUTH_KEYS="$2"; shift 2 ;;
|
||||||
|
*) echo "unknown arg: $1"; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
. "${BUILDER_DIR}/VERSIONS"
|
. "${BUILDER_DIR}/VERSIONS"
|
||||||
export PATH="$PATH:/usr/local/go/bin"
|
export PATH="$PATH:/usr/local/go/bin"
|
||||||
|
|
||||||
echo "=== bee production ISO build ==="
|
# NOTE: lz4 compression for modloop is disabled — Alpine initramfs may not support lz4 squashfs.
|
||||||
echo "Alpine: ${ALPINE_VERSION}, Go: ${GO_VERSION}, NVIDIA: ${NVIDIA_DRIVER_VERSION}"
|
# Default xz compression is used until lz4 support is confirmed.
|
||||||
|
|
||||||
|
echo "=== bee ISO build ==="
|
||||||
|
echo "Alpine: ${ALPINE_VERSION}, Go: ${GO_VERSION}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- compile audit binary (static, Linux amd64) ---
|
||||||
|
# Skip rebuild if binary is newer than all Go source files.
|
||||||
AUDIT_BIN="${DIST_DIR}/bee-audit-linux-amd64"
|
AUDIT_BIN="${DIST_DIR}/bee-audit-linux-amd64"
|
||||||
mkdir -p "$DIST_DIR"
|
NEED_BUILD=1
|
||||||
|
if [ -f "$AUDIT_BIN" ]; then
|
||||||
|
NEWEST_SRC=$(find "${REPO_ROOT}/audit" -name '*.go' -newer "$AUDIT_BIN" | head -1)
|
||||||
|
[ -z "$NEWEST_SRC" ] && NEED_BUILD=0
|
||||||
|
fi
|
||||||
|
|
||||||
cd "${REPO_ROOT}/audit"
|
if [ "$NEED_BUILD" = "1" ]; then
|
||||||
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
|
echo "=== building audit binary ==="
|
||||||
go build \
|
cd "${REPO_ROOT}/audit"
|
||||||
-ldflags "-s -w -X main.Version=${AUDIT_VERSION}" \
|
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
|
||||||
-o "$AUDIT_BIN" \
|
go build \
|
||||||
./cmd/audit
|
-ldflags "-s -w -X main.Version=${AUDIT_VERSION:-$(date +%Y%m%d)}" \
|
||||||
|
-o "$AUDIT_BIN" \
|
||||||
|
./cmd/audit
|
||||||
|
echo "binary: $AUDIT_BIN"
|
||||||
|
echo "size: $(du -sh "$AUDIT_BIN" | cut -f1)"
|
||||||
|
else
|
||||||
|
echo "=== audit binary up to date, skipping build ==="
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- inject authorized_keys for SSH access ---
|
||||||
|
# Uses the same Ed25519 keys as release signing (from git.mchus.pro/mchus/keys).
|
||||||
|
# SSH public keys are stored alongside signing keys as ~/.keys/<name>.key.pub
|
||||||
|
AUTHORIZED_KEYS_FILE="${OVERLAY_DIR}/root/.ssh/authorized_keys"
|
||||||
|
mkdir -p "${OVERLAY_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
|
||||||
|
# auto-collect all developer SSH public keys from ~/.keys/*.key.pub
|
||||||
|
> "$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 " root password will be set to: bee / eeb"
|
||||||
|
echo " (generate a key with: sh keys/scripts/keygen.sh <your-name>)"
|
||||||
|
USE_PASSWORD_FALLBACK=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- password fallback: write marker file read by init script ---
|
||||||
|
if [ "${USE_PASSWORD_FALLBACK:-0}" = "1" ]; then
|
||||||
|
touch "${OVERLAY_DIR}/etc/bee-ssh-password-fallback"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- copy audit binary into overlay ---
|
||||||
mkdir -p "${OVERLAY_DIR}/usr/local/bin"
|
mkdir -p "${OVERLAY_DIR}/usr/local/bin"
|
||||||
cp "$AUDIT_BIN" "${OVERLAY_DIR}/usr/local/bin/audit"
|
cp "${DIST_DIR}/bee-audit-linux-amd64" "${OVERLAY_DIR}/usr/local/bin/audit"
|
||||||
chmod +x "${OVERLAY_DIR}/usr/local/bin/audit"
|
chmod +x "${OVERLAY_DIR}/usr/local/bin/audit"
|
||||||
|
|
||||||
# Copy optional vendor utilities if already fetched.
|
# --- vendor utilities (optional pre-fetched binaries) ---
|
||||||
for tool in storcli64 sas2ircu sas3ircu mstflint; do
|
for tool in storcli64 sas2ircu sas3ircu mstflint gpu_burn; do
|
||||||
if [ -f "${VENDOR_DIR}/${tool}" ]; then
|
if [ -f "${VENDOR_DIR}/${tool}" ]; then
|
||||||
cp "${VENDOR_DIR}/${tool}" "${OVERLAY_DIR}/usr/local/bin/${tool}"
|
cp "${VENDOR_DIR}/${tool}" "${OVERLAY_DIR}/usr/local/bin/${tool}"
|
||||||
chmod +x "${OVERLAY_DIR}/usr/local/bin/${tool}" || true
|
chmod +x "${OVERLAY_DIR}/usr/local/bin/${tool}" || true
|
||||||
@@ -40,21 +109,29 @@ for tool in storcli64 sas2ircu sas3ircu mstflint; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Build and inject NVIDIA proprietary modules + userspace tools.
|
# --- build NVIDIA kernel modules and inject into overlay ---
|
||||||
echo "=== building NVIDIA modules ==="
|
echo ""
|
||||||
|
echo "=== building NVIDIA ${NVIDIA_DRIVER_VERSION} modules ==="
|
||||||
sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}"
|
sh "${BUILDER_DIR}/build-nvidia-module.sh" "${NVIDIA_DRIVER_VERSION}" "${DIST_DIR}"
|
||||||
KVER="$(ls /usr/src/ 2>/dev/null | grep '^linux-headers-' | sed 's/linux-headers-//' | sort -V | tail -1)"
|
|
||||||
|
# Determine kernel version (same as what goes into the ISO — both use linux-lts from same Alpine)
|
||||||
|
KVER=$(ls /usr/src/ 2>/dev/null | grep '^linux-headers-' | sed 's/linux-headers-//' | sort -V | tail -1)
|
||||||
NVIDIA_CACHE="${DIST_DIR}/nvidia-${NVIDIA_DRIVER_VERSION}-${KVER}"
|
NVIDIA_CACHE="${DIST_DIR}/nvidia-${NVIDIA_DRIVER_VERSION}-${KVER}"
|
||||||
|
|
||||||
mkdir -p "${OVERLAY_DIR}/lib/modules/${KVER}/extra/nvidia"
|
# Inject .ko files into overlay at /usr/local/lib/nvidia/ (not /lib/modules/ — modloop squashfs
|
||||||
cp "${NVIDIA_CACHE}/modules/"*.ko "${OVERLAY_DIR}/lib/modules/${KVER}/extra/nvidia/"
|
# mounts over that path at boot and makes it read-only, so overlay content there is inaccessible)
|
||||||
|
OVERLAY_KMOD_DIR="${OVERLAY_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_DIR}/usr/local/bin" "${OVERLAY_DIR}/usr/lib"
|
mkdir -p "${OVERLAY_DIR}/usr/local/bin" "${OVERLAY_DIR}/usr/lib"
|
||||||
cp "${NVIDIA_CACHE}/bin/nvidia-smi" "${OVERLAY_DIR}/usr/local/bin/"
|
cp "${NVIDIA_CACHE}/bin/nvidia-smi" "${OVERLAY_DIR}/usr/local/bin/"
|
||||||
chmod +x "${OVERLAY_DIR}/usr/local/bin/nvidia-smi"
|
chmod +x "${OVERLAY_DIR}/usr/local/bin/nvidia-smi"
|
||||||
cp "${NVIDIA_CACHE}/lib/"* "${OVERLAY_DIR}/usr/lib/" 2>/dev/null || true
|
cp "${NVIDIA_CACHE}/lib/"* "${OVERLAY_DIR}/usr/lib/" 2>/dev/null || true
|
||||||
|
|
||||||
# Embed build metadata used at runtime.
|
|
||||||
|
# --- embed build metadata ---
|
||||||
mkdir -p "${OVERLAY_DIR}/etc"
|
mkdir -p "${OVERLAY_DIR}/etc"
|
||||||
BUILD_DATE="$(date +%Y-%m-%d)"
|
BUILD_DATE="$(date +%Y-%m-%d)"
|
||||||
GIT_COMMIT="$(git -C "${REPO_ROOT}" rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
GIT_COMMIT="$(git -C "${REPO_ROOT}" rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
||||||
@@ -67,12 +144,27 @@ ALPINE_VERSION=${ALPINE_VERSION}
|
|||||||
NVIDIA_DRIVER_VERSION=${NVIDIA_DRIVER_VERSION}
|
NVIDIA_DRIVER_VERSION=${NVIDIA_DRIVER_VERSION}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# --- export build info for genapkovl to inject into motd ---
|
||||||
|
BUILD_DATE=$(date +%Y-%m-%d)
|
||||||
|
GIT_COMMIT=$(git -C "${REPO_ROOT}" rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
||||||
|
export BEE_BUILD_INFO="${BUILD_DATE} git:${GIT_COMMIT} alpine:${ALPINE_VERSION} nvidia:${NVIDIA_DRIVER_VERSION}"
|
||||||
|
|
||||||
|
# --- build ISO using mkimage ---
|
||||||
|
mkdir -p "${DIST_DIR}"
|
||||||
|
echo ""
|
||||||
|
echo "=== building ISO ==="
|
||||||
|
|
||||||
|
# Install our mkimage profile where mkimage.sh can find it.
|
||||||
|
# ~/.mkimage is the user plugin directory loaded by mkimage.sh.
|
||||||
mkdir -p "${HOME}/.mkimage"
|
mkdir -p "${HOME}/.mkimage"
|
||||||
cp "${BUILDER_DIR}/mkimg.bee.sh" "${HOME}/.mkimage/"
|
cp "${BUILDER_DIR}/mkimg.bee.sh" "${HOME}/.mkimage/"
|
||||||
cp "${BUILDER_DIR}/genapkovl-bee.sh" "${HOME}/.mkimage/"
|
cp "${BUILDER_DIR}/genapkovl-bee.sh" "${HOME}/.mkimage/"
|
||||||
|
|
||||||
|
# Export overlay dir so the profile script can find it regardless of SRCDIR.
|
||||||
export BEE_OVERLAY_DIR="${OVERLAY_DIR}"
|
export BEE_OVERLAY_DIR="${OVERLAY_DIR}"
|
||||||
|
|
||||||
|
# Clean workdir selectively: remove everything except apks cache so packages aren't re-downloaded.
|
||||||
|
# mkimage stores each section in a hash-named subdir; apks_* dirs contain downloaded packages.
|
||||||
if [ -d /var/tmp/bee-iso-work ]; then
|
if [ -d /var/tmp/bee-iso-work ]; then
|
||||||
find /var/tmp/bee-iso-work -maxdepth 1 -mindepth 1 \
|
find /var/tmp/bee-iso-work -maxdepth 1 -mindepth 1 \
|
||||||
-not -name 'apks_*' -not -name 'kernel_*' \
|
-not -name 'apks_*' -not -name 'kernel_*' \
|
||||||
@@ -80,6 +172,9 @@ if [ -d /var/tmp/bee-iso-work ]; then
|
|||||||
-exec rm -rf {} + 2>/dev/null || true
|
-exec rm -rf {} + 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Run from /var/tmp to avoid git repo context conflicts and to ensure enough scratch space.
|
||||||
|
# mkinitfs/update-kernel use TMPDIR for initramfs build; tmpfs /tmp is only ~1GB.
|
||||||
|
# mkimage.sh sources genapkovl-*.sh from CWD (not from ~/.mkimage), so copy it here too.
|
||||||
export TMPDIR=/var/tmp
|
export TMPDIR=/var/tmp
|
||||||
cp "${BUILDER_DIR}/genapkovl-bee.sh" /var/tmp/
|
cp "${BUILDER_DIR}/genapkovl-bee.sh" /var/tmp/
|
||||||
cd /var/tmp
|
cd /var/tmp
|
||||||
@@ -93,5 +188,9 @@ sh /usr/share/aports/scripts/mkimage.sh \
|
|||||||
--profile bee
|
--profile bee
|
||||||
|
|
||||||
ISO="${DIST_DIR}/alpine-bee-${ALPINE_VERSION}-x86_64.iso"
|
ISO="${DIST_DIR}/alpine-bee-${ALPINE_VERSION}-x86_64.iso"
|
||||||
|
echo ""
|
||||||
echo "=== done ==="
|
echo "=== done ==="
|
||||||
echo "ISO: $ISO"
|
echo "ISO: $ISO"
|
||||||
|
echo "Size: $(du -sh "$ISO" 2>/dev/null | cut -f1 || echo 'not found')"
|
||||||
|
echo ""
|
||||||
|
echo "Boot via BMC virtual media and SSH to the server IP on port 22 as root."
|
||||||
|
|||||||
@@ -12,18 +12,19 @@ makefile() { OWNER="$1" PERMS="$2" FILENAME="$3"; cat > "$FILENAME"; chown "$OWN
|
|||||||
rc_add() { mkdir -p "$tmp/etc/runlevels/$2"; ln -sf /etc/init.d/"$1" "$tmp/etc/runlevels/$2/$1"; }
|
rc_add() { mkdir -p "$tmp/etc/runlevels/$2"; ln -sf /etc/init.d/"$1" "$tmp/etc/runlevels/$2/$1"; }
|
||||||
|
|
||||||
mkdir -p "$tmp/etc"
|
mkdir -p "$tmp/etc"
|
||||||
makefile root:root 0644 "$tmp/etc/hostname" <<EOT
|
makefile root:root 0644 "$tmp/etc/hostname" <<EOF
|
||||||
$HOSTNAME
|
$HOSTNAME
|
||||||
EOT
|
EOF
|
||||||
|
|
||||||
|
# Empty interfaces file — prevents ifupdown from erroring, bee-network handles DHCP
|
||||||
mkdir -p "$tmp/etc/network"
|
mkdir -p "$tmp/etc/network"
|
||||||
makefile root:root 0644 "$tmp/etc/network/interfaces" <<EOT
|
makefile root:root 0644 "$tmp/etc/network/interfaces" <<EOF
|
||||||
auto lo
|
auto lo
|
||||||
iface lo inet loopback
|
iface lo inet loopback
|
||||||
EOT
|
EOF
|
||||||
|
|
||||||
mkdir -p "$tmp/etc/apk"
|
mkdir -p "$tmp/etc/apk"
|
||||||
makefile root:root 0644 "$tmp/etc/apk/world" <<EOT
|
makefile root:root 0644 "$tmp/etc/apk/world" <<EOF
|
||||||
alpine-base
|
alpine-base
|
||||||
dmidecode
|
dmidecode
|
||||||
smartmontools
|
smartmontools
|
||||||
@@ -34,12 +35,18 @@ util-linux
|
|||||||
lsblk
|
lsblk
|
||||||
e2fsprogs
|
e2fsprogs
|
||||||
lshw
|
lshw
|
||||||
openrc
|
dropbear
|
||||||
ca-certificates
|
libqrencode-tools
|
||||||
tzdata
|
tzdata
|
||||||
jq
|
ca-certificates
|
||||||
wget
|
strace
|
||||||
EOT
|
procps
|
||||||
|
lsof
|
||||||
|
file
|
||||||
|
less
|
||||||
|
vim
|
||||||
|
dialog
|
||||||
|
EOF
|
||||||
|
|
||||||
rc_add devfs sysinit
|
rc_add devfs sysinit
|
||||||
rc_add dmesg sysinit
|
rc_add dmesg sysinit
|
||||||
@@ -58,14 +65,16 @@ rc_add mount-ro shutdown
|
|||||||
rc_add killprocs shutdown
|
rc_add killprocs shutdown
|
||||||
rc_add savecache shutdown
|
rc_add savecache shutdown
|
||||||
|
|
||||||
|
rc_add bee-sshsetup default
|
||||||
rc_add bee-network default
|
rc_add bee-network default
|
||||||
rc_add bee-update default
|
rc_add dropbear default
|
||||||
rc_add bee-nvidia default
|
rc_add bee-nvidia default
|
||||||
rc_add bee-audit default
|
rc_add bee-audit-debug default
|
||||||
|
|
||||||
if [ -d "$OVERLAY/etc" ]; then
|
if [ -d "$OVERLAY/etc" ]; then
|
||||||
cp -r "$OVERLAY/etc/." "$tmp/etc/"
|
cp -r "$OVERLAY/etc/." "$tmp/etc/"
|
||||||
chmod +x "$tmp/etc/init.d/"* 2>/dev/null || true
|
chmod +x "$tmp/etc/init.d/"* 2>/dev/null || true
|
||||||
|
[ -n "$BEE_BUILD_INFO" ] && sed -i "s/%%BUILD_INFO%%/${BEE_BUILD_INFO}/" "$tmp/etc/motd" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$tmp/usr"
|
mkdir -p "$tmp/usr"
|
||||||
@@ -74,9 +83,24 @@ if [ -d "$OVERLAY/usr" ]; then
|
|||||||
chmod +x "$tmp/usr/local/bin/"* 2>/dev/null || true
|
chmod +x "$tmp/usr/local/bin/"* 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -d "$OVERLAY/root" ]; then
|
||||||
|
mkdir -p "$tmp/root"
|
||||||
|
cp -r "$OVERLAY/root/." "$tmp/root/"
|
||||||
|
chmod 700 "$tmp/root/.ssh" 2>/dev/null || true
|
||||||
|
chmod 600 "$tmp/root/.ssh/authorized_keys" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
if [ -d "$OVERLAY/lib" ]; then
|
if [ -d "$OVERLAY/lib" ]; then
|
||||||
mkdir -p "$tmp/lib"
|
mkdir -p "$tmp/lib"
|
||||||
cp -r "$OVERLAY/lib/." "$tmp/lib/"
|
cp -r "$OVERLAY/lib/." "$tmp/lib/"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
tar -c -C "$tmp" etc usr lib 2>/dev/null | gzip -9n > "$HOSTNAME.apkovl.tar.gz"
|
mkdir -p "$tmp/etc/dropbear" "$tmp/etc/conf.d"
|
||||||
|
# -R: auto-generate host keys if missing
|
||||||
|
# no dependency on networking service — bee-network handles DHCP independently
|
||||||
|
makefile root:root 0644 "$tmp/etc/conf.d/dropbear" <<EOF
|
||||||
|
DROPBEAR_OPTS="-R -B"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
tar -c -C "$tmp" etc usr root lib 2>/dev/null | gzip -9n > "$HOSTNAME.apkovl.tar.gz"
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Alpine mkimage profile: bee (production)
|
# Alpine mkimage profile: bee
|
||||||
|
|
||||||
profile_bee() {
|
profile_bee() {
|
||||||
title="Bee Hardware Audit"
|
title="Bee Hardware Audit"
|
||||||
desc="Hardware audit LiveCD (production unattended mode)"
|
desc="Hardware audit LiveCD"
|
||||||
arch="x86_64"
|
arch="x86_64"
|
||||||
hostname="alpine-bee"
|
hostname="alpine-bee"
|
||||||
apkovl="genapkovl-bee.sh"
|
apkovl="genapkovl-bee.sh"
|
||||||
@@ -13,7 +13,8 @@ profile_bee() {
|
|||||||
kernel_addons=""
|
kernel_addons=""
|
||||||
initfs_cmdline="modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-lts quiet"
|
initfs_cmdline="modules=loop,squashfs,sd-mod,usb-storage modloop=/boot/modloop-lts quiet"
|
||||||
initfs_features="ata base cdrom ext4 mmc nvme raid scsi squashfs usb virtio nfit"
|
initfs_features="ata base cdrom ext4 mmc nvme raid scsi squashfs usb virtio nfit"
|
||||||
|
grub_mod="all_video disk part_gpt part_msdos linux normal configfile search search_label efi_gop fat iso9660 cat echo ls test true help gzio multiboot2 efi_uga"
|
||||||
|
syslinux_serial="0 115200"
|
||||||
apks="
|
apks="
|
||||||
alpine-base
|
alpine-base
|
||||||
linux-lts
|
linux-lts
|
||||||
@@ -38,10 +39,20 @@ profile_bee() {
|
|||||||
e2fsprogs
|
e2fsprogs
|
||||||
lshw
|
lshw
|
||||||
|
|
||||||
|
dropbear
|
||||||
openrc
|
openrc
|
||||||
ca-certificates
|
libqrencode-tools
|
||||||
tzdata
|
tzdata
|
||||||
jq
|
ca-certificates
|
||||||
wget
|
|
||||||
|
strace
|
||||||
|
procps
|
||||||
|
lsof
|
||||||
|
file
|
||||||
|
less
|
||||||
|
vim
|
||||||
|
dialog
|
||||||
|
|
||||||
|
gcompat
|
||||||
"
|
"
|
||||||
}
|
}
|
||||||
|
|||||||
176
iso/builder/smoketest.sh
Normal file
176
iso/builder/smoketest.sh
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# smoketest.sh — run on a live ISO via SSH to verify all critical components.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ssh root@<ip> 'sh -s' < smoketest.sh
|
||||||
|
# or: scp smoketest.sh root@<ip>:/var/tmp/ && ssh root@<ip> sh /var/tmp/smoketest.sh
|
||||||
|
#
|
||||||
|
# Exit code: 0 = all required checks passed, 1 = at least one required check failed.
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
WARN=0
|
||||||
|
|
||||||
|
ok() { echo "[ OK ] $*"; PASS=$((PASS+1)); }
|
||||||
|
fail() { echo "[ FAIL ] $*"; FAIL=$((FAIL+1)); }
|
||||||
|
warn() { echo "[ WARN ] $*"; WARN=$((WARN+1)); }
|
||||||
|
info() { echo "[ INFO ] $*"; }
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo " bee live ISO smoketest"
|
||||||
|
echo " host: $(uname -n)"
|
||||||
|
echo " kernel: $(uname -r)"
|
||||||
|
echo " date: $(date -u)"
|
||||||
|
echo "========================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- kernel version ---
|
||||||
|
KVER=$(uname -r)
|
||||||
|
info "kernel: $KVER"
|
||||||
|
|
||||||
|
# --- PATH ---
|
||||||
|
echo "-- PATH & binaries --"
|
||||||
|
for tool in dmidecode smartctl nvme ipmitool lspci audit; do
|
||||||
|
if p=$(PATH="/usr/local/bin:$PATH" command -v "$tool" 2>/dev/null); then
|
||||||
|
ok "$tool found: $p"
|
||||||
|
else
|
||||||
|
fail "$tool: NOT FOUND"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for tool in nvidia-smi gpu_burn; do
|
||||||
|
if p=$(PATH="/usr/local/bin:$PATH" command -v "$tool" 2>/dev/null); then
|
||||||
|
ok "$tool found: $p"
|
||||||
|
else
|
||||||
|
warn "$tool: NOT FOUND (optional but expected)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- NVIDIA modules --"
|
||||||
|
KO_DIR="/usr/local/lib/nvidia"
|
||||||
|
if [ -d "$KO_DIR" ]; then
|
||||||
|
ko_count=$(ls "$KO_DIR"/*.ko 2>/dev/null | wc -l)
|
||||||
|
ok "NVIDIA ko dir exists: $KO_DIR ($ko_count .ko files)"
|
||||||
|
else
|
||||||
|
fail "NVIDIA ko dir missing: $KO_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
for mod in nvidia nvidia_modeset nvidia_uvm; do
|
||||||
|
if /sbin/lsmod 2>/dev/null | grep -q "^$mod "; then
|
||||||
|
ok "module loaded: $mod"
|
||||||
|
else
|
||||||
|
fail "module NOT loaded: $mod"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- nvidia-smi --"
|
||||||
|
if PATH="/usr/local/bin:$PATH" command -v nvidia-smi >/dev/null 2>&1; then
|
||||||
|
if PATH="/usr/local/bin:$PATH" nvidia-smi -L 2>/dev/null | grep -q "GPU"; then
|
||||||
|
gpu_count=$(PATH="/usr/local/bin:$PATH" nvidia-smi -L 2>/dev/null | grep -c "GPU")
|
||||||
|
ok "nvidia-smi: $gpu_count GPU(s) found"
|
||||||
|
else
|
||||||
|
fail "nvidia-smi: runs but no GPUs detected"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
fail "nvidia-smi: not found in PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- lib symlinks --"
|
||||||
|
for lib in libnvidia-ml libcuda; do
|
||||||
|
if [ -f "/usr/lib/${lib}.so.1" ] || [ -L "/usr/lib/${lib}.so.1" ]; then
|
||||||
|
ok "/usr/lib/${lib}.so.1 exists"
|
||||||
|
else
|
||||||
|
fail "/usr/lib/${lib}.so.1 MISSING (nvidia-smi will fail)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- gcompat (glibc compat for nvidia-smi) --"
|
||||||
|
if [ -L /lib64/ld-linux-x86-64.so.2 ] || [ -f /lib64/ld-linux-x86-64.so.2 ]; then
|
||||||
|
ok "gcompat: /lib64/ld-linux-x86-64.so.2 present"
|
||||||
|
else
|
||||||
|
fail "gcompat: /lib64/ld-linux-x86-64.so.2 MISSING — nvidia-smi will fail to exec"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- openrc services --"
|
||||||
|
for svc in bee-nvidia bee-network; do
|
||||||
|
if rc-service "$svc" status >/dev/null 2>&1; then
|
||||||
|
ok "service running: $svc"
|
||||||
|
else
|
||||||
|
fail "service NOT running: $svc"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for svc in bee-audit-debug dropbear bee-sshsetup; do
|
||||||
|
if [ -f "/etc/init.d/$svc" ]; then
|
||||||
|
if rc-service "$svc" status >/dev/null 2>&1; then
|
||||||
|
ok "service running: $svc"
|
||||||
|
else
|
||||||
|
warn "service not running: $svc (may be one-shot)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- audit binary --"
|
||||||
|
AUDIT=/usr/local/bin/audit
|
||||||
|
if [ -x "$AUDIT" ]; then
|
||||||
|
ok "audit binary: present"
|
||||||
|
ver=$("$AUDIT" --version 2>/dev/null || "$AUDIT" version 2>/dev/null || echo "unknown")
|
||||||
|
info "audit version: $ver"
|
||||||
|
else
|
||||||
|
fail "audit binary: NOT FOUND at $AUDIT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- audit last run --"
|
||||||
|
if [ -f /var/log/bee-audit.log ]; then
|
||||||
|
last_line=$(tail -1 /var/log/bee-audit.log)
|
||||||
|
info "last log line: $last_line"
|
||||||
|
if grep -q "audit completed" /var/log/bee-audit.log 2>/dev/null; then
|
||||||
|
ok "audit: completed successfully"
|
||||||
|
elif grep -q "audit started" /var/log/bee-audit.log 2>/dev/null; then
|
||||||
|
warn "audit: started but may not have completed"
|
||||||
|
fi
|
||||||
|
# check for nvidia enrichment
|
||||||
|
if grep -q "nvidia: enrichment skipped" /var/log/bee-audit.log 2>/dev/null; then
|
||||||
|
reason=$(grep "nvidia: enrichment skipped" /var/log/bee-audit.log | tail -1)
|
||||||
|
fail "audit: nvidia enrichment skipped — $reason"
|
||||||
|
else
|
||||||
|
ok "audit: nvidia enrichment OK"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "audit: no log found at /var/log/bee-audit.log"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- network --"
|
||||||
|
if ip route show default 2>/dev/null | grep -q "default"; then
|
||||||
|
gw=$(ip route show default | awk '{print $3}' | head -1)
|
||||||
|
ok "default route: $gw"
|
||||||
|
else
|
||||||
|
fail "no default route"
|
||||||
|
fi
|
||||||
|
if ping -c1 -W3 1.1.1.1 >/dev/null 2>&1; then
|
||||||
|
ok "internet: reachable (1.1.1.1)"
|
||||||
|
else
|
||||||
|
fail "internet: unreachable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "-- /etc/profile.d/bee.sh PATH --"
|
||||||
|
if grep -q "/usr/local/bin" /etc/profile.d/bee.sh 2>/dev/null; then
|
||||||
|
ok "/etc/profile.d/bee.sh exports /usr/local/bin"
|
||||||
|
else
|
||||||
|
fail "/etc/profile.d/bee.sh does not add /usr/local/bin to PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " Results: OK=$PASS FAIL=$FAIL WARN=$WARN"
|
||||||
|
echo "========================================"
|
||||||
|
[ "$FAIL" -eq 0 ] && exit 0 || exit 1
|
||||||
1
iso/overlay/etc/dropbear/dropbear.conf
Normal file
1
iso/overlay/etc/dropbear/dropbear.conf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
DROPBEAR_OPTS="-p 22 -R -B"
|
||||||
@@ -1,20 +1,21 @@
|
|||||||
#!/sbin/openrc-run
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
description="Bee: run hardware audit (production unattended mode)"
|
description="Bee: run hardware audit"
|
||||||
|
|
||||||
depend() {
|
depend() {
|
||||||
need localmount
|
need localmount
|
||||||
after bee-update bee-nvidia
|
after bee-network bee-nvidia
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
ebegin "Running hardware audit"
|
ebegin "Running hardware audit"
|
||||||
/usr/local/bin/audit --output usb > /var/log/bee-audit.json 2>/var/log/bee-audit.log
|
/usr/local/bin/audit --output stdout > /var/log/bee-audit.json 2>/var/log/bee-audit.log
|
||||||
rc=$?
|
local rc=$?
|
||||||
if [ "$rc" -eq 0 ]; then
|
if [ $rc -eq 0 ]; then
|
||||||
einfo "Audit complete"
|
einfo "Audit complete: /var/log/bee-audit.json"
|
||||||
|
einfo "SSH in and inspect results. Dropbear is running."
|
||||||
else
|
else
|
||||||
ewarn "Audit finished with errors"
|
ewarn "Audit finished with errors — check /var/log/bee-audit.log"
|
||||||
fi
|
fi
|
||||||
eend 0
|
eend 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ description="Bee: bring up network interfaces via DHCP"
|
|||||||
|
|
||||||
depend() {
|
depend() {
|
||||||
need localmount
|
need localmount
|
||||||
before bee-update bee-audit
|
before bee-audit
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
description="Bee: load NVIDIA kernel modules"
|
description="Bee: load NVIDIA kernel modules"
|
||||||
|
|
||||||
|
NVIDIA_KO_DIR="/usr/local/lib/nvidia"
|
||||||
|
|
||||||
depend() {
|
depend() {
|
||||||
need localmount
|
need localmount
|
||||||
before bee-audit
|
before bee-audit
|
||||||
@@ -9,23 +11,39 @@ depend() {
|
|||||||
|
|
||||||
start() {
|
start() {
|
||||||
ebegin "Loading NVIDIA modules"
|
ebegin "Loading NVIDIA modules"
|
||||||
kver="$(uname -r)"
|
einfo "kernel: $(uname -r)"
|
||||||
einfo "kernel: ${kver}"
|
|
||||||
if [ -d "/lib/modules/${kver}/extra/nvidia" ]; then
|
if [ ! -d "$NVIDIA_KO_DIR" ]; then
|
||||||
einfo "module dir: /lib/modules/${kver}/extra/nvidia"
|
ewarn "NVIDIA module dir missing: $NVIDIA_KO_DIR"
|
||||||
ls "/lib/modules/${kver}/extra/nvidia"/*.ko 2>/dev/null | sed 's/^/ /' || true
|
eend 1
|
||||||
else
|
return 1
|
||||||
ewarn "module dir missing: /lib/modules/${kver}/extra/nvidia"
|
|
||||||
fi
|
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
|
for mod in nvidia nvidia-modeset nvidia-uvm; do
|
||||||
if modprobe "$mod" 2>/dev/null; then
|
ko="$NVIDIA_KO_DIR/${mod}.ko"
|
||||||
einfo "loaded: $mod"
|
[ -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
|
else
|
||||||
ewarn "failed to load: $mod"
|
ewarn "not found: $ko"
|
||||||
dmesg | tail -n 5 | sed 's/^/ dmesg: /' || true
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
28
iso/overlay/etc/init.d/bee-sshsetup
Executable file
28
iso/overlay/etc/init.d/bee-sshsetup
Executable 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
|
||||||
|
}
|
||||||
@@ -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
37
iso/overlay/etc/init.d/dropbear
Executable 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
13
iso/overlay/etc/inittab
Normal 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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -1 +1,21 @@
|
|||||||
export PATH="$PATH:/usr/local/bin"
|
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
|
||||||
|
|||||||
0
iso/overlay/root/.ssh/.gitkeep
Normal file
0
iso/overlay/root/.ssh/.gitkeep
Normal file
8
iso/overlay/usr/local/bin/bee-net-restart
Normal file
8
iso/overlay/usr/local/bin/bee-net-restart
Normal 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
15
iso/overlay/usr/local/bin/bee-network.sh
Executable file → Normal file
@@ -1,9 +1,12 @@
|
|||||||
#!/bin/sh
|
#!/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_PREFIX="bee-network"
|
||||||
|
|
||||||
log() { echo "[$LOG_PREFIX] $*"; }
|
log() { echo "[$LOG_PREFIX] $*"; }
|
||||||
|
|
||||||
|
# find physical interfaces: exclude lo and virtual (docker/virbr/veth/tun/tap)
|
||||||
interfaces=$(ip -o link show \
|
interfaces=$(ip -o link show \
|
||||||
| awk -F': ' '{print $2}' \
|
| awk -F': ' '{print $2}' \
|
||||||
| grep -v '^lo$' \
|
| grep -v '^lo$' \
|
||||||
@@ -16,9 +19,13 @@ if [ -z "$interfaces" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
for iface in $interfaces; do
|
for iface in $interfaces; do
|
||||||
ip link set "$iface" up 2>/dev/null || { log "WARN: failed to bring up $iface"; continue; }
|
log "bringing up $iface"
|
||||||
udhcpc -i "$iface" -b -t 0 -T 3 >/dev/null 2>&1 &
|
ip link set "$iface" up 2>/dev/null || { log "WARN: could not bring up $iface"; continue; }
|
||||||
log "dhcp started for $iface"
|
|
||||||
|
# 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
|
done
|
||||||
|
|
||||||
log "done"
|
log "done"
|
||||||
|
|||||||
581
iso/overlay/usr/local/bin/bee-tui
Executable file
581
iso/overlay/usr/local/bin/bee-tui
Executable 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
|
||||||
@@ -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 "$@"
|
|
||||||
Reference in New Issue
Block a user