login: pass shell console fds explicitly as Stdio (relibc inheritance drops login-shell stdio; getty does the same for login)

This commit is contained in:
2026-07-18 18:13:44 +09:00
parent 1677bdd2c3
commit 02a86400cc
+30
View File
@@ -132,6 +132,36 @@ pub fn spawn_shell<T: Default>(user: &User<T>) -> IoResult<i32> {
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),