gioser

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 1471Shell 173Rust 105Markdown 943545 Definiciones

⎇ main scripts/servidor/arje-botones.sh Shell · 160 líneas · 7.9 KB Historia crudo

arje-botones: el poweroff ya baja los servicios — deuda cerrada 2d97880a3 · Sergio ·

1#!/bin/sh
2# =============================================================================
3#  arje-botones.sh — que `reboot`, `poweroff`, `halt` y `shutdown` hagan algo
4#  en una imagen cuyo PID 1 es `arje-zero`. Se corre SOBRE UN ROOTFS, al armar
5#  la imagen, no sobre la máquina viva.
6#
7#  Uso:  ./scripts/servidor/arje-botones.sh <rootfs>
8#
9#  ── POR QUÉ EXISTE (2026-09-25) ─────────────────────────────────────────────
10#
11#  `servidor-image.sh` §3 ya se ocupa de que `/sbin/init` sea `arje-zero`. Esto
12#  es el paso siguiente y sin él la imagen queda con un defecto silencioso: con
13#  arje-zero de PID 1, **`reboot` no reinicia y no dice nada**.
14#
15#  La causa, medida en `takana` el 2026-09-25 (y antes en `momento`, 26-ago):
16#  el `reboot` de la caja —sea el de OpenRC, sea el applet de busybox— termina
17#  mandándole una señal a PID 1. arje-zero atrapa **SIGINT y sólo SIGINT**
18#  (`/proc/1/status` → `SigCgt: 0000000000000442`; los otros dos bits son el
19#  SIGBUS/SIGSEGV que instala el runtime de Rust). Y el kernel **descarta** las
20#  señales sin handler dirigidas a PID 1 — no es que falle: no pasa nada, sin
21#  error y sin mensaje. Es el peor modo de fallar, y costó una tarde cada vez.
22#
23#  Del otro lado, `arje-zero::apagar_maquina()` está implementado de verdad
24#  (cascade topológico, kill(-1), sync, remount-ro, reboot(2)) y se dispara
25#  justo con SIGINT, que es lo que el kernel entrega ante Ctrl+Alt+Del con
26#  `kernel.ctrl-alt-del = 0`. O sea: el camino existía entero y no estaba
27#  conectado a ningún botón. Esto lo conecta.
28#
29#  ⚠ ASIMETRÍA: hay señal para REINICIAR y no la hay para APAGAR — arje-zero
30#  atrapa SIGINT y sólo SIGINT. ✅ **Pero desde el 2026-10-22 ya no se paga
31#  entera**: `arjectl` existe sellado y expone `poweroff|reboot|suspend|
32#  hibernate`, que ES el `BusRequest::PowerOff` que esta nota pedía. El shim lo
33#  INTENTA primero —y ahí los servicios sí reciben su SIGTERM— y cae a SysRq
34#  sólo si no está o si el bus no contesta. La caída hace falta: `arjectl` viaja
35#  en `servidor` y `servidor-minimo` y NO en los cinco escritorios.
36#
37#  ⚠ EL BOTÓN DEL PANEL DEL PROVEEDOR NO SE ARREGLA ACÁ, y no por falta de
38#  ganas: en una caja musl `acpid` es el applet de **busybox**, que encuentra su
39#  handler y **no puede ejecutarlo** (`/bin/sh: can't open './PWRF/00000080':
40#  No file descriptors available` — EMFILE; a mano, parado en el mismo
41#  directorio, el mismo `/bin/sh` lo corre perfecto). Es un defecto de su
42#  `spawn()`. Hasta que haya un `acpid` de verdad, del panel sólo sirve «Reset»,
43#  que es el hipervisor. La línea de comandos sí queda arreglada, que es lo que
44#  este script promete.
45#
46#  ⚠ VAN EN /usr/local/bin A PROPÓSITO: el `secure_path` de sudo lo mira ANTES
47#  que /usr/bin y /sbin, así que estos ganan sin pisar el `reboot` de la caja —
48#  que sigue ahí para el día que el init sea otro.
49#
50#  ⚠ ESTO ES UNA COPIA de la lógica de `tawasuyu/scripts/install-arje-reboot.sh`
51#  (que hace lo mismo sobre una máquina VIVA). Está duplicada a propósito: al
52#  armar la imagen el repo tawasuyu no existe, y una imagen que sólo reinicia si
53#  alguien se acuerda de correr un script de OTRO repo es la clase de arreglo
54#  que se pierde en la siguiente instalación. Si tocás los shims, tocá los dos.
55# =============================================================================
56set -eu
57
58RFS=${1:-}
59[ -n "$RFS" ] || { echo "uso: $0 <rootfs>" >&2; exit 2; }
60[ -d "$RFS" ] || { echo "!! no existe el rootfs '$RFS'" >&2; exit 1; }
61
62install -d -m 755 "$RFS/usr/local/bin"
63
64cat > "$RFS/usr/local/bin/reboot" <<'SH'
65#!/bin/sh
66# reboot — el de verdad, para una máquina con arje-zero de PID 1.
67# El de la caja le manda una señal a PID 1 que arje-zero no atrapa, y el kernel
68# la descarta: vuelve el prompt y no pasa nada. SIGINT es la que sí atiende, y
69# de ahí sale su apagar_maquina(): cascade, kill(-1), sync, remount-ro, reboot(2).
70[ "$(id -u)" = 0 ] || { echo "reboot: hace falta root (probá: sudo reboot)" >&2; exit 1; }
71[ "$(ps -p 1 -o comm=)" = "arje-zero" ] || {
72  echo "reboot: el PID 1 NO es arje-zero — este shim no aplica." >&2
73  echo "        usá el reboot de tu init: /sbin/reboot" >&2
74  exit 1
75}
76sync
77echo "reboot: SIGINT a PID 1 (arje-zero) — reinicio ordenado en curso" >&2
78exec kill -INT 1
79SH
80
81cat > "$RFS/usr/local/bin/poweroff" <<'SH'
82#!/bin/sh
83# poweroff — DOS caminos, y el primero es el ordenado.
84#
85# ✅ **Deuda cerrada el 2026-10-22.** Este shim decía: «El camino bueno es
86# BusRequest::PowerOff por el bus de arje, cuando arje-ctl esté en la imagen».
87# Ya está: `arjectl` existe sellado y expone `poweroff | reboot | suspend |
88# hibernate`, que es exactamente ese BusRequest. Así que ahora se INTENTA eso
89# primero y los servicios SÍ reciben su SIGTERM por el cascade de arje.
90#
91# ⚠ Y se cae a SysRq a propósito, no por desconfianza: `arjectl` viaja sólo en
92# `servidor` y `servidor-minimo` (medido en targets.toml), así que en los cinco
93# escritorios no está. Un shim que dependiera de él fallaría justo donde más se
94# nota — alguien apagando su portátil. El respaldo es el camino de antes:
95# 'u' = remount-ro de emergencia (evita el fsck del próximo arranque), 'o' =
96# apagar, sin cascade.
97[ "$(id -u)" = 0 ] || { echo "poweroff: hace falta root" >&2; exit 1; }
98
99# 1 · el camino ordenado: el bus de arje
100if command -v arjectl >/dev/null 2>&1; then
101  if arjectl poweroff 2>/dev/null; then
102    # arjectl volvió sin error: arje está bajando los entes. No seguimos al
103    # respaldo — hacerlo cortaría el cascade que acabamos de pedir.
104    exit 0
105  fi
106  echo "poweroff: arjectl no pudo (¿bus caído?) — voy por SysRq" >&2
107fi
108
109# 2 · el respaldo: directo al kernel, sin cascade
110[ "$(cat /proc/sys/kernel/sysrq 2>/dev/null)" = 0 ] && {
111  echo "poweroff: kernel.sysrq está en 0 y arjectl no sirvió — no hay por dónde." >&2
112  exit 1
113}
114echo "poweroff: SIN cascade (los servicios no reciben SIGTERM) — sync + SysRq u,o" >&2
115sync
116echo u > /proc/sysrq-trigger
117sleep 1
118echo o > /proc/sysrq-trigger
119SH
120
121cat > "$RFS/usr/local/bin/shutdown" <<'SH'
122#!/bin/sh
123# shutdown — traduce las banderas de siempre a los dos shims que sí andan.
124# No implementa el retardo (`shutdown -r +5`): esta máquina no tiene un init que
125# lo agende, y fingir que sí es cómo se llega a «creí que se iba a apagar».
126# Sin argumentos NO se apaga nada: el shutdown de siempre exige un TIME, y un
127# shim que apague en el acto ante un `shutdown` tipeado a medias es una trampa.
128[ $# -gt 0 ] || { echo "uso: shutdown [-r|-h] now   (el retardo no está implementado)" >&2; exit 1; }
129modo=poweroff
130for a in "$@"; do
131  case "$a" in
132    -r) modo=reboot ;;
133    -h|-P|-H) modo=poweroff ;;
134    -c) echo "shutdown: acá no hay nada agendado que cancelar." >&2; exit 0 ;;
135    now|-*) ;;
136    +*|[0-9]*:[0-9]*)
137      echo "shutdown: el retardo '$a' NO está implementado — sin init que lo agende." >&2
138      echo "          corré 'shutdown -r now' cuando de verdad quieras." >&2
139      exit 1 ;;
140  esac
141done
142exec "/usr/local/bin/$modo"
143SH
144
145ln -sf poweroff "$RFS/usr/local/bin/halt"
146chmod 755 "$RFS/usr/local/bin/reboot" "$RFS/usr/local/bin/poweroff" "$RFS/usr/local/bin/shutdown"
147
148# Ctrl+Alt+Del tiene que llegarle a arje como SIGINT, no reiniciar a lo bruto, y
149# SysRq tiene que estar encendido o el shim de poweroff no tiene por dónde. Los
150# dos son el default de esta imagen, pero un default no es una garantía.
151install -d -m 755 "$RFS/etc/sysctl.d"
152cat > "$RFS/etc/sysctl.d/60-arje-botones.conf" <<'CONF'
153# Ctrl+Alt+Del => SIGINT a PID 1 (que es de donde arje saca su reinicio ordenado).
154kernel.ctrl-alt-del = 0
155# SysRq encendido: es por donde apaga /usr/local/bin/poweroff, y es el escape
156# que siempre funciona (`sync; echo u > /proc/sysrq-trigger; echo b > ...`).
157kernel.sysrq = 1
158CONF
159
160echo "==> botones : reboot/poweroff/halt/shutdown en /usr/local/bin + sysctl (SIGINT a PID 1)"