From 02a86400ccc29ed23bb0c01dc24fe789b4fac392 Mon Sep 17 00:00:00 2001 From: vasilito Date: Sat, 18 Jul 2026 18:13:44 +0900 Subject: [PATCH] login: pass shell console fds explicitly as Stdio (relibc inheritance drops login-shell stdio; getty does the same for login) --- src/lib.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 32c3fc7b34..6183520f53 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -132,6 +132,36 @@ pub fn spawn_shell(user: &User) -> IoResult { let mut command = user.shell_cmd(); + // The relibc Redox spawn does not reliably carry inherited stdio (fds 0/1/2) + // to the child through filetable inheritance for a login shell: the fds + // arrive disconnected, so the shell has no console, reads EOF immediately, + // exits, and getty re-spawns login in a tight loop. getty itself sidesteps + // this by handing `login` its console fds *explicitly* as Stdio (see + // getty.rs), which the spawn lowers to dup2 file-actions that actually + // connect the child's descriptors. Do the same for the shell: duplicate the + // current console fds and pass them explicitly. Stdio takes ownership of the + // dups and closes them after the spawn; login keeps its own 0/1/2. + #[cfg(target_os = "redox")] + unsafe { + use std::os::fd::{FromRawFd, RawFd}; + use std::process::Stdio; + let din = libc::dup(0); + let dout = libc::dup(1); + let derr = libc::dup(2); + if din >= 0 && dout >= 0 && derr >= 0 { + command + .stdin(Stdio::from_raw_fd(din as RawFd)) + .stdout(Stdio::from_raw_fd(dout as RawFd)) + .stderr(Stdio::from_raw_fd(derr as RawFd)); + } else { + for d in [din, dout, derr] { + if d >= 0 { + libc::close(d); + } + } + } + } + let mut child = command.spawn()?; match child.wait()?.code() { Some(code) => Ok(code),