gioser

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 1572Shell 203Rust 109Markdown 993728 Sutichaykuna

⎇ main scripts/instalador-usb-test.sh Shell · 158 siq'ikuna · 9.1 KB Ñawpa willay ch'uya

primero se elige, después se salta — y sólo si hace falta (SDD 32) 36152da92 · Sergio ·

1#!/bin/sh
2# instalador-usb-test.sh — el USB, arrancado por FIRMWARE UEFI de verdad (OVMF), no por `-kernel`.
3#
4# ══ QUÉ PRUEBA ════════════════════════════════════════════════════════════════════════════════
5# La cadena entera del medio, que es lo que el salto NO ejercita:
6#
7#   firmware UEFI → \EFI\BOOT\BOOTX64.EFI (EFI-stub) → initramfs de PIVOTE
8#                 → findfs LABEL=takana-medio → switch_root → el instalador
9#
10# Con `-kernel` QEMU salta el firmware entero y no probaría ni la tabla, ni la ESP, ni el stub, ni
11# el nombre `\initramfs.cpio.gz` que el cmdline horneado exige. Por eso acá va OVMF por `pflash` y
12# **sin `-kernel`**: la firmware busca sola la ruta de medios removibles.
13#
14# ⚠ `-vga none`. No es por ahorrar: con pantalla, el instalador manda su pregunta a `tty0` —que es
15# lo correcto en un portátil— y por serie no se vería nada. Sin pantalla, el `vtconsole` es `dummy`,
16# el instalador lo detecta y se va al serial, que es donde este arnés puede mirar. Las dos ramas de
17# esa decisión se ejercitan: ésta acá, y la de pantalla en el metal del usuario.
18#
19# Uso:  scripts/instalador-usb-test.sh [--conservar]
20# Vars: WORKER (def dev.gioser.net) · IMG (el USB) · P (dir de trabajo)
21set -eu
22ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
23WORKER="${WORKER:-dev.gioser.net}"
24IMG="${IMG:-work/takana-instalador-usb.img}"
25P="${P:-work/.usb-test}"
26REMOTO="/tmp/usb-test-$$"
27CONSERVAR=""; INSTALAR="${INSTALAR:-}"
28for a in "$@"; do case "$a" in
29    --conservar) CONSERVAR=1 ;;
30    # Además de llegar al censo, CONTESTARLO: se teclea la opción por el serial y se comprueba
31    # después, sobre el disco, que se instaló donde se dijo. La opción va fija porque el layout lo
32    # fija este arnés, y el censo se comprueba ANTES de contestar para que un cambio de layout falle
33    # en voz alta en vez de elegir otra partición en silencio.
34    --instalar) INSTALAR=1 ;;
35    *) echo "opción desconocida: $a" >&2; exit 2 ;;
36esac; done
37
38[ -r "$IMG" ] || { echo "no encuentro $IMG (armalo con scripts/instalador-usb.sh)" >&2; exit 1; }
39
40. "$(dirname "$0")/lib/herramientas-desde-store.sh"
41SFDISK=$(herramienta util-linux sbin/sfdisk sfdisk)   || exit 1
42MFORMAT=$(herramienta mtools usr/bin/mformat mformat) || exit 1
43MMD=$(herramienta     mtools usr/bin/mmd     mmd)     || exit 1
44MCOPY=$(herramienta   mtools usr/bin/mcopy   mcopy)   || exit 1
45GZIP=$(herramienta    gzip bin/gzip gzip)             || exit 1
46
47FALLOS=0
48ok()   { echo "  ✓ $*"; }
49falla(){ echo "  ✗ $*" >&2; FALLOS=$(( FALLOS + 1 )); }
50
51mkdir -p "$P"
52D="$P/destino.img"
53echo "==> disco DESTINO con vecinos de verdad (ESP con Windows + 2400M libre + hueco)"
54rm -f "$D"; truncate -s 4096M "$D"
55"$SFDISK" --quiet "$D" <<'EOF'
56label: gpt
57start=2048,   size=262144,  type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, name="EFI System Partition"
58start=264192, size=4915200, type=linux, name="destino"
59EOF
60rm -f "$P/esp.img"; truncate -s 128M "$P/esp.img"
61"$MFORMAT" -F -v VECINO -i "$P/esp.img" ::
62"$MMD"   -i "$P/esp.img" ::/EFI ::/EFI/Microsoft ::/EFI/Microsoft/Boot
63head -c 400000 /dev/urandom > "$P/ajeno.bin"
64"$MCOPY" -i "$P/esp.img" "$P/ajeno.bin" ::/EFI/Microsoft/Boot/bootmgfw.efi
65dd if="$P/esp.img" of="$D" bs=1M seek=1 conv=notrunc status=none
66
67echo "==> subiendo al worker ($WORKER)"
68ssh -o BatchMode=yes "root@$WORKER" "mkdir -p $REMOTO"
69for f in "$IMG" "$D"; do
70    "$GZIP" -1 -c "$f" | ssh -o BatchMode=yes "root@$WORKER" "gzip -d -c > $REMOTO/$(basename "$f")"
71done
72
73echo "==> arrancando por FIRMWARE (OVMF, sin -kernel)"
74LOG="$P/serie.log"
75# La entrada del menú se TECLEA por el serial. El `sleep` de delante no es pereza: si los
76# caracteres llegan antes de que el instalador abra la consola se pierden, y el menú se queda
77# esperando una tecla que ya pasó.
78if [ -n "$INSTALAR" ]; then
79    # `/dev/vdb2`: el destino de este banco no tiene filesystem, así que el censo lo marca CRUDA y
80    # la confirmación NO es un «sí» — hay que escribir el nombre del dispositivo. Es la contención
81    # que protege un almacén de negación plausible de un instalador ingenuo, y este arnés la
82    # ejercita en vez de esquivarla.
83    TECLAS="(sleep 40; printf '4\n/dev/vdb2\nsi\n'; sleep 300)"
84else
85    TECLAS="cat /dev/null"
86fi
87ssh -o BatchMode=yes "root@$WORKER" "cd $REMOTO && \
88    OV=\$(ls /usr/share/edk2/ovmf/OVMF_CODE.fd /usr/share/ovmf/x64/OVMF_CODE.fd 2>/dev/null | head -1) && \
89    OVV=\$(ls /usr/share/edk2/ovmf/OVMF_VARS.fd /usr/share/ovmf/x64/OVMF_VARS.fd 2>/dev/null | head -1) && \
90    cp \"\$OVV\" vars.fd && \
91    $TECLAS | timeout 420 qemu-system-x86_64 -m 4096 -smp 2 -nographic -vga none -no-reboot \
92      -drive if=pflash,format=raw,unit=0,readonly=on,file=\"\$OV\" \
93      -drive if=pflash,format=raw,unit=1,file=vars.fd \
94      -drive file=$(basename "$IMG"),format=raw,if=virtio \
95      -drive file=destino.img,format=raw,if=virtio" > "$LOG" 2>&1 || true
96if [ -n "$INSTALAR" ]; then
97    echo "==> trayendo el disco destino de vuelta"
98    ssh -o BatchMode=yes "root@$WORKER" "gzip -c $REMOTO/destino.img" > "$P/destino-despues.img.gz"
99    "$GZIP" -d -c "$P/destino-despues.img.gz" > "$P/destino-despues.img"
100fi
101echo "    log: $LOG ($(wc -l < "$LOG") líneas)"
102[ -n "$CONSERVAR" ] || ssh -o BatchMode=yes "root@$WORKER" "rm -rf $REMOTO"
103
104echo "==> comprobaciones (la CADENA, que es lo que el salto no prueba)"
105limpio() { sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\r//g' "$LOG"; }
106limpio > "$P/serie.txt"
107grep -q 'TAKANA-MEDIO: buscando el medio' "$P/serie.txt" \
108    && ok "la firmware lanzó BOOTX64.EFI y el EFI-stub cargó el pivote" \
109    || falla "no llegó al pivote: la firmware no arrancó el medio"
110grep -q 'TAKANA-MEDIO-OK: switch_root' "$P/serie.txt" \
111    && ok "el pivote encontró LABEL=takana-medio y pivotó" \
112    || falla "el pivote no encontró el medio"
113grep -q 'arrancado desde el MEDIO' "$P/serie.txt" \
114    && ok "el instalador sabe que vino del medio (y no dice «el sistema viejo ya no corre»)" \
115    || falla "el instalador no distinguió medio de salto"
116grep -q 'la pregunta salen por /dev/ttyS0\|salen por /dev/console' "$P/serie.txt" \
117    && ok "sin pantalla, la pregunta se fue al serial (vtconsole dummy detectado)" \
118    || falla "la consola elegida no fue la serie estando sin pantalla"
119grep -q 'el terreno de esta máquina' "$P/serie.txt" \
120    && ok "el censo del terreno salió por la consola" || falla "no salió el censo"
121# La prueba de que el medio NO se ofrece a sí mismo: su ext4 es la raíz viva y tiene que estar
122# marcada NO TOCAR, no ⚡. Ofrecerla sería ofrecer formatear el instalador mientras corre.
123grep -q 'es el MEDIO desde el que arrancaste' "$P/serie.txt" \
124    && ok "el medio NO se ofrece a sí mismo como destino" \
125    || falla "el medio no quedó marcado como intocable"
126grep -q 'NTFS\|NO TOCAR' "$P/serie.txt" && ok "hay particiones marcadas NO TOCAR" || falla "el censo no marcó nada"
127
128# La opción que se va a teclear tiene que ser la que creemos, y se comprueba ANTES de dar por bueno
129# nada: si el layout cambió, «4» ya no es el destino y el test tiene que decirlo.
130if [ -n "$INSTALAR" ]; then
131    grep -qE '^ +4 +/dev/vdb2 ' "$P/serie.txt" \
132        && ok "la opción 4 del menú es /dev/vdb2 (la que se tecleó)" \
133        || falla "la opción 4 no es /dev/vdb2: el layout cambió y se tecleó a ciegas"
134    grep -q 'Escribí el nombre completo (/dev/vdb2)' "$P/serie.txt" \
135        && ok "lo que no se identifica pidió confirmación FUERTE (escribir el nombre)" \
136        || falla "una partición sin identificar se aceptó con un sí"
137    grep -q 'takana instalada en' "$P/serie.txt" && ok "el instalador dijo que instaló" \
138        || falla "el instalador no llegó a instalar (¿las teclas no entraron por el serial?)"
139    A="$P/destino-despues.img"
140    BL2=$(herramienta util-linux sbin/blkid blkid) || exit 1
141    dd if="$A" of="$P/p2.img" bs=1M skip=129 count=2400 status=none
142    "$BL2" "$P/p2.img" 2>/dev/null | grep -q 'LABEL="hammer-root"' \
143        && ok "el destino quedó ext4 con LABEL=hammer-root" \
144        || falla "el destino no tiene LABEL=hammer-root: $("$BL2" "$P/p2.img" 2>&1 | head -1)"
145    MD=$(herramienta mtools usr/bin/mdir mdir) || exit 1
146    "$MD" -i "$A@@1048576" ::/EFI/takana 2>/dev/null < /dev/null | grep -qi 'takanax64' \
147        && ok "la ESP del destino tiene \\EFI\\takana\\takanax64.efi" || falla "la ESP del destino quedó sin nuestro cargador"
148    MC=$(herramienta mtools usr/bin/mcopy mcopy) || exit 1
149    rm -f "$P/vecino-despues.bin"
150    "$MC" -i "$A@@1048576" ::/EFI/Microsoft/Boot/bootmgfw.efi "$P/vecino-despues.bin" 2>/dev/null < /dev/null || true
151    [ "$(sha256sum "$P/ajeno.bin" | cut -d' ' -f1)" = "$(sha256sum "$P/vecino-despues.bin" 2>/dev/null | cut -d' ' -f1)" ] \
152        && ok "el bootmgfw.efi del vecino quedó byte a byte igual" || falla "el vecino de la ESP cambió"
153    rm -f "$P/p2.img" "$P/destino-despues.img" "$P/destino-despues.img.gz"
154fi
155
156echo
157if [ "$FALLOS" = 0 ]; then echo "VERDE — el USB arranca por firmware y llega al instalador."; else
158    echo "ROJO — $FALLOS comprobación(es). Log: $P/serie.txt" >&2; exit 1; fi