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…
⎇ be881ad0996aee773686a36ce014df0d648de2ed
crates/hammer-recover/src/main.rs
History
Who touched what
raw
auto-recover al arranque: crate hammer-recover + hook /sbin/init (E4 #4b)
1//! `hammer-recover` — auto-recuperación de upgrades al **arranque** (E4 / refinamiento #4b).
2//!
3//! El producto NO lleva el CLI `hammer` completo; este mini-binario (estático musl) es lo único que el
4//! sistema instalado necesita para auto-sanar un upgrade interrumpido. El wrapper `/sbin/init` lo corre
5//! **tras montar `/store` y `/var/lib/hammer`** y **antes** de incarnar arje-zero:
6//!
7//! - Sin intento pendiente ⇒ no-op (el caso normal).
8//! - Con un `pending.json` (un `hammer upgrade apply` cortado por un apagón/reinicio): intenta
9//! **completar** (roll-forward, re-ejecuta el plan idempotente + commitea); si no puede (p.ej. el árbol
10//! ya no está en `/store`), **deshace** (roll-back) para dejar el FHS consistente. En cualquier caso el
11//! sistema arranca en un estado coherente, no a medias.
12//!
13//! Encaja con el modelo de generaciones **in-place** (el FHS es la generación viva): no hay menú de
14//! generaciones tipo NixOS que seleccionar, sino una reparación determinista del estado a medias.
15//!
16//! Paths del producto (overridables por entorno para tests):
17//! `HAMMER_RECOVER_ROOT=/` `HAMMER_RECOVER_STORE=/store`
18//! `HAMMER_RECOVER_STATE=/var/lib/hammer/upgrades` `HAMMER_RECOVER_JOURNAL=/var/lib/hammer/journal`
19//! Flag: `--rollback` fuerza deshacer en vez de completar.
20//!
21//! NUNCA sale con código ≠0 por un fallo de recuperación: es un hook de arranque, no debe abortar el
22//! boot. Reporta por consola y deja que arje-zero siga (con el estado más consistente que logró).
23
24use std::path::PathBuf;
25
26fn env_path(key: &str, default: &str) -> PathBuf {
27 std::env::var(key).map(PathBuf::from).unwrap_or_else(|_| PathBuf::from(default))
28}
29
30fn main() {
31 let force_rollback = std::env::args().skip(1).any(|a| a == "--rollback");
32 let root = env_path("HAMMER_RECOVER_ROOT", "/");
33 let store = env_path("HAMMER_RECOVER_STORE", "/store");
34 let state = env_path("HAMMER_RECOVER_STATE", "/var/lib/hammer/upgrades");
35 let journal_dir = env_path("HAMMER_RECOVER_JOURNAL", "/var/lib/hammer/journal");
36
37 match hammer_upgrade::pending(&state) {
38 Ok(None) => {
39 println!("hammer-recover: sin upgrade interrumpido");
40 return;
41 }
42 Ok(Some(p)) => {
43 println!("hammer-recover: upgrade interrumpido (generación {}) — recuperando", p.id);
44 }
45 Err(e) => {
46 eprintln!("hammer-recover: no pude leer el estado de upgrades: {e}");
47 return;
48 }
49 }
50
51 let journal = hammer_journal::Journal::open(&journal_dir).ok();
52 let j = journal.as_ref();
53
54 if force_rollback {
55 match hammer_upgrade::recover(&store, &root, &state, j, true) {
56 Ok(r) => println!("hammer-recover: DESHECHO (generación {} descartada)", r.generation),
57 Err(e) => eprintln!("hammer-recover: rollback falló: {e}"),
58 }
59 return;
60 }
61
62 // Optimista: completar el upgrade. Si el roll-forward falla, deshacer ⇒ FHS consistente igual.
63 match hammer_upgrade::recover(&store, &root, &state, j, false) {
64 Ok(r) => println!("hammer-recover: COMPLETADO (generación {})", r.generation),
65 Err(e) => {
66 eprintln!("hammer-recover: roll-forward falló ({e}); deshaciendo para dejar el FHS consistente");
67 match hammer_upgrade::recover(&store, &root, &state, j, true) {
68 Ok(r) => println!("hammer-recover: DESHECHO tras fallo (generación {})", r.generation),
69 Err(e2) => eprintln!("hammer-recover: rollback de respaldo también falló: {e2}"),
70 }
71 }
72 }
73}