From e54484e553bbb202502cae088daff1294e3d98ea Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Sat, 25 Jul 2026 17:51:03 +0900 Subject: [PATCH] init: pin daemon stdio to boot-log VT1 so the login prompt lands last MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Daemon stdio (fd 0/1/2) is inherited from bootstrap pointing at /scheme/debug — the kernel console — which fbcond renders onto the *active* VT. Once 29_activate_console switches the active VT to 2 for the getty login, any daemon that logs afterwards paints on top of the login banner/prompt: late async driver attach (i2c-hidd, evdevd, dw-acpi-i2cd, all via common::setup_logging -> stderr) and smolnetd's bounded NIC wait. So the banner ended up "near the end" but never last. Re-enable switch_stdio, redirecting daemon stdio to a fixed boot-log VT (/scheme/fbcon/1) right after the /usr switchroot, before the /usr units run. VT1 is the active console during boot (so the full boot stays visible there), fbcond still mirrors it to serial, and VT2 is left clean for a prompt that lands last. This is NOT the old /scheme/log redirect that went dark: that relied on fbbootlogd surfacing /scheme/log to the framebuffer (racy). A real VT renders directly. Best-effort: if fbcon/1 can't be opened, stdio stays on the kernel console exactly as before. Co-Authored-By: Claude Opus 4.8 (1M context) --- init/src/main.rs | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/init/src/main.rs b/init/src/main.rs index 275275e64b..f660673d0f 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -130,16 +130,6 @@ fn main() { scheduler .schedule_start_and_report_errors(&mut unit_store, UnitId("00_logd.service".to_owned())); scheduler.step(&mut unit_store, &mut init_config); - // NOTE: init/daemon stdio is deliberately kept on the kernel console - // rather than being redirected into /scheme/log. Redirecting made the boot - // "go dark" after logd on text/recovery ISOs (bare/mini): the graphical - // bootlog (fbbootlogd, VT1) does not reliably surface /scheme/log to the - // framebuffer, and logd's console mirror is racy, so the console showed - // nothing between logd and the login VT — indistinguishable from a hang. - // Keeping stdio on the console makes the full boot observable on - // `-serial` and the framebuffer. (A quiet/splash mode for the graphical - // full ISO can re-enable the redirect once fbbootlogd rendering is fixed.) - let _ = switch_stdio; let runtime_target = UnitId("00_runtime.target".to_owned()); scheduler.schedule_start_and_report_errors(&mut unit_store, runtime_target.clone()); @@ -155,6 +145,29 @@ fn main() { Path::new("/usr"), Path::new("/etc"), ); + + // Pin init/daemon stdio to the dedicated boot-log VT (fbcon/1) before the + // /usr units run. Rationale: stdio otherwise stays on the kernel console + // (/scheme/debug), which fbcond renders onto the *active* VT. Once the + // `29_activate_console` unit switches the active VT to 2 for the getty + // login, any daemon that logs afterwards — late async driver attach + // (i2c-hidd, evdevd, dw-acpi-i2cd) or smolnetd's bounded NIC wait — paints + // its output on top of the login banner/prompt, so the prompt is never the + // last thing on screen. Pinning daemon output to VT1 (a fixed VT, not the + // active one) keeps the boot log fully visible — VT1 is the active console + // during boot, and fbcond still mirrors it to the serial line — while + // leaving VT2 clean for a login prompt that lands last. + // + // This is deliberately NOT the old `/scheme/log` redirect that made boot + // "go dark": that depended on fbbootlogd surfacing /scheme/log to the + // framebuffer (racy/unreliable). A real VT renders directly, so full-boot + // observability is preserved. Best-effort: if fbcon/1 cannot be opened + // (e.g. fbcond absent on a headless/serial-only boot), stdio stays on the + // kernel console and the boot remains observable exactly as before. + if let Err(err) = switch_stdio("/scheme/fbcon/1") { + eprintln!("INIT: keeping stdio on kernel console (fbcon/1 unavailable: {err})"); + } + { // FIXME introduce multi-user.target unit and replace the config dir iteration // scheduler.schedule_start_and_report_errors(&mut unit_store, UnitId("multi-user.target".to_owned()));