#!/bin/sh # ============================================================================= # arje-botones.sh — que `reboot`, `poweroff`, `halt` y `shutdown` hagan algo # en una imagen cuyo PID 1 es `arje-zero`. Se corre SOBRE UN ROOTFS, al armar # la imagen, no sobre la máquina viva. # # Uso: ./scripts/servidor/arje-botones.sh # # ── POR QUÉ EXISTE (2026-09-25) ───────────────────────────────────────────── # # `servidor-image.sh` §3 ya se ocupa de que `/sbin/init` sea `arje-zero`. Esto # es el paso siguiente y sin él la imagen queda con un defecto silencioso: con # arje-zero de PID 1, **`reboot` no reinicia y no dice nada**. # # La causa, medida en `takana` el 2026-09-25 (y antes en `momento`, 26-ago): # el `reboot` de la caja —sea el de OpenRC, sea el applet de busybox— termina # mandándole una señal a PID 1. arje-zero atrapa **SIGINT y sólo SIGINT** # (`/proc/1/status` → `SigCgt: 0000000000000442`; los otros dos bits son el # SIGBUS/SIGSEGV que instala el runtime de Rust). Y el kernel **descarta** las # señales sin handler dirigidas a PID 1 — no es que falle: no pasa nada, sin # error y sin mensaje. Es el peor modo de fallar, y costó una tarde cada vez. # # Del otro lado, `arje-zero::apagar_maquina()` está implementado de verdad # (cascade topológico, kill(-1), sync, remount-ro, reboot(2)) y se dispara # justo con SIGINT, que es lo que el kernel entrega ante Ctrl+Alt+Del con # `kernel.ctrl-alt-del = 0`. O sea: el camino existía entero y no estaba # conectado a ningún botón. Esto lo conecta. # # ⚠ ASIMETRÍA QUE NO SE PUEDE TAPAR: hay señal para REINICIAR y no la hay para # APAGAR. Por eso `poweroff` va por SysRq y **se saltea el cascade**. El camino # bueno es `BusRequest::PowerOff` por el bus de arje; cuando `arje-ctl` esté en # la imagen, el shim de poweroff debería delegarle en vez de hacer esto. # # ⚠ EL BOTÓN DEL PANEL DEL PROVEEDOR NO SE ARREGLA ACÁ, y no por falta de # ganas: en una caja musl `acpid` es el applet de **busybox**, que encuentra su # handler y **no puede ejecutarlo** (`/bin/sh: can't open './PWRF/00000080': # No file descriptors available` — EMFILE; a mano, parado en el mismo # directorio, el mismo `/bin/sh` lo corre perfecto). Es un defecto de su # `spawn()`. Hasta que haya un `acpid` de verdad, del panel sólo sirve «Reset», # que es el hipervisor. La línea de comandos sí queda arreglada, que es lo que # este script promete. # # ⚠ VAN EN /usr/local/bin A PROPÓSITO: el `secure_path` de sudo lo mira ANTES # que /usr/bin y /sbin, así que estos ganan sin pisar el `reboot` de la caja — # que sigue ahí para el día que el init sea otro. # # ⚠ ESTO ES UNA COPIA de la lógica de `tawasuyu/scripts/install-arje-reboot.sh` # (que hace lo mismo sobre una máquina VIVA). Está duplicada a propósito: al # armar la imagen el repo tawasuyu no existe, y una imagen que sólo reinicia si # alguien se acuerda de correr un script de OTRO repo es la clase de arreglo # que se pierde en la siguiente instalación. Si tocás los shims, tocá los dos. # ============================================================================= set -eu RFS=${1:-} [ -n "$RFS" ] || { echo "uso: $0 " >&2; exit 2; } [ -d "$RFS" ] || { echo "!! no existe el rootfs '$RFS'" >&2; exit 1; } install -d -m 755 "$RFS/usr/local/bin" cat > "$RFS/usr/local/bin/reboot" <<'SH' #!/bin/sh # reboot — el de verdad, para una máquina con arje-zero de PID 1. # El de la caja le manda una señal a PID 1 que arje-zero no atrapa, y el kernel # la descarta: vuelve el prompt y no pasa nada. SIGINT es la que sí atiende, y # de ahí sale su apagar_maquina(): cascade, kill(-1), sync, remount-ro, reboot(2). [ "$(id -u)" = 0 ] || { echo "reboot: hace falta root (probá: sudo reboot)" >&2; exit 1; } [ "$(ps -p 1 -o comm=)" = "arje-zero" ] || { echo "reboot: el PID 1 NO es arje-zero — este shim no aplica." >&2 echo " usá el reboot de tu init: /sbin/reboot" >&2 exit 1 } sync echo "reboot: SIGINT a PID 1 (arje-zero) — reinicio ordenado en curso" >&2 exec kill -INT 1 SH cat > "$RFS/usr/local/bin/poweroff" <<'SH' #!/bin/sh # poweroff — apaga de verdad, PERO SIN el cascade de arje: arje-zero instala # SIGINT y sólo SIGINT, así que no hay señal de apagado que mandarle. Va directo # al kernel por SysRq: 'u' = remount-ro de emergencia (evita el fsck del próximo # arranque), 'o' = apagar. Los servicios NO reciben su SIGTERM. El camino bueno # es BusRequest::PowerOff por el bus de arje, cuando arje-ctl esté en la imagen. [ "$(id -u)" = 0 ] || { echo "poweroff: hace falta root" >&2; exit 1; } [ "$(cat /proc/sys/kernel/sysrq 2>/dev/null)" = 0 ] && { echo "poweroff: kernel.sysrq está en 0 — no hay por dónde. Ponelo en 1." >&2 exit 1 } echo "poweroff: sin cascade (arje no expone señal de apagado) — sync + SysRq u,o" >&2 sync echo u > /proc/sysrq-trigger sleep 1 echo o > /proc/sysrq-trigger SH cat > "$RFS/usr/local/bin/shutdown" <<'SH' #!/bin/sh # shutdown — traduce las banderas de siempre a los dos shims que sí andan. # No implementa el retardo (`shutdown -r +5`): esta máquina no tiene un init que # lo agende, y fingir que sí es cómo se llega a «creí que se iba a apagar». # Sin argumentos NO se apaga nada: el shutdown de siempre exige un TIME, y un # shim que apague en el acto ante un `shutdown` tipeado a medias es una trampa. [ $# -gt 0 ] || { echo "uso: shutdown [-r|-h] now (el retardo no está implementado)" >&2; exit 1; } modo=poweroff for a in "$@"; do case "$a" in -r) modo=reboot ;; -h|-P|-H) modo=poweroff ;; -c) echo "shutdown: acá no hay nada agendado que cancelar." >&2; exit 0 ;; now|-*) ;; +*|[0-9]*:[0-9]*) echo "shutdown: el retardo '$a' NO está implementado — sin init que lo agende." >&2 echo " corré 'shutdown -r now' cuando de verdad quieras." >&2 exit 1 ;; esac done exec "/usr/local/bin/$modo" SH ln -sf poweroff "$RFS/usr/local/bin/halt" chmod 755 "$RFS/usr/local/bin/reboot" "$RFS/usr/local/bin/poweroff" "$RFS/usr/local/bin/shutdown" # Ctrl+Alt+Del tiene que llegarle a arje como SIGINT, no reiniciar a lo bruto, y # SysRq tiene que estar encendido o el shim de poweroff no tiene por dónde. Los # dos son el default de esta imagen, pero un default no es una garantía. install -d -m 755 "$RFS/etc/sysctl.d" cat > "$RFS/etc/sysctl.d/60-arje-botones.conf" <<'CONF' # Ctrl+Alt+Del => SIGINT a PID 1 (que es de donde arje saca su reinicio ordenado). kernel.ctrl-alt-del = 0 # SysRq encendido: es por donde apaga /usr/local/bin/poweroff, y es el escape # que siempre funciona (`sync; echo u > /proc/sysrq-trigger; echo b > ...`). kernel.sysrq = 1 CONF echo "==> botones : reboot/poweroff/halt/shutdown en /usr/local/bin + sysctl (SIGINT a PID 1)"