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…
Fase 3 — diario de mutaciones (hammer-journal + fanotify + hammer journal)
1//! `hammerd` — daemon de hammer. Dos responsabilidades:
2//! 1. Diario de mutaciones: fanotify sobre /bin,/sbin,/lib,/etc → `hammer-journal`.
3//! 2. Bus de agente: /run/agent.sock (JSON-líneas, SO_PEERCRED). Ver `docs/07-agent-bus.md`.
4//!
5//! Fase 3 implementa (1). Fase 5 añadirá (2). Si el daemon arranca sin CAP_SYS_ADMIN, el
6//! watcher falla en init y el daemon sigue corriendo sin él (warn claro). Esto es
7//! deliberado para que puedas correr `hammerd` en dev sin sudo y aún así probar la parte
8//! del bus.
9
10use std::path::PathBuf;
11
12use clap::Parser;
13
14mod watcher;
15
16#[derive(Parser)]
17#[command(name = "hammerd", version, about = "Daemon de hammer: bus de agente + diario.")]
18struct Args {
19 /// Socket del bus de agente.
20 #[arg(long, default_value = "/run/agent.sock")]
21 agent_sock: String,
22 /// FIFO de control humano del init.
23 #[arg(long, default_value = "/run/init.control")]
24 init_control: String,
25 /// Directorio del diario de mutaciones.
26 #[arg(long, default_value = "/var/lib/hammer/journal")]
27 journal: String,
28 /// Directorio raíz de overlays (para que el watcher pueda filtrar eventos bajo overlays
29 /// activos). Default coincide con `hammer try`.
30 #[arg(long, default_value = hammer_overlay::DEFAULT_STATE_ROOT)]
31 overlay_state_root: String,
32 /// Directorios extra a vigilar; si vacío, usa los defaults del FHS (SDD 05 §2).
33 #[arg(long = "watch")]
34 extra_watch: Vec<PathBuf>,
35}
36
37fn main() -> anyhow::Result<()> {
38 tracing_subscriber::fmt()
39 .with_writer(std::io::stderr)
40 .with_env_filter(
41 tracing_subscriber::EnvFilter::try_from_default_env()
42 .unwrap_or_else(|_| "info".into()),
43 )
44 .init();
45
46 let args = Args::parse();
47 tracing::info!(
48 agent_sock = %args.agent_sock,
49 init_control = %args.init_control,
50 journal = %args.journal,
51 "hammerd: arranque"
52 );
53
54 let journal = hammer_journal::Journal::open(&args.journal)?;
55 let mut dirs = watcher::default_watch_dirs();
56 dirs.extend(args.extra_watch);
57
58 let overlay_root = PathBuf::from(&args.overlay_state_root);
59 match watcher::Watcher::start(&dirs, journal, Some(overlay_root)) {
60 Ok(w) => {
61 tracing::info!("watcher fanotify activo");
62 w.run_forever()?;
63 }
64 Err(e) => {
65 tracing::warn!(
66 error = %e,
67 "watcher fanotify NO arrancó; el daemon sigue (sin diario en este run)"
68 );
69 // TODO(fase-5): aquí entra el bus de agente. De momento sólo dormimos para que
70 // el daemon no salga.
71 std::thread::park();
72 }
73 }
74 Ok(())
75}