getty: harden the console<->PTY bridge against transient read/write/event errors (log-and-continue instead of panic; a panic here killed the live shell's I/O)

This commit is contained in:
2026-07-19 18:02:17 +09:00
parent b0cfab717e
commit 3b02e0b999
+40 -16
View File
@@ -58,6 +58,10 @@ pub fn handle(
// master_fd => PTY
let handle_event = |event_id: usize| {
// The console<->PTY bridge must never panic on a transient read/write
// error: getty runs this while the login shell is alive, so a panic here
// kills the shell's I/O forwarding (input stops, output stops). Log and
// stop the current forwarding cycle instead; the event loop keeps going.
if event_id as RawFd == tty_fd {
let mut packet = [0; 4096];
loop {
@@ -65,10 +69,15 @@ pub fn handle(
Ok(0) => return,
Ok(count) => count,
Err(ref err) if err.errno() == EAGAIN => break,
Err(_) => panic!("getty: failed to read from TTY"),
Err(err) => {
eprintln!("getty: failed to read from TTY: {err}");
return;
}
};
redox::write(master_fd as usize, &packet[..count])
.expect("getty: failed to write master PTY");
if let Err(err) = redox::write(master_fd as usize, &packet[..count]) {
eprintln!("getty: failed to write master PTY: {err}");
break;
}
}
} else if event_id as RawFd == master_fd {
let mut packet = [0; 4096];
@@ -77,10 +86,15 @@ pub fn handle(
Ok(0) => return,
Ok(count) => count,
Err(ref err) if err.errno() == EAGAIN => break,
Err(_) => panic!("getty: failed to read from master TTY"),
Err(err) => {
eprintln!("getty: failed to read from master PTY: {err}");
return;
}
};
redox::write(tty_fd as usize, &packet[1..count])
.expect("getty: failed to write to TTY");
if let Err(err) = redox::write(tty_fd as usize, &packet[1..count]) {
eprintln!("getty: failed to write to TTY: {err}");
break;
}
if packet[0] & 1 == 1 {
let _ = redox::fsync(tty_fd as usize);
}
@@ -92,26 +106,36 @@ pub fn handle(
handle_event(master_fd as usize);
'events: loop {
let sys_event = event_queue
.next()
.expect("getty: event queue stopped")
.expect("getty: failed to read event file");
let sys_event = match event_queue.next() {
Some(Ok(event)) => event,
Some(Err(err)) => {
eprintln!("getty: failed to read event file: {err}; exiting bridge");
break 'events;
}
None => {
eprintln!("getty: event queue stopped; exiting bridge");
break 'events;
}
};
handle_event(sys_event.fd);
match process.try_wait() {
Ok(status) => match status {
Some(_code) => break 'events,
None => (),
},
Ok(Some(_code)) => break 'events,
Ok(None) => (),
Err(err) => match err.kind() {
ErrorKind::WouldBlock => (),
_ => panic!("getty: failed to wait on child: {:?}", err),
_ => {
eprintln!("getty: failed to wait on child: {err:?}; exiting bridge");
break 'events;
}
},
}
}
let _ = process.kill();
process.wait().expect("getty: failed to wait on login");
if let Err(err) = process.wait() {
eprintln!("getty: failed to wait on login: {err}");
}
}
pub fn getpty(columns: u16, lines: u16) -> (RawFd, String) {