login: restore blocking mode on console fds before spawning the shell

The login name is read via the liner line editor, which puts the console
fd into non-blocking (and raw) mode and does not restore it. The shell
inherits fd 0; an interactive shell doing blocking line reads on a
non-blocking pty slave never receives forwarded input (reads return
empty), so the shell can never run a command. Clear O_NONBLOCK on
stdin/stdout/stderr in spawn_shell before spawning. Restores the login
namespace restriction (skipping it did not affect the symptom).
This commit is contained in:
2026-07-18 13:05:01 +09:00
parent c935e2689e
commit e440bf86c1
2 changed files with 28 additions and 11 deletions
+12 -9
View File
@@ -176,16 +176,19 @@ pub fn main() {
stdout.flush().r#try(&mut stderr);
}
// DIAGNOSTIC: temporarily skip the login namespace
// restriction (apply_login_schemes). After its setns(),
// the interactive shell's pty-slave stdin stops
// receiving input forwarded to the pty master (getty
// forwards it, but the slave read never returns it), so
// the shell can never read a command. Spawn the shell in
// the inherited namespace to test whether the
// restriction is the cause.
let _ = apply_login_schemes; // keep import used
let before_ns_fd =
apply_login_schemes(user, &DEFAULT_SCHEMES).unwrap_or_exit(1);
let _ = syscall::fcntl(
before_ns_fd.raw(),
syscall::F_SETFD,
syscall::O_CLOEXEC,
);
spawn_shell(user).unwrap_or_exit(1);
let _ = syscall::fcntl(before_ns_fd.raw(), syscall::F_SETFD, 0);
let _ = libredox::call::close(
libredox::call::setns(before_ns_fd.into_raw()).unwrap_or_exit(1),
);
break;
}
+16 -2
View File
@@ -18,9 +18,9 @@
use std::io::Result as IoResult;
use libredox::call::{fchown, open};
use libredox::call::{fchown, fcntl, open};
use libredox::error::Result as SysResult;
use libredox::flag::{O_CLOEXEC, O_CREAT, O_DIRECTORY};
use libredox::flag::{O_CLOEXEC, O_CREAT, O_DIRECTORY, O_NONBLOCK};
use redox_users::{All, AllGroups, Error, Result, User, auth};
const DEFAULT_MODE: u16 = 0o700;
@@ -72,6 +72,20 @@ impl AllGroupsExt for AllGroups {
/// spawn_shell(user).unwrap();
/// ```
pub fn spawn_shell<T: Default>(user: &User<T>) -> IoResult<i32> {
// The login name is read by a line editor (liner) that puts the console
// fd into non-blocking (and raw) mode and does not always restore it. The
// shell inherits fd 0, and an interactive shell that does blocking line
// reads on a non-blocking pty slave never receives forwarded input (the
// read returns empty). Restore blocking mode on stdin/stdout/stderr before
// spawning the shell.
const F_GETFL: usize = 3;
const F_SETFL: usize = 4;
for fd in 0..=2 {
if let Ok(flags) = fcntl(fd, F_GETFL, 0) {
let _ = fcntl(fd, F_SETFL, flags & !(O_NONBLOCK as usize));
}
}
let mut command = user.shell_cmd();
let mut child = command.spawn()?;