From c715ee2dee341c730cd2206797b8bec989f7ac5c Mon Sep 17 00:00:00 2001 From: sergio Date: Sat, 23 May 2026 01:16:26 +0000 Subject: [PATCH] fix(init): la salida de arje-zero ahora se ve en VGA Y serial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Síntoma: el screenshot del usuario en la VPS Hetzner mostraba systemd booteando y se quedaba congelado en el último printk del kernel justo antes del switch-root. arje-zero arrancaba bien pero su salida iba al serial invisible. Causa: el cmdline traía `console=tty1 console=ttyS0,115200` — y el kernel hace que `/dev/console` apunte al ÚLTIMO `console=`, así toda la salida de stdout/stderr de arje-zero (tracing + banner de la rescue shell) caía en ttyS0 (serial), no en la VGA que muestra noVNC. Dos arreglos: - Orden de consolas invertido en el menuentry → `/dev/console` = tty1 (lo que efectivamente se ve en la consola web del proveedor). - arje-zero también escribe a `/dev/kmsg` (ring buffer del kernel), que el kernel hace eco a TODAS las consolas registradas — el mecanismo que usa systemd para que sus mensajes salgan tanto en VGA como en serial. Defense in depth: el banner de rescue y un eco temprano «despierta como PID 1» aparecen sí o sí en cualquier consola. Co-Authored-By: Claude Opus 4.7 --- crates/init/arje-zero/src/main.rs | 28 +++++++++++++++++++++++++++- scripts/install-arje-as-init.sh | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/crates/init/arje-zero/src/main.rs b/crates/init/arje-zero/src/main.rs index 18c3d3e..46977ea 100644 --- a/crates/init/arje-zero/src/main.rs +++ b/crates/init/arje-zero/src/main.rs @@ -87,6 +87,10 @@ fn main() -> anyhow::Result<()> { } info!("ente-zero despierta como PID 1"); + // Eco temprano al ring buffer del kernel: garantiza que el «estoy + // vivo» aparezca en TODAS las consolas (VGA + serial), no sólo en + // el `/dev/console` apuntado por el último `console=` del cmdline. + write_to_kmsg("despierta como PID 1"); // Doctrina dura: PID 1 NUNCA puede salir — el kernel haría panic // ("Attempted to kill init") y, con `panic=N` en el cmdline, la // máquina cae en un reboot-loop. Por eso cualquier fallo de arranque @@ -184,15 +188,37 @@ fn spawn_console_shell() -> std::io::Result { .status() } -/// Escribe un mensaje directo a `/dev/console`, con stderr de respaldo. +/// Escribe un mensaje directo a `/dev/console`, **a `/dev/kmsg`** +/// (ring buffer del kernel, que se hace eco a todas las consolas +/// registradas), y a stderr de respaldo. Así el banner se ve en VGA y +/// serial sin importar cuál sea el último `console=` del cmdline. fn write_to_console(msg: &str) { use std::io::Write; if let Ok(mut f) = std::fs::OpenOptions::new().write(true).open("/dev/console") { let _ = f.write_all(msg.as_bytes()); } + write_to_kmsg(msg); eprint!("{msg}"); } +/// Escribe un mensaje al `/dev/kmsg` del kernel — éste lo replica a +/// todas las consolas registradas (`console=` del cmdline). Es el +/// canal que usa systemd para que sus avisos se vean tanto en la VGA +/// como en el serial. +fn write_to_kmsg(msg: &str) { + use std::io::Write; + let Ok(mut f) = std::fs::OpenOptions::new().write(true).open("/dev/kmsg") else { + return; + }; + // `<1>` = ALERT, prioridad alta — aparece incluso con loglevel bajo. + for line in msg.lines() { + let trimmed = line.trim_end(); + if !trimmed.is_empty() { + let _ = writeln!(f, "<1>arje-zero: {trimmed}"); + } + } +} + async fn primordial_loop( seed_card: arje_card::EntityCard, dev_mode: bool, diff --git a/scripts/install-arje-as-init.sh b/scripts/install-arje-as-init.sh index 5239d46..a913797 100755 --- a/scripts/install-arje-as-init.sh +++ b/scripts/install-arje-as-init.sh @@ -156,7 +156,7 @@ menuentry "arje (init=/sbin/arje-zero) — kernel $KVER" { insmod gzio insmod part_msdos insmod ext2 - linux $VMLINUZ $ROOT_OPT rw init=/sbin/arje-zero console=tty1 console=ttyS0,115200 panic=10 + linux $VMLINUZ $ROOT_OPT rw init=/sbin/arje-zero console=ttyS0,115200 console=tty1 panic=10 initrd $INITRD } # END ARJE-MENUENTRY