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 1411Shell 141Markdown 85Rust 842918 Definiciones

⎇ main scripts/iso-image.sh Shell · 252 líneas · 15.1 KB Historia crudo

fix(instalador): la cuenta de quien va a usar la maquina se pregunta antes de instalar 0f66c9973 · sergio ·

1#!/bin/sh
2# iso-image.sh — Etapa E5 (SDD 13): produce un MEDIO LIVE booteable como ISO 9660 / El Torito.
3#
4# A diferencia de install-image.sh (E1, disco GPT con / persistente en vda2), un medio live arranca
5# ENTERO desde RAM: GRUB (El Torito) carga kernel + initramfs desde el ISO 9660, el kernel desempaqueta
6# el initramfs (el rootfs del producto) a un tmpfs y corre /init — sin tocar disco. Es el medio para
7# correr el instalador (`takana install /dev/sdX`) en hardware nuevo, o para probar el sistema sin
8# instalarlo.
9#
10# El ISO lo ARMA `xorriso` construido por takana desde fuente (recipes/xorriso.toml) — el host no lo
11# trae. La cadena de boot BIOS reusa los módulos GRUB i386-pc de E1, pero con el core El Torito
12# (formato i386-pc-eltorito + iso9660) en vez del core de disco (ext2 + biosdisk).
13#
14# Cadena: SeaBIOS → El Torito boot catalog → eltorito.img (cdboot+core GRUB) → lee (cd0)/boot/grub/
15# grub.cfg → `linux /boot/bzImage rdinit=/init` + `initrd /boot/initramfs.cpio.gz` → kernel desempaqueta
16# → /init (marcador + exec del init real del rootfs).
17#
18# Uso:
19#   ./scripts/iso-image.sh                       # crea work/hammer-live.iso
20#   BOOT=1 KVM=1 ./scripts/iso-image.sh          # además lo arranca desde el CD (auto-boot)
21#
22# Variables:
23#   ROOTFS   dir rootfs a empaquetar como initramfs   (default work/builder-rootfs)
24#   KERNEL   bzImage a embeber                          (default store/*-linux/boot/bzImage)
25#   ISO      ISO de salida                              (default work/hammer-live.iso)
26#   XORRISO  binario xorriso                            (default el del store; si no, host)
27#   GRUB_LIB módulos GRUB i386-pc                       (default /usr/lib/grub/i386-pc)
28#   CMDLINE  cmdline del kernel             (default "console=ttyS0 rdinit=/init")
29#   BOOT/KVM/MEM  passthrough al arranque de prueba
30set -eu
31ROOT="$(cd "$(dirname "$0")/.." && pwd)"
32cd "$ROOT"
33
34ROOTFS="${ROOTFS:-work/builder-rootfs}"
35ISO="${ISO:-work/hammer-live.iso}"
36KERNEL="${KERNEL:-$(ls store/*-linux/boot/bzImage 2>/dev/null | head -1)}"
37GRUB_LIB="${GRUB_LIB:-/usr/lib/grub/i386-pc}"
38GRUB_EFI_LIB="${GRUB_EFI_LIB:-/usr/lib/grub/x86_64-efi}"
39CMDLINE="${CMDLINE:-console=ttyS0 rdinit=/init}"
40# ⚠ Las herramientas salen del STORE por HASH VIGENTE, no por el primer glob ni por fecha
41# (2026-09-22): con dos artefactos del mismo paquete conviviendo, el más reciente puede ser el viejo.
42# Ver `scripts/lib/herramientas-desde-store.sh`.
43. "$(dirname "$0")/lib/herramientas-desde-store.sh"
44
45[ -d "$ROOTFS" ] || { echo "no existe ROOTFS: $ROOTFS" >&2; exit 1; }
46[ -e "$ROOTFS/sbin/init" ] || { echo "ROOTFS sin /sbin/init" >&2; exit 1; }
47[ -n "$KERNEL" ] && [ -r "$KERNEL" ] || { echo "no encuentro el kernel (set KERNEL=<bzImage>)" >&2; exit 1; }
48echo "==> herramientas del ensamblado"
49XORRISO="${XORRISO:-$(herramienta xorriso usr/bin/xorriso xorriso)}" || exit 1
50CPIO=$(herramienta libarchive usr/bin/bsdcpio cpio) || exit 1
51GZIP=$(herramienta gzip bin/gzip gzip) || exit 1
52MFORMAT=$(herramienta mtools usr/bin/mformat mformat) || exit 1
53MCOPY=$(herramienta   mtools usr/bin/mcopy   mcopy)   || exit 1
54MMD=$(herramienta     mtools usr/bin/mmd     mmd)     || exit 1
55# GRUB es el único que NO puede salir del store: no hay `recipes/grub.toml`. El ISO arranca por El
56# Torito (BIOS), así que sin él no hay ISO. La rama UEFI de este mismo script y `metal-iso.sh
57# EFI_STUB=1` no lo necesitan — ahí el bzImage ES el binario EFI (ADR 0018).
58[ -d "$GRUB_LIB" ] && [ -r "$GRUB_LIB/cdboot.img" ] || { echo "faltan módulos GRUB i386-pc en $GRUB_LIB — y NO hay receta de grub en el corpus (ver scripts/vigia-ensamblado.sh)" >&2; exit 1; }
59command -v grub-mkimage >/dev/null 2>&1 || { echo "falta grub-mkimage, y NO hay receta de grub: el ISO BIOS depende de un binario del host sin hash" >&2; exit 1; }
60echo "   ⚠ grub-mkimage ← $(command -v grub-mkimage) DEL HOST, sin hash: el gestor de arranque no es nuestro" >&2
61
62echo "==> xorriso: $XORRISO ($("$XORRISO" -version 2>&1 | grep -m1 'xorriso version' || echo '?'))"
63
64STAGE="work/.iso-stage"
65rm -rf "$STAGE"
66ISO_ROOT="$STAGE/iso"
67RFS="$STAGE/rfs"
68mkdir -p "$ISO_ROOT/boot/grub/i386-pc" "$RFS"
69
70# --- initramfs: rootfs (hardlinks) + /init marcador que arranca el init real ---
71echo "==> staging initramfs desde $ROOTFS (hardlinks) + /init marcador"
72cp -al "$ROOTFS" "$RFS/root"
73# El cpio se arma desde $RFS/root; inyectamos /init ahí (rdinit=/init lo busca en la raíz del initramfs).
74# /init del medio live: monta lo básico + marcador. Con AUTO_INSTALL=<dev>, instala desatendido a ese
75# disco y apaga (instalación unattended + scaffolding del test); si no, arranca el init real (live).
76{
77  echo '#!/bin/sh'
78  echo '# /init del medio live (Etapa E5).'
79  echo '/bin/busybox mount -t proc     proc /proc 2>/dev/null || true'
80  echo '/bin/busybox mount -t sysfs    sys  /sys  2>/dev/null || true'
81  echo '/bin/busybox mount -t devtmpfs dev  /dev  2>/dev/null || true'
82  echo 'echo "HAMMER-ISO-LIVE-OK: medio live arrancado desde El Torito"'
83  if [ -n "${AUTO_INSTALL:-}" ]; then
84    echo "echo 'HAMMER-AUTO-INSTALL: instalando a ${AUTO_INSTALL}'"
85    echo "if /usr/bin/takana-install '${AUTO_INSTALL}'; then echo 'TAKANA-INSTALL-OK'; else echo 'TAKANA-INSTALL-FAIL'; fi"
86    echo '/bin/busybox sync'
87    echo '/bin/busybox poweroff -f'
88  elif [ "${INSTALLER:-0}" = 1 ] && [ "${INSTALLER_INTERACTIVE:-1}" = 1 ]; then
89    # ISO instalador SIN AUTO_INSTALL ⇒ recibir al usuario con la TUI del instalador en la consola.
90    # Tras instalar (o cancelar) ofrece reiniciar o caer al live. TAKANA_INSTALL_INTERACTIVE fuerza el
91    # modo aunque /dev/console no pase el test -t (a esta altura stdin puede no ser un tty).
92    echo '[ -e /dev/console ] && exec 0</dev/console 1>/dev/console 2>/dev/console'
93    echo 'TAKANA_INSTALL_INTERACTIVE=1 /usr/bin/takana-install || true'
94    echo 'echo; echo "Instalación finalizada.  [r] reiniciar  ·  [Enter] shell en vivo"'
95    echo 'read -r _a || _a=""'
96    echo 'case "$_a" in r|R) /bin/busybox reboot -f ;; *) exec /sbin/init ;; esac'
97  else
98    echo 'exec /sbin/init'
99  fi
100} > "$RFS/root/init"
101chmod +x "$RFS/root/init"
102
103# --- INSTALLER=1: payload de instalación a disco DESDE el live (refinamiento #1) ---
104# Bundle kernel + GRUB (boot.img + core.img MBR-prefix + módulos) bajo /usr/lib/hammer/install/ y el
105# instalador en /usr/bin/takana-install. El core.img se arma acá (build-time, el host tiene
106# grub-mkimage); el live NO lo necesita — sólo dd's. core prefix (hd0,msdos1)/boot/grub para boot MBR.
107if [ "${INSTALLER:-0}" = 1 ]; then
108    echo "==> INSTALLER=1: bundling payload de instalación a disco (kernel + GRUB MBR + takana-install)"
109    IPL="$RFS/root/usr/lib/hammer/install"
110    mkdir -p "$IPL/grub/i386-pc" "$IPL/kernel"
111    cp "$KERNEL" "$IPL/kernel/bzImage"; chmod 0644 "$IPL/kernel/bzImage"
112    # Rama EFI del instalador (ADR 0010 paso 5): el kernel `linux-metal` hornea CONFIG_CMDLINE con
113    # initrd=/initramfs.cpio.gz rdinit=/init ⇒ arranca por EFI-stub como \EFI\BOOT\BOOTX64.EFI sin
114    # bootloader. Lo bundleamos junto al genérico; takana-install lo usa si el live corre en UEFI.
115    EFI_KERNEL="${EFI_KERNEL:-$(ls -dt store/*-linux-metal/boot/bzImage 2>/dev/null | head -1)}"
116    if [ -n "${EFI_KERNEL:-}" ] && [ -r "$EFI_KERNEL" ]; then
117        cp "$EFI_KERNEL" "$IPL/kernel/bzImage-efi"; chmod 0644 "$IPL/kernel/bzImage-efi"
118        echo "    + kernel EFI-stub (metal) para la rama UEFI del instalador"
119    else
120        echo "    ! sin kernel linux-metal en el store ⇒ el instalador sólo tendrá rama BIOS/MBR" >&2
121    fi
122    cp "$GRUB_LIB/boot.img" "$IPL/grub/boot.img"
123    cp "$GRUB_LIB"/*.mod "$GRUB_LIB"/*.lst "$IPL/grub/i386-pc/" 2>/dev/null || true
124    # core.img para disco MBR: prefix a la 1ª partición msdos, módulos de disco (biosdisk/part_msdos/ext2).
125    grub-mkimage -O i386-pc -p '(hd0,msdos1)/boot/grub' -o "$IPL/grub/core.img" \
126        biosdisk part_msdos ext2 normal configfile linux echo ls boot search
127    install -m 0755 "$ROOT/scripts/takana-live-install.sh" "$RFS/root/usr/bin/takana-install"
128    # La sección de la cuenta viaja CON el instalador: `scripts/lib/` no existe dentro del producto,
129    # y sin la librería el instalador no pregunta por ningún usuario — que es el defecto que esa
130    # librería existe para cerrar (medido en metal el 2026-10-14: máquina instalada e
131    # inadministrable). El instalador la busca acá y avisa en el resumen si no la encuentra.
132    install -d -m 0755 "$RFS/root/usr/lib/takana"
133    install -m 0644 "$ROOT/scripts/lib/cuenta.sh" "$RFS/root/usr/lib/takana/cuenta.sh"
134    # hammer-recover (static musl): auto-sana un upgrade interrumpido al arranque del sistema instalado.
135    RECOVER_BIN="$ROOT/target/x86_64-unknown-linux-musl/release/hammer-recover"
136    if [ ! -x "$RECOVER_BIN" ]; then
137        echo "==> compilando hammer-recover (static musl) para el payload"
138        cargo build --release --target x86_64-unknown-linux-musl -p takana-recover --manifest-path "$ROOT/Cargo.toml" >/dev/null 2>&1
139    fi
140    [ -x "$RECOVER_BIN" ] || { echo "no pude construir hammer-recover (target musl?)" >&2; exit 1; }
141    install -m 0755 "$RECOVER_BIN" "$IPL/hammer-recover"
142    # CLI `takana` (static musl) para el menú de arranque por grafo (ADR 0010): el hook de init del
143    # sistema instalado lo invoca como `takana boot menu`. El producto trae arje-zero/hammerd pero NO el
144    # CLI ⇒ lo bundleamos al payload; takana-install lo copia a /usr/bin/hammer del disco.
145    HAMMER_BIN="$ROOT/target/x86_64-unknown-linux-musl/release/hammer"
146    if [ ! -x "$HAMMER_BIN" ]; then
147        echo "==> compilando hammer CLI (static musl) para el payload"
148        cargo build --release --target x86_64-unknown-linux-musl -p takana-cli --manifest-path "$ROOT/Cargo.toml" >/dev/null 2>&1
149    fi
150    [ -x "$HAMMER_BIN" ] || { echo "no pude construir el CLI hammer (target musl?)" >&2; exit 1; }
151    install -m 0755 "$HAMMER_BIN" "$IPL/hammer"
152fi
153
154echo "==> empaquetando initramfs.cpio.gz"
155( cd "$RFS/root" && find . -print0 | "$CPIO" --null -o -H newc -R 0:0 2>/dev/null | "$GZIP" -1 ) > "$ISO_ROOT/boot/initramfs.cpio.gz"
156
157# --- kernel + módulos GRUB + grub.cfg en el árbol del ISO ---
158cp "$KERNEL" "$ISO_ROOT/boot/bzImage"; chmod 0644 "$ISO_ROOT/boot/bzImage"
159cp "$GRUB_LIB"/*.mod "$GRUB_LIB"/*.lst "$ISO_ROOT/boot/grub/i386-pc/" 2>/dev/null || true
160cat > "$ISO_ROOT/boot/grub/grub.cfg" <<CFG
161set timeout=2
162set default=0
163serial --unit=0 --speed=115200
164terminal_input serial console
165terminal_output serial console
166insmod iso9660
167insmod normal
168menuentry "hammer (live)" {
169    linux /boot/bzImage $CMDLINE
170    initrd /boot/initramfs.cpio.gz
171}
172CFG
173
174# --- core GRUB El Torito: igual que el de disco pero con iso9660+biosdisk en vez de ext2 ---
175echo "==> grub-mkimage El Torito (formato i386-pc-eltorito, módulos iso9660)"
176grub-mkimage -O i386-pc-eltorito -p '/boot/grub' -o "$ISO_ROOT/boot/grub/i386-pc/eltorito.img" \
177    biosdisk iso9660 part_gpt part_msdos normal configfile linux echo ls boot search
178
179# --- EFI=1: rama UEFI (refinamiento #3). Construye grubx64.efi (mismo grub.cfg, lee kernel+initrd del
180#     iso9660) y una ESP FAT con EFI/BOOT/BOOTX64.EFI, poblada con mtools (sin root). El kernel ya trae
181#     EFI_STUB; aquí GRUB EFI es el cargador (espeja la rama BIOS). xorriso añade un 2º El Torito (EFI). ---
182EFI_ARGS=""
183if [ "${EFI:-0}" = 1 ]; then
184    [ -n "${MTOOLS_DIR:-}" ] && { MFORMAT="$MTOOLS_DIR/mformat"; MCOPY="$MTOOLS_DIR/mcopy"; MMD="$MTOOLS_DIR/mmd"; }
185    [ -x "$MFORMAT" ] && [ -x "$MCOPY" ] || MFORMAT="$(command -v mformat 2>/dev/null || true)"
186    [ -n "$MFORMAT" ] && [ -x "$MFORMAT" ] || { echo "no encuentro mtools (build recipes/mtools.toml)" >&2; exit 1; }
187    [ -x "$MCOPY" ] || { MCOPY="$(command -v mcopy)"; MMD="$(command -v mmd)"; }
188    ESP="$ISO_ROOT/boot/grub/efi.img"
189    if [ "${EFI_STUB:-0}" = 1 ]; then
190        # --- EFI_STUB=1: arranque por EFI-STUB DIRECTO (sin GRUB-EFI). El bzImage ES el BOOTX64.EFI; la
191        #     firmware lo lanza por el path de medio removible \EFI\BOOT\BOOTX64.EFI. El cmdline va
192        #     HORNEADO en el kernel (CONFIG_CMDLINE, recipes/linux-metal.toml) con initrd=/initramfs.cpio.gz
193        #     ⇒ el EFI-stub carga el initrd de la PROPIA ESP (file.c convierte '/'→'\'). Esquiva el bug de
194        #     GRUB 2.14 que cuelga la rama UEFI con cualquier kernel. ---
195        INITRAMFS="$ISO_ROOT/boot/initramfs.cpio.gz"
196        [ -r "$INITRAMFS" ] || { echo "no encuentro $INITRAMFS para la ESP EFI-stub" >&2; exit 1; }
197        echo "==> EFI=1 EFI_STUB=1: kernel→BOOTX64.EFI + initramfs en la ESP (FAT32, sin GRUB-EFI)"
198        # ESP grande (kernel + initramfs + holgura) ⇒ FAT32 (-F), válido y recomendado para ESP en metal.
199        espkb=$(( ( $(stat -c%s "$KERNEL") + $(stat -c%s "$INITRAMFS") ) / 1024 + 8192 ))
200        rm -f "$ESP"; truncate -s "$(( espkb * 1024 ))" "$ESP"
201        "$MFORMAT" -F -i "$ESP" ::
202        "$MMD"   -i "$ESP" ::/EFI ::/EFI/BOOT
203        "$MCOPY" -i "$ESP" "$KERNEL"    ::/EFI/BOOT/BOOTX64.EFI
204        "$MCOPY" -i "$ESP" "$INITRAMFS" ::/initramfs.cpio.gz
205    else
206        # --- EFI=1 (default): GRUB EFI como cargador (espeja la rama BIOS, lee kernel+initrd del iso9660). ---
207        [ -d "$GRUB_EFI_LIB" ] || { echo "faltan módulos GRUB x86_64-efi en $GRUB_EFI_LIB" >&2; exit 1; }
208        echo "==> EFI=1: grub-mkimage x86_64-efi + ESP FAT (mtools: $MFORMAT)"
209        mkdir -p "$STAGE/efi"
210        grub-mkimage -O x86_64-efi -p '/boot/grub' -o "$STAGE/efi/bootx64.efi" \
211            part_gpt part_msdos fat iso9660 normal configfile linux echo ls boot search search_fs_uuid efi_gop efi_uga
212        # Tamaño ≥ 1.44 MiB; mformat SIN -F auto-elige FAT12/16 por nº de clusters (FAT32 exige ~33 MiB).
213        espkb=$(( ( $(stat -c%s "$STAGE/efi/bootx64.efi") / 1024 ) + 512 ))
214        [ "$espkb" -lt 1440 ] && espkb=1440
215        rm -f "$ESP"; truncate -s "$(( espkb * 1024 ))" "$ESP"
216        "$MFORMAT" -i "$ESP" ::
217        "$MMD"   -i "$ESP" ::/EFI ::/EFI/BOOT
218        "$MCOPY" -i "$ESP" "$STAGE/efi/bootx64.efi" ::/EFI/BOOT/BOOTX64.EFI
219    fi
220    EFI_ARGS="-eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat"
221fi
222
223# --- armar el ISO con xorriso (personalidad mkisofs), El Torito BIOS (+EFI si EFI=1) + isohybrid MBR ---
224echo "==> xorriso: armando $ISO (El Torito BIOS${EFI_ARGS:+ + EFI} + isohybrid MBR)"
225rm -f "$ISO"
226MBR_ARGS=""
227[ -r "$GRUB_LIB/boot_hybrid.img" ] && MBR_ARGS="--grub2-mbr $GRUB_LIB/boot_hybrid.img"
228# shellcheck disable=SC2086
229"$XORRISO" -as mkisofs \
230    -volid HAMMER_LIVE \
231    -graft-points \
232    $MBR_ARGS \
233    -b boot/grub/i386-pc/eltorito.img \
234    -no-emul-boot -boot-load-size 4 -boot-info-table \
235    --grub2-boot-info \
236    $EFI_ARGS \
237    -o "$ISO" \
238    "$ISO_ROOT" 2>&1 | grep -viE "^xorriso : NOTE|Drive current|Media current|Media status|Media summary|libisofs:|^xorriso : UPDATE" || true
239
240[ -s "$ISO" ] || { echo "FALLO: xorriso no produjo el ISO" >&2; exit 1; }
241rm -rf "$STAGE"
242echo "==> ISO live creado: $(du -h "$ISO" | cut -f1) — $ISO"
243echo "    auto-boot: qemu-system-x86_64 -cdrom $ISO -boot d -nographic"
244
245if [ "${BOOT:-0}" = 1 ]; then
246    echo "==> arranque de prueba (desde el CD El Torito)"
247    accel=""
248    [ "${KVM:-0}" = 1 ] && [ -w /dev/kvm ] && accel="-enable-kvm -cpu host"
249    # shellcheck disable=SC2086
250    exec qemu-system-x86_64 -m "${MEM:-4096}" -no-reboot -nographic $accel \
251        -cdrom "$ISO" -boot d
252fi