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…
la imagen USB, para cuando el salto no se puede — y tres bugs que sólo se ven por ese camino
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 TECLAS="(sleep 40; printf '4\nsi\n'; sleep 300)"
80else
81 TECLAS="cat /dev/null"
82fi
83ssh -o BatchMode=yes "root@$WORKER" "cd $REMOTO && \
84 OV=\$(ls /usr/share/edk2/ovmf/OVMF_CODE.fd /usr/share/ovmf/x64/OVMF_CODE.fd 2>/dev/null | head -1) && \
85 OVV=\$(ls /usr/share/edk2/ovmf/OVMF_VARS.fd /usr/share/ovmf/x64/OVMF_VARS.fd 2>/dev/null | head -1) && \
86 cp \"\$OVV\" vars.fd && \
87 $TECLAS | timeout 420 qemu-system-x86_64 -m 4096 -smp 2 -nographic -vga none -no-reboot \
88 -drive if=pflash,format=raw,unit=0,readonly=on,file=\"\$OV\" \
89 -drive if=pflash,format=raw,unit=1,file=vars.fd \
90 -drive file=$(basename "$IMG"),format=raw,if=virtio \
91 -drive file=destino.img,format=raw,if=virtio" > "$LOG" 2>&1 || true
92if [ -n "$INSTALAR" ]; then
93 echo "==> trayendo el disco destino de vuelta"
94 ssh -o BatchMode=yes "root@$WORKER" "gzip -c $REMOTO/destino.img" > "$P/destino-despues.img.gz"
95 "$GZIP" -d -c "$P/destino-despues.img.gz" > "$P/destino-despues.img"
96fi
97echo " log: $LOG ($(wc -l < "$LOG") líneas)"
98[ -n "$CONSERVAR" ] || ssh -o BatchMode=yes "root@$WORKER" "rm -rf $REMOTO"
99
100echo "==> comprobaciones (la CADENA, que es lo que el salto no prueba)"
101limpio() { sed 's/\x1b\[[0-9;]*[a-zA-Z]//g; s/\r//g' "$LOG"; }
102limpio > "$P/serie.txt"
103grep -q 'TAKANA-MEDIO: buscando el medio' "$P/serie.txt" \
104 && ok "la firmware lanzó BOOTX64.EFI y el EFI-stub cargó el pivote" \
105 || falla "no llegó al pivote: la firmware no arrancó el medio"
106grep -q 'TAKANA-MEDIO-OK: switch_root' "$P/serie.txt" \
107 && ok "el pivote encontró LABEL=takana-medio y pivotó" \
108 || falla "el pivote no encontró el medio"
109grep -q 'arrancado desde el MEDIO' "$P/serie.txt" \
110 && ok "el instalador sabe que vino del medio (y no dice «el sistema viejo ya no corre»)" \
111 || falla "el instalador no distinguió medio de salto"
112grep -q 'la pregunta salen por /dev/ttyS0\|salen por /dev/console' "$P/serie.txt" \
113 && ok "sin pantalla, la pregunta se fue al serial (vtconsole dummy detectado)" \
114 || falla "la consola elegida no fue la serie estando sin pantalla"
115grep -q 'el terreno de esta máquina' "$P/serie.txt" \
116 && ok "el censo del terreno salió por la consola" || falla "no salió el censo"
117# La prueba de que el medio NO se ofrece a sí mismo: su ext4 es la raíz viva y tiene que estar
118# marcada NO TOCAR, no ⚡. Ofrecerla sería ofrecer formatear el instalador mientras corre.
119grep -q 'es el MEDIO desde el que arrancaste' "$P/serie.txt" \
120 && ok "el medio NO se ofrece a sí mismo como destino" \
121 || falla "el medio no quedó marcado como intocable"
122grep -q 'NTFS\|NO TOCAR' "$P/serie.txt" && ok "hay particiones marcadas NO TOCAR" || falla "el censo no marcó nada"
123
124# La opción que se va a teclear tiene que ser la que creemos, y se comprueba ANTES de dar por bueno
125# nada: si el layout cambió, «4» ya no es el destino y el test tiene que decirlo.
126if [ -n "$INSTALAR" ]; then
127 grep -qE '^ +4 +/dev/vdb2 .*se puede usar' "$P/serie.txt" \
128 && ok "la opción 4 del menú es /dev/vdb2 (la que se tecleó)" \
129 || falla "la opción 4 no es /dev/vdb2: el layout cambió y se tecleó a ciegas"
130 grep -q 'takana instalada en' "$P/serie.txt" && ok "el instalador dijo que instaló" \
131 || falla "el instalador no llegó a instalar (¿las teclas no entraron por el serial?)"
132 A="$P/destino-despues.img"
133 BL2=$(herramienta util-linux sbin/blkid blkid) || exit 1
134 dd if="$A" of="$P/p2.img" bs=1M skip=129 count=2400 status=none
135 "$BL2" "$P/p2.img" 2>/dev/null | grep -q 'LABEL="hammer-root"' \
136 && ok "el destino quedó ext4 con LABEL=hammer-root" \
137 || falla "el destino no tiene LABEL=hammer-root: $("$BL2" "$P/p2.img" 2>&1 | head -1)"
138 MD=$(herramienta mtools usr/bin/mdir mdir) || exit 1
139 "$MD" -i "$A@@1048576" ::/EFI/takana 2>/dev/null < /dev/null | grep -qi 'takanax64' \
140 && ok "la ESP del destino tiene \\EFI\\takana\\takanax64.efi" || falla "la ESP del destino quedó sin nuestro cargador"
141 MC=$(herramienta mtools usr/bin/mcopy mcopy) || exit 1
142 rm -f "$P/vecino-despues.bin"
143 "$MC" -i "$A@@1048576" ::/EFI/Microsoft/Boot/bootmgfw.efi "$P/vecino-despues.bin" 2>/dev/null < /dev/null || true
144 [ "$(sha256sum "$P/ajeno.bin" | cut -d' ' -f1)" = "$(sha256sum "$P/vecino-despues.bin" 2>/dev/null | cut -d' ' -f1)" ] \
145 && ok "el bootmgfw.efi del vecino quedó byte a byte igual" || falla "el vecino de la ESP cambió"
146 rm -f "$P/p2.img" "$P/destino-despues.img" "$P/destino-despues.img.gz"
147fi
148
149echo
150if [ "$FALLOS" = 0 ]; then echo "VERDE — el USB arranca por firmware y llega al instalador."; else
151 echo "ROJO — $FALLOS comprobación(es). Log: $P/serie.txt" >&2; exit 1; fi