`declarar.py` tenía su propio molde de card de arje y `formatos/arje.py` el suyo: el N×M que el
pivote existe para evitar, adentro de mi propio código. Y no quedó en teoría — CADA COPIA TENÍA UN
CAMPO MAL DE LA RAÍZ, Y NINGUNO DE LOS DOS EL MISMO:
campo declarar.py arje.py semilla REAL del producto
provides ["Spawn","Journal"] ✓ [] ✗ ["Spawn","Journal"]
supervision Restart{…} ✗ "OneShot" ✓ "OneShot"
Las consecuencias son concretas: sin `Spawn`/`Journal` las hijas no tienen quién las lance ni dónde
escribir, y una card `Virtual` con `Restart` le pide a arje que respawnee algo que nunca corrió.
Ahora hay un lector `proc` (el censo es un formato de origen, igual que systemd u OpenRC) y el
escritor `arje` es el ÚNICO que emite tarjetas; `declarar.py` queda con lo suyo, el perfil. Tres
lectores en la familia: systemd y openrc leen LO DECLARADO —que es lo que miente, `rc-status` daba
`stopped` para cinco servicios vivos— y `proc` lee LO QUE CORRE, que es donde aparecen los 15
`no-declarado`.
Dos cosas más, las dos sobre no mentir:
· **Una semilla vacía parecería un éxito.** Si ningún servicio tiene `decision = "muda"`, el lector
lo DICE y sale ≠0 en vez de emitir cero tarjetas en silencio. Mismo modo de fallo que un artefacto
vacío en el store.
· **El acta se ahogaba en su propio ruido.** Anotaba el `envp` vacío una vez POR SERVICIO: 26 líneas
idénticas que tapaban los dos hallazgos reales (`shuma-daemon` corre como `sergio`; los que no
tienen cmdline). La limitación es del lector y vale para todos ⇒ una entrada nombrando a los 26.
Un acta donde casi todo es la misma línea se deja de leer, y entonces no queda ningún acta. Y al
revés: las entradas que SÍ son por servicio ahora lo nombran (`gitea: cwd=/var/lib/gitea`).
Controles: la raíz generada coincide campo por campo con la del producto; dos corridas dan el fichero
byte a byte idéntico; 26 tarjetas con 26 ids únicos.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x
78 lines
4.6 KiB
Python
78 lines
4.6 KiB
Python
"""Lector del CENSO (lo que `/proc` dice que corre) → modelo pivote «servicio» (SDD 29).
|
|
|
|
El tercer lector de la familia, y el que no tiene equivalente en ningún init: `systemd` y `openrc`
|
|
leen **lo declarado**, que es exactamente lo que miente — en gioser `rc-status` daba `stopped` para
|
|
cinco servicios que estaban vivos, y los `no-declarado` (15 de 39) no aparecen en ninguna
|
|
declaración porque, por definición, nadie los declaró. Éste lee **lo que corre**.
|
|
|
|
Los tres se complementan y ninguno solo alcanza. Que los tres desemboquen en el mismo escritor es
|
|
justamente el punto del pivote: la tarjeta de arje se emite en UN lugar, no uno por procedencia.
|
|
|
|
── LO QUE ESTE LECTOR NO PUEDE ─────────────────────────────────────────────────────────────────
|
|
El censo **no lee `/proc/<pid>/environ`** — trae tokens y contraseñas, y un censo se guarda en un
|
|
fichero que se manda por ahí. Así que el `envp` de toda tarjeta sale VACÍO. Un servicio que dependa
|
|
de variables de entorno va a arrancar y fallar, y eso se dice por su nombre, no en una nota general.
|
|
"""
|
|
import sys, os, tomllib
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
from modelo import Servicio, Sistema # noqa: E402
|
|
from formatos import lector # noqa: E402
|
|
|
|
|
|
@lector("proc", familia="servicio")
|
|
def leer(texto, nombre=""):
|
|
sis = Sistema(formato_origen="proc")
|
|
acta = sis.acta
|
|
censo = tomllib.loads(texto)
|
|
servicios = censo.get("servicio", [])
|
|
if not servicios:
|
|
acta.anota(0, "SIN-TRADUCIR", nombre or "censo",
|
|
"el censo no tiene ningún `[[servicio]]`: ¿es el fichero correcto?")
|
|
return sis
|
|
|
|
decididos = [s for s in servicios if s.get("decision") == "muda"]
|
|
if not decididos:
|
|
# Salir con cero tarjetas y código 0 sería una semilla VACÍA que parece un éxito. Es el mismo
|
|
# modo de fallo que un artefacto vacío en el store: un ausente falla ruidosamente, un vacío
|
|
# llega hasta el final diciendo que todo fue bien.
|
|
acta.anota(0, "SIN-TRADUCIR", nombre or "censo",
|
|
f"ninguno de los {len(servicios)} servicios tiene `decision = \"muda\"`. La "
|
|
"semilla saldría VACÍA y parecería un éxito: decidí primero (`planear.py --decide`)")
|
|
return sis
|
|
|
|
for s in decididos:
|
|
srv = Servicio(nombre=s["name"], tipo="simple")
|
|
srv.cmd = (s.get("cmdline") or "").strip()
|
|
srv.cwd = (s.get("cwd") or "").strip()
|
|
srv.usuario = (s.get("usuario") or "").strip()
|
|
if srv.usuario:
|
|
srv.lineas["usuario"] = 0
|
|
# Está corriendo y se decidió mudarlo ⇒ se quiere arriba. Es una decisión, y se anota como
|
|
# tal: el censo no dice si estaba en el arranque, dice que está VIVO ahora.
|
|
srv.arranque_auto = True
|
|
srv.reinicio = "siempre"
|
|
if not srv.cmd:
|
|
acta.anota(0, "SIN-TRADUCIR", s["name"],
|
|
"no se pudo leer su línea de comando en el origen: NO se emite tarjeta. Una "
|
|
"tarjeta que no arranca es peor que una ausente — la ausente falla ruidosamente "
|
|
"al primer arranque, la rota deja el servicio caído sin que nada avise")
|
|
continue
|
|
if s.get("ports"):
|
|
acta.anota(0, "traducido", f"{s['name']} sirve {', '.join(str(p) for p in s['ports'])}")
|
|
sis.servicios.append(srv)
|
|
|
|
# ⚠ UNA entrada, no una por servicio. La limitación es del LECTOR y vale igual para todos, así
|
|
# que repetirla 26 veces no informa 26 veces: tapa los hallazgos que sí son de un servicio
|
|
# concreto (el que corre como `sergio`, el que no tiene cmdline). Un acta en la que casi todo es
|
|
# la misma línea se deja de leer, y entonces no queda ningún acta.
|
|
if sis.servicios:
|
|
acta.anota(0, "decision", f"supervisión de las {len(sis.servicios)} tarjetas",
|
|
"el censo dice que CORREN, no si el origen las levantaba al arrancar; se declaran "
|
|
"con reinicio automático porque se decidió mudarlas. Sacá del `genesis` las que no "
|
|
"quieras al boot")
|
|
acta.anota(0, "SIN-TRADUCIR", f"envp VACÍO en las {len(sis.servicios)} tarjetas",
|
|
"el censo NO lee `/proc/<pid>/environ` (trae tokens), así que ninguna tarjeta "
|
|
"lleva entorno. El que dependa de variables arranca y falla: " +
|
|
" ".join(x.nombre for x in sis.servicios))
|
|
return sis
|