From 123649e2009d0cc956fb023103cfc2e75082c551 Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 18 Jul 2026 08:08:20 +0900 Subject: [PATCH] login: add spawn_shell diagnostics to pinpoint shell-spawn ENOENT Probe the shell binary and home directory in the (possibly restricted) login namespace before spawning, and print the exact spawn error with raw_os_error. This isolates whether a failed login shell spawn is due to a missing binary, an unreachable cwd/home, or an exec-time error. --- src/lib.rs | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 2789547063..3f662a35ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -72,9 +72,42 @@ impl AllGroupsExt for AllGroups { /// spawn_shell(user).unwrap(); /// ``` pub fn spawn_shell(user: &User) -> IoResult { + // DIAGNOSTIC: probe the shell binary and home directory in the (possibly + // restricted) login namespace before spawning, so a spawn failure can be + // pinpointed (missing binary vs. missing/unreachable cwd vs. exec error). + match std::fs::metadata(&user.shell) { + Ok(m) => eprintln!( + "login-diag: shell '{}' present (len={}, mode={:o})", + user.shell, + m.len(), + m.permissions().readonly() as u32 + ), + Err(e) => eprintln!("login-diag: shell '{}' NOT accessible: {}", user.shell, e), + } + match std::fs::metadata(&user.home) { + Ok(_) => eprintln!("login-diag: home '{}' present", user.home), + Err(e) => eprintln!("login-diag: home '{}' NOT accessible: {}", user.home, e), + } + match std::fs::File::open(&user.shell) { + Ok(_) => eprintln!("login-diag: shell '{}' opened OK", user.shell), + Err(e) => eprintln!("login-diag: shell '{}' open FAILED: {}", user.shell, e), + } + let mut command = user.shell_cmd(); - let mut child = command.spawn()?; + let mut child = match command.spawn() { + Ok(child) => child, + Err(e) => { + eprintln!( + "login-diag: spawn of shell '{}' (cwd '{}') FAILED: {} (raw_os_error={:?})", + user.shell, + user.home, + e, + e.raw_os_error() + ); + return Err(e); + } + }; match child.wait()?.code() { Some(code) => Ok(code), None => Ok(1),