tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1182Shell 87Rust 63Markdown 541592 Sutichaykuna

scripts/hammer-install: instalador a disco in-place (Etapa E2)

`hammer install <device>`: vuelca el product-rootfs lean sobre un disco
destino (block device real /dev/sdX, o un fichero pre-dimensionado para
pruebas) con el mismo layout GPT + GRUB BIOS de E1 pero IN-PLACE. Tras
instalar, el disco arranca solo (SeaBIOS → GRUB → arje-zero → hammerd+
getty+sshd). Contraparte "a disco real" de product-image.sh (imagen portátil).

- install-image.sh generalizado: si IMG es block device (o PREALLOC=1 sobre
  fichero pre-dimensionado) NO trunca ni borra el nodo — valida capacidad e
  instala in-place. sfdisk/mke2fs -d/dd-splice/GRUB son idénticos (dd a un
  offset de device = a un offset de fichero). El camino rootless de E1
  (unshare -r + dd conv=sparse,notrunc + patch GRUB) sirve igual al device.
- hammer-install.sh: resuelve el product-rootfs, provisiona authorized_keys
  (AUTHKEYS=<file> o TESTKEY=1 efímera), guardas de seguridad (FORCE=1 para
  un block device, rechazo si está montado), y delega a install-image.sh.
  Con BOOT=1+TESTKEY=1 arranca el target y valida por SSH.

Validado: instalación IN-PLACE sobre un fichero pre-dimensionado de 2200M
(ejercita el camino exacto del device, sin hardware real) → el disco
instalado auto-bootea (sin -kernel) y sirve SSH: ls = "uutils coreutils
0.9.0", /dev/vda3→/store y /dev/vda4→/var/lib/hammer montadas. El device
real sólo añade el guard FORCE + autodetección [-b].

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

2d18ed6dcd531971a288eaa8c5aa035befdd18a6 · ruwaq sergio · (4 killa ñawpaq) · tayta 8ba571aeb · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

2 qillqakuna · +120 −3

diff --git a/scripts/hammer-install.sh b/scripts/hammer-install.sh
new file mode 100755
index 00000000..185497cd
--- /dev/null
+++ b/scripts/hammer-install.sh
@@ -0,0 +1,102 @@
+#!/usr/bin/env bash
+# hammer-install.sh — Etapa E2: instalador a disco. Vuelca el `product-rootfs` (lean: 4/4 + userland
+# Rust + sshd) sobre un DISCO destino (block device real `/dev/sdX`, o un fichero pre-dimensionado para
+# pruebas) con el mismo layout GPT + GRUB BIOS de la imagen E1, pero IN-PLACE sobre el target. Tras
+# instalar, el disco arranca solo (SeaBIOS → GRUB → kernel → arje-zero PID1 → hammerd+getty+sshd).
+#
+# Es la contraparte "a disco real" de scripts/product-image.sh (que produce un fichero imagen portátil):
+# la lógica de particionado/format/GRUB es la misma (delegada a install-image.sh); sólo cambia el target.
+#
+# Uso:
+#   sudo PRODUCT=<hash> AUTHKEYS=~/.ssh/id_ed25519.pub FORCE=1 ./scripts/hammer-install.sh /dev/sdX
+#   # prueba sin hardware (fichero pre-dimensionado, se valida el camino in-place + boot SSH):
+#   PRODUCT=<hash> TESTKEY=1 BOOT=1 ./scripts/hammer-install.sh work/target.img
+#
+# Variables:
+#   PRODUCT   product-rootfs sellado (hash/prefijo; default: el más reciente del store)
+#   AUTHKEYS  fichero authorized_keys a instalar para root (recomendado en instalación real)
+#   TESTKEY   1 ⇒ genera un par efímero y usa su pubkey como authorized_keys (para validar por SSH)
+#   FORCE     1 ⇒ requerido para escribir sobre un BLOCK DEVICE (seguridad anti-pisada)
+#   KERNEL    bzImage a embeber (default store/*-linux/boot/bzImage)
+#   ROOT_SIZE/STORE_SIZE/STATE_SIZE   MiB de cada partición (default 1024/512/512)
+#   BOOT      1 ⇒ tras instalar, arranca el target con QEMU (útil sólo para fichero/imagen) + handshake
+#   KVM/MEM/PORT/DEADLINE   passthrough del arranque de prueba
+set -euo pipefail
+ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
+TARGET="${1:-}"
+[ -n "$TARGET" ] || { echo "uso: $0 <device|imagen>   (p.ej. /dev/sdX o work/target.img)"; exit 2; }
+WORK="$ROOT/work/hammer-install"; RFS="$WORK/rootfs"
+
+# --- resolver product-rootfs sellado ---
+if [ -n "${PRODUCT:-}" ]; then
+  PDIR=$(ls -d store/"${PRODUCT#b3:}"*-product-rootfs 2>/dev/null | head -1)
+else
+  PDIR=$(ls -dt store/*-product-rootfs 2>/dev/null | head -1)
+fi
+[ -n "$PDIR" ] && [ -d "$PDIR" ] || { echo "no encuentro product-rootfs (corré 'hammer bootstrap product')"; exit 1; }
+echo "==> product-rootfs: $PDIR"
+echo "==> target:         $TARGET"
+
+# --- guardas de seguridad para un disco real ---
+PREALLOC=0
+if [ -b "$TARGET" ]; then
+  [ "${FORCE:-0}" = 1 ] || { echo "!! $TARGET es un BLOCK DEVICE — exijo FORCE=1 para escribirlo (borra datos)"; exit 1; }
+  if mount | grep -q "^$TARGET"; then echo "!! $TARGET (o una partición) está MONTADO — desmontá antes"; exit 1; fi
+  echo "==> escritura sobre block device REAL (FORCE=1)"
+elif [ -e "$TARGET" ]; then
+  PREALLOC=1   # fichero existente ⇒ in-place (no truncar): exactamente el camino de un device
+  echo "==> target fichero pre-dimensionado ⇒ instalación in-place (PREALLOC)"
+else
+  echo "==> target fichero nuevo ⇒ install-image lo crea (truncate)"
+fi
+
+# --- copia escribible del product-rootfs + provisión de authorized_keys ---
+rm -rf "$WORK"; mkdir -p "$WORK"
+cp -a "$PDIR"/. "$RFS"/; chmod -R u+w "$RFS"; rm -rf "$RFS/.hammer"
+install -d -m 0700 "$RFS/root/.ssh"
+if [ "${TESTKEY:-0}" = 1 ]; then
+  "$(ls store/*-openssh/usr/bin/ssh-keygen | head -1)" -t ed25519 -N '' -f "$WORK/clientkey" -q
+  cat "$WORK/clientkey.pub" > "$RFS/root/.ssh/authorized_keys"
+  echo "==> authorized_keys: llave de prueba efímera ($WORK/clientkey)"
+elif [ -n "${AUTHKEYS:-}" ] && [ -f "$AUTHKEYS" ]; then
+  cat "$AUTHKEYS" > "$RFS/root/.ssh/authorized_keys"
+  echo "==> authorized_keys: $AUTHKEYS"
+else
+  echo "==> (sin authorized_keys: nadie podrá entrar por SSH hasta provisionarlas; usá AUTHKEYS= o TESTKEY=1)"
+fi
+chmod 0600 "$RFS/root/.ssh/authorized_keys" 2>/dev/null || true
+
+# --- delegar particionado/format/GRUB a install-image.sh, apuntando al target (IN-PLACE si aplica) ---
+echo "==> instalando sobre $TARGET (GPT + ext4 + GRUB BIOS, in-place=$PREALLOC)"
+ROOTFS="$RFS" IMG="$TARGET" PREALLOC="$PREALLOC" BOOT=0 \
+  ROOT_SIZE="${ROOT_SIZE:-1024}" STORE_SIZE="${STORE_SIZE:-512}" STATE_SIZE="${STATE_SIZE:-512}" \
+  sh ./scripts/install-image.sh
+echo "==> instalado. El disco arranca solo (SeaBIOS → GRUB → arje-zero)."
+
+[ "${BOOT:-0}" = 1 ] || { echo "    (set BOOT=1 para arrancar el target y probar SSH)"; exit 0; }
+[ "${TESTKEY:-0}" = 1 ] || { echo "    BOOT=1 sin TESTKEY=1: no tengo llave para el handshake; omito."; exit 0; }
+
+# --- arranque de prueba del target (sólo fichero/imagen) + handshake SSH ---
+KVM="${KVM:-1}"; MEM="${MEM:-2048}"; PORT="${PORT:-2225}"; DEADLINE="${DEADLINE:-150}"
+accel=(-cpu Broadwell); { [ "$KVM" = 1 ] && [ -w /dev/kvm ]; } && accel=(-enable-kvm -cpu host)
+echo "==> AUTO-BOOT del target (sin -kernel) + hostfwd :$PORT->:22"
+qemu-system-x86_64 -m "$MEM" -no-reboot -nographic "${accel[@]}" \
+  -drive file="$TARGET",format=raw,if=virtio \
+  -netdev "user,id=n0,hostfwd=tcp::$PORT-:22" -device e1000,netdev=n0 > "$WORK/console.log" 2>&1 &
+QPID=$!; trap 'kill $QPID 2>/dev/null || true' EXIT
+SSHOPTS=(-p "$PORT" -i "$WORK/clientkey" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null
+         -o GlobalKnownHostsFile=/dev/null -o PasswordAuthentication=no -o ConnectTimeout=4 -o LogLevel=ERROR)
+echo "==> esperando handshake desde el disco instalado (deadline ${DEADLINE}s)…"
+start=$(date +%s); ok=0
+while [ $(( $(date +%s) - start )) -lt "$DEADLINE" ]; do
+  kill -0 $QPID 2>/dev/null || { echo "!! QEMU murió"; break; }
+  if out=$(ssh "${SSHOPTS[@]}" root@localhost \
+        'echo INSTALLED_DISK_SSH_OK uid=$(id -u); uname -sr; ls --version 2>&1|head -1; df -h /store /var/lib/hammer 2>/dev/null|tail -2' 2>/dev/null); then
+    echo "========= RESPUESTA DEL DISCO INSTALADO ========="; echo "$out"; echo "================================================="
+    echo "$out" | grep -q INSTALLED_DISK_SSH_OK && ok=1; break
+  fi
+  sleep 3
+done
+echo "---- consola (cola) ----"; tail -12 "$WORK/console.log" 2>/dev/null
+[ "$ok" = 1 ] && { echo; echo "*** HAMMER INSTALL OK — disco instalado in-place arranca solo y sirve SSH ***"; exit 0; }
+echo; echo "*** HAMMER INSTALL FALLÓ — ver $WORK/console.log ***"; exit 1
diff --git a/scripts/install-image.sh b/scripts/install-image.sh
index a29f276a..6d68cef1 100644
--- a/scripts/install-image.sh
+++ b/scripts/install-image.sh
@@ -18,8 +18,13 @@
 #   ./scripts/install-image.sh                                  # crea work/hammer-install.img
 #   BOOT=1 KVM=1 ./scripts/install-image.sh                     # además lo arranca (auto-boot, sin -kernel)
 #
+# Target IN-PLACE (Etapa E2): si IMG es un block device (o PREALLOC=1 sobre un fichero pre-dimensionado),
+# se instala EN SITIO — no se trunca ni se borra el nodo, sólo se valida capacidad. Esa es la base de
+# `hammer install /dev/sdX` (ver scripts/hammer-install.sh): misma lógica, apuntando a un disco real.
+#
 # Variables:
 #   ROOTFS      dir rootfs a empaquetar       (default work/builder-rootfs)
+#   PREALLOC    1 ⇒ trata IMG como target pre-dimensionado in-place (no trunca; prueba el camino device)
 #   KERNEL      bzImage a embeber en /boot     (default el del store: store/*-linux/boot/bzImage)
 #   IMG         imagen de salida               (default work/hammer-install.img)
 #   ROOT_SIZE   MiB de /         (default 4096)
@@ -104,9 +109,19 @@ START4=$(( START3 + STORE_SECT ))
 TOTAL_SECT=$(( START4 + STATE_SECT + 2048 ))
 BIOS_GUID="21686148-6449-6E6F-744E-656564454649" # GPT "BIOS boot partition"
 
-echo "==> imagen : $IMG (GPT BIOS-boot, root ${ROOT_SIZE}M + store ${STORE_SIZE}M + estado ${STATE_SIZE}M)"
-rm -f "$IMG"
-truncate -s $(( TOTAL_SECT * 512 )) "$IMG"
+# Target = block device (instalación in-place, Etapa E2 `hammer install /dev/sdX`) o fichero imagen.
+# Un device tiene tamaño fijo: NO se trunca ni se borra el nodo; se valida capacidad. PREALLOC=1 fuerza
+# este modo sobre un fichero pre-dimensionado (para probar el camino de device sin hardware real).
+TOTAL_BYTES=$(( TOTAL_SECT * 512 ))
+if [ -b "$IMG" ] || [ "${PREALLOC:-0}" = 1 ]; then
+    cur=$(blockdev --getsize64 "$IMG" 2>/dev/null || stat -c%s "$IMG" 2>/dev/null || echo 0)
+    [ "$cur" -ge "$TOTAL_BYTES" ] || { echo "target $IMG es ${cur}B < ${TOTAL_BYTES}B requeridos" >&2; exit 1; }
+    echo "==> target IN-PLACE: $IMG ($(( cur/1024/1024 ))M disponibles; root ${ROOT_SIZE}M + store ${STORE_SIZE}M + estado ${STATE_SIZE}M)"
+else
+    echo "==> imagen : $IMG (GPT BIOS-boot, root ${ROOT_SIZE}M + store ${STORE_SIZE}M + estado ${STATE_SIZE}M)"
+    rm -f "$IMG"
+    truncate -s "$TOTAL_BYTES" "$IMG"
+fi
 sfdisk --quiet "$IMG" >/dev/null <<EOF
 label: gpt
 start=$START1, size=$BIOS_SECT,  type=$BIOS_GUID, name="bios-boot"