#!/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: hay señal para REINICIAR y no la hay para APAGAR — arje-zero # atrapa SIGINT y sólo SIGINT. ✅ **Pero desde el 2026-10-22 ya no se paga # entera**: `arjectl` existe sellado y expone `poweroff|reboot|suspend| # hibernate`, que ES el `BusRequest::PowerOff` que esta nota pedía. El shim lo # INTENTA primero —y ahí los servicios sí reciben su SIGTERM— y cae a SysRq # sólo si no está o si el bus no contesta. La caída hace falta: `arjectl` viaja # en `servidor` y `servidor-minimo` y NO en los cinco escritorios. # # ⚠ 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 — DOS caminos, y el primero es el ordenado. # # ✅ **Deuda cerrada el 2026-10-22.** Este shim decía: «El camino bueno es # BusRequest::PowerOff por el bus de arje, cuando arje-ctl esté en la imagen». # Ya está: `arjectl` existe sellado y expone `poweroff | reboot | suspend | # hibernate`, que es exactamente ese BusRequest. Así que ahora se INTENTA eso # primero y los servicios SÍ reciben su SIGTERM por el cascade de arje. # # ⚠ Y se cae a SysRq a propósito, no por desconfianza: `arjectl` viaja sólo en # `servidor` y `servidor-minimo` (medido en targets.toml), así que en los cinco # escritorios no está. Un shim que dependiera de él fallaría justo donde más se # nota — alguien apagando su portátil. El respaldo es el camino de antes: # 'u' = remount-ro de emergencia (evita el fsck del próximo arranque), 'o' = # apagar, sin cascade. [ "$(id -u)" = 0 ] || { echo "poweroff: hace falta root" >&2; exit 1; } # 1 · el camino ordenado: el bus de arje if command -v arjectl >/dev/null 2>&1; then if arjectl poweroff 2>/dev/null; then # arjectl volvió sin error: arje está bajando los entes. No seguimos al # respaldo — hacerlo cortaría el cascade que acabamos de pedir. exit 0 fi echo "poweroff: arjectl no pudo (¿bus caído?) — voy por SysRq" >&2 fi # 2 · el respaldo: directo al kernel, sin cascade [ "$(cat /proc/sys/kernel/sysrq 2>/dev/null)" = 0 ] && { echo "poweroff: kernel.sysrq está en 0 y arjectl no sirvió — no hay por dónde." >&2 exit 1 } echo "poweroff: SIN cascade (los servicios no reciben SIGTERM) — 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)"