#!/usr/bin/env bash # build-arje-initrd.sh — empaqueta ente-zero + shims compat + Tarjeta Semilla # en un initramfs CPIO+gzip listo para arrancar bajo QEMU o como /init real. # # Toolchain: musl-static por default. ente-zero es PID 1; cualquier # dependencia dinámica (libgcc_s, libc.so.6, ld-linux) que no esté en el # initramfs produce kernel panic. Compilar contra musl elimina el problema # completamente — un solo ELF estático. # # Layout del initrd resultante: # /init → wrapper sh que exec /sbin/ente-zero # /sbin/ente-zero → PID 1 (musl-static) # /usr/sbin/ente-*-compat → shims systemd (musl-static) # /usr/sbin/ente-echo, ente-policy-provider # /ente/seed.card.json → Tarjeta Semilla # /bin/{sh,ls,cat,...} → busybox-static (recomendado) # /dev, /proc, /sys, /run → puntos de montaje (ente-zero los monta) # # Uso: # scripts/build-arje-initrd.sh [seed.card.json] [out.cpio.gz] # # seed default: seeds/arje-prod.card.json # out default: out/arje.initrd.cpio.gz # # Env: # ARJE_TARGET triple de cargo (default: x86_64-unknown-linux-musl). # Para glibc dinámica usar x86_64-unknown-linux-gnu — en # ese caso hay que vendorear libgcc_s.so.1 + libc.so.6 + # ld-linux-x86-64.so.2 manualmente o el kernel paniquea. # BUSYBOX_BIN path a un busybox-static. Default: busca `busybox` en PATH. # EXTRA_BINS binarios extra a copiar (deben ser estáticos), space-separated. # # Requisitos del host: # - rust toolchain con el target musl instalado: # rustup target add x86_64-unknown-linux-musl # - musl-gcc para algunos crates con build.rs C (sled, etc.): # Debian/Ubuntu: apt install musl-tools # Alpine: apk add musl-dev gcc # Arch: pacman -S musl # - cpio, gzip. # - busybox-static (recomendado, opcional): # Debian/Ubuntu: apt install busybox-static # Alpine: (busybox viene built-in) set -euo pipefail SEED="${1:-seeds/arje-prod.card.json}" OUT="${2:-out/arje.initrd.cpio.gz}" TARGET="${ARJE_TARGET:-x86_64-unknown-linux-musl}" BUSYBOX_BIN="${BUSYBOX_BIN:-$(command -v busybox 2>/dev/null || true)}" EXTRA_BINS="${EXTRA_BINS:-}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$REPO_DIR" if [ ! -f "$SEED" ]; then echo "[build-initrd] seed no encontrada: $SEED" >&2 exit 2 fi # Chequeo del target: si es musl, debe estar instalado. Mensaje accionable # cuando falta — la causa #1 de fracaso al primer intento. if [[ "$TARGET" == *-musl ]]; then RUSTLIB="$(rustc --print sysroot 2>/dev/null)/lib/rustlib/$TARGET" if [ ! -d "$RUSTLIB" ]; then cat >&2 </dev/null 2>&1; then echo "[build-initrd] seed inválida: $SEED" >&2 exit 3 fi # 3. Sanity check: si compilamos musl, los ELF deben ser ESTÁTICOS. # Si quedó alguno con interpreter dinámico, el kernel paniquea. if [[ "$TARGET" == *-musl ]] && command -v file >/dev/null 2>&1; then bad=0 for b in "$BIN_DIR/ente-zero" "$BIN_DIR/ente-echo"; do [ -f "$b" ] || continue if file "$b" | grep -q "dynamically linked"; then echo "[build-initrd] WARN: $b quedó DYNAMIC (esperábamos static):" >&2 file "$b" >&2 bad=$((bad + 1)) fi done if [ "$bad" -gt 0 ]; then echo "[build-initrd] abortando — un binario PID-1 dinámico paniquea el kernel" >&2 exit 6 fi fi # 4. Stage root del initrd. STAGE="$(mktemp -d -t arje-initrd.XXXXXX)" trap 'rm -rf "$STAGE"' EXIT mkdir -p "$STAGE"/{bin,sbin,usr/bin,usr/sbin,etc,ente,proc,sys,dev,run,tmp,sys/fs/cgroup} # 5. Copiar binarios arje. install -m 0755 "$BIN_DIR/ente-zero" "$STAGE/sbin/ente-zero" for b in ente-echo ente-policy-provider \ ente-logind-compat ente-hostnamed-compat ente-timedated-compat \ ente-localed-compat ente-journald-compat ente-resolved-compat \ ente-polkit-compat ente-machined-compat ente-systemd1-compat \ ente-notify-compat ente-timer-compat ente-tmpfiles-compat \ ente-binfmt-compat; do install -m 0755 "$BIN_DIR/$b" "$STAGE/usr/sbin/$b" done # 5b. CLIs de administración → /usr/bin (no /usr/sbin: no son services). for e in brahman-status busctl brainctl; do install -m 0755 "$EX_DIR/$e" "$STAGE/usr/bin/$e" done # 6. Userspace mínimo. Con binarios musl-static no necesitamos vendorear # libc/ld-linux. Las dos rutas: # (a) busybox-static apuntado por $BUSYBOX_BIN → 1 binario, todo simlink. # (b) sin busybox → copiar /bin/sh + sus deps via ldd. Sólo seguro si el # /bin/sh del host es estático (ej. dash en algunos distros NO lo es). if [ -n "$BUSYBOX_BIN" ] && [ -x "$BUSYBOX_BIN" ]; then echo "[build-initrd] usando busybox-static: $BUSYBOX_BIN" # Validar que el busybox es realmente estático (causa frecuente de panic). if command -v file >/dev/null 2>&1 && \ file "$BUSYBOX_BIN" | grep -q "dynamically linked"; then echo "[build-initrd] ERROR: $BUSYBOX_BIN es dinámico — necesitás busybox-static" >&2 echo " apt install busybox-static # Debian/Ubuntu" >&2 exit 7 fi install -m 0755 "$BUSYBOX_BIN" "$STAGE/bin/busybox" ( cd "$STAGE/bin" && for app in sh ls cat mount umount mkdir cp mv \ echo grep sed awk ps kill sleep insmod modprobe poweroff reboot \ sysctl dmesg ip ifconfig; do ln -sf busybox "$app" done ) else echo "[build-initrd] WARN: sin busybox-static — vendoreando /bin/sh + libs via ldd" echo " Esto es FRÁGIL. Instalá busybox-static y reintentá:" echo " apt install busybox-static # Debian/Ubuntu" copy_lib() { local lib="$1" [ -f "$lib" ] || return 0 local dest="$STAGE${lib}" mkdir -p "$(dirname "$dest")" cp -L "$lib" "$dest" } for b in /bin/sh /bin/ls /bin/cat /bin/mount /bin/umount /bin/mkdir; do [ -x "$b" ] || continue cp -L "$b" "$STAGE${b}" while read -r lib; do copy_lib "$lib"; done < <( ldd "$b" 2>/dev/null | awk '{ for (i=1;i<=NF;i++) if ($i ~ /^\//) print $i }' ) done fi # 7. Tarjeta Semilla. Path canónico en prod: /ente/seed.card.json install -m 0644 "$SEED" "$STAGE/ente/seed.card.json" # 8. /init wrapper. El kernel pasa control a /init; nosotros invocamos # ente-zero como PID 1 real con su env mínimo. cat > "$STAGE/init" <<'EOF' #!/bin/sh # arje /init — kernel → este script → ente-zero (PID 1 lo hereda via exec) export PATH=/usr/sbin:/usr/bin:/sbin:/bin export RUST_LOG="${RUST_LOG:-ente_zero=info,brahman_handshake=info,info}" # ente-zero monta /proc /sys /dev /sys/fs/cgroup él mismo. exec /sbin/ente-zero EOF chmod 0755 "$STAGE/init" # 9. Binarios extra a vendorear (deben ser estáticos). if [ -n "$EXTRA_BINS" ]; then for b in $EXTRA_BINS; do [ -x "$b" ] || { echo "[build-initrd] EXTRA_BINS: $b no existe"; exit 4; } install -m 0755 "$b" "$STAGE/usr/sbin/$(basename "$b")" done fi # 10. Empaquetar CPIO + gzip. Formato newc (estándar para Linux initramfs). mkdir -p "$(dirname "$OUT")" ( cd "$STAGE" && find . -print0 | cpio -o -H newc --null --quiet ) | gzip -9 > "$OUT" echo "[build-initrd] generado: $OUT ($(du -h "$OUT" | cut -f1))" echo "[build-initrd] target: $TARGET"