From 1677bdd2c333c0f16ee74ad6f7c1b2cfa59a2dfe Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 18 Jul 2026 17:56:46 +0900 Subject: [PATCH] login: clear FD_CLOEXEC on stdio before spawning shell (+diag) so the login shell inherits console I/O --- src/lib.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index bef973455b..32c3fc7b34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,6 +86,26 @@ pub fn spawn_shell(user: &User) -> IoResult { } } + // The relibc Redox spawn closes, in the child, every inherited fd that has + // FD_CLOEXEC set in the parent's filetable (see relibc + // platform/redox/mod.rs, the fds_to_close loop). A login shell MUST inherit + // its console stdio, but the pty-slave fds can arrive here still carrying + // FD_CLOEXEC (login itself keeps using them fine, since CLOEXEC only affects + // exec, not use) — which then silently drops the shell's stdin/stdout/stderr + // and leaves an interactive shell with no console. Explicitly clear + // FD_CLOEXEC on 0/1/2 so they survive the exec into the shell. + const F_GETFD: usize = 1; + const F_SETFD: usize = 2; + const FD_CLOEXEC: usize = 1; + for fd in 0..=2 { + let fl = fcntl(fd, F_GETFL, 0); + let fd_fl = fcntl(fd, F_GETFD, 0); + eprintln!("login-diag: fd{fd} F_GETFL={fl:?} F_GETFD={fd_fl:?}"); + if let Ok(flags) = fd_fl { + let _ = fcntl(fd, F_SETFD, flags & !FD_CLOEXEC); + } + } + // The login name is read with the `liner` line editor, which switches the // console pty into raw mode (ICANON/ECHO cleared) and does not restore it. // In raw mode the pty's line discipline never flushes a completed line to