arranque-grafo: hammer boot menu — orquestador del menú + wiring en el init (ADR 0010)

hammer boot menu ata las tres piezas del contrato: emite el grafo → lanza el
compositor (mirada, --compositor configurable) → activa el nodo que el usuario
dejó en boot-select. Graceful: sin compositor (servidor headless) emite el grafo
y sigue el arranque. Refactor: activate_and_report compartido con boot activate;
--out/--select configurables (testeable sin /run/hammer root-only).

Wiring: iso-image INSTALLER=1 + install-image-efi bundlean el CLI hammer (static
musl) al sistema instalado (el producto trae arje-zero/hammerd pero no el CLI);
el wrapper /sbin/init lo invoca tras hammer-recover, salida al serial (no pinta
tty0 ⇒ respeta cero-parpadeo). Cierra la pieza #3 del handoff mirada (quién
lanza el menú en el boot): lo ownea hammer, desde el hook de init.

Tests: boot_menu.rs (3 caminos del glue: graceful/sin-selección/selección→activate)
+ efi-disk-boot-test asevera que el menú corre. Validado en OVMF: pivote →
INIT-OK → menú (emite boot-graph.json + saltea sin mirada) → arje-zero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 21:30:30 -04:00
co-authored by Claude Opus 4.8
parent c7d1d9cf5c
commit a5be781ef6
6 changed files with 203 additions and 11 deletions
+16
View File
@@ -117,6 +117,16 @@ echo "==> staging root (hardlinks del rootfs, sin /store ni /var/lib/hammer)"
cp -al "$ROOTFS" "$ROOT_TREE"
rm -rf "$ROOT_TREE/store" "$ROOT_TREE/var/lib/hammer"
mkdir -p "$ROOT_TREE/store" "$ROOT_TREE/var/lib/hammer"
# CLI `hammer` (static musl) para el menú de arranque por grafo (ADR 0010): el hook de init lo invoca
# como `hammer boot menu`. El producto trae arje-zero/hammerd pero NO el CLI ⇒ lo inyectamos acá si
# está compilado (igual patrón que hammer-recover). Opcional: sin él, el wrapper saltea el menú.
HAMMER_MUSL="${HAMMER_MUSL:-$ROOT/target/x86_64-unknown-linux-musl/release/hammer}"
if [ -x "$HAMMER_MUSL" ]; then
cp "$HAMMER_MUSL" "$ROOT_TREE/usr/bin/hammer"; chmod 0755 "$ROOT_TREE/usr/bin/hammer"
echo " + CLI hammer (static musl) para el menú de arranque por grafo"
else
echo " ! sin hammer musl ($HAMMER_MUSL) ⇒ el disco no traerá el menú de arranque (cargo build --target x86_64-unknown-linux-musl -p hammer-cli)" >&2
fi
# /sbin/init: wrapper que emite un marcador a /dev/ttyS0 (sobrevive `quiet`, que suprime el printk
# kernel-side) y hace exec de arje-zero. Prueba positiva de que el switch_root llegó al init real.
rm -f "$ROOT_TREE/sbin/init"
@@ -127,6 +137,12 @@ cat > "$ROOT_TREE/sbin/init" <<'RINIT'
# /dev/ttyS0 no existe todavía y el echo se pierde). arje-zero re-monta lo que necesite, es idempotente.
/bin/busybox mount -t devtmpfs dev /dev 2>/dev/null || true
echo "HAMMER-EFI-INIT-OK: arje-zero PID1" > /dev/ttyS0 2>/dev/null || true
# Menú de arranque por grafo (ADR 0010): emite /run/hammer/boot-graph.json y, si hay un compositor
# (mirada), lo pinta sobre KMS para elegir un nodo. Graceful: sin compositor, emite el grafo y sigue.
# Salida al serial (no pinta tty0 ⇒ respeta el cero-parpadeo). No bloquea el arranque si falla.
if [ -x /usr/bin/hammer ]; then
/usr/bin/hammer boot menu > /dev/ttyS0 2>&1 || true
fi
exec /usr/bin/arje-zero
RINIT
chmod +x "$ROOT_TREE/sbin/init"