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 // master_fd => PTY
let handle_event = |event_id: usize| { 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 { if event_id as RawFd == tty_fd {
let mut packet = [0; 4096]; let mut packet = [0; 4096];
loop { loop {
@@ -65,10 +69,15 @@ pub fn handle(
Ok(0) => return, Ok(0) => return,
Ok(count) => count, Ok(count) => count,
Err(ref err) if err.errno() == EAGAIN => break, 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]) if let Err(err) = redox::write(master_fd as usize, &packet[..count]) {
.expect("getty: failed to write master PTY"); eprintln!("getty: failed to write master PTY: {err}");
break;
}
} }
} else if event_id as RawFd == master_fd { } else if event_id as RawFd == master_fd {
let mut packet = [0; 4096]; let mut packet = [0; 4096];
@@ -77,10 +86,15 @@ pub fn handle(
Ok(0) => return, Ok(0) => return,
Ok(count) => count, Ok(count) => count,
Err(ref err) if err.errno() == EAGAIN => break, 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]) if let Err(err) = redox::write(tty_fd as usize, &packet[1..count]) {
.expect("getty: failed to write to TTY"); eprintln!("getty: failed to write to TTY: {err}");
break;
}
if packet[0] & 1 == 1 { if packet[0] & 1 == 1 {
let _ = redox::fsync(tty_fd as usize); let _ = redox::fsync(tty_fd as usize);
} }
@@ -92,26 +106,36 @@ pub fn handle(
handle_event(master_fd as usize); handle_event(master_fd as usize);
'events: loop { 'events: loop {
let sys_event = event_queue let sys_event = match event_queue.next() {
.next() Some(Ok(event)) => event,
.expect("getty: event queue stopped") Some(Err(err)) => {
.expect("getty: failed to read event file"); 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); handle_event(sys_event.fd);
match process.try_wait() { match process.try_wait() {
Ok(status) => match status { Ok(Some(_code)) => break 'events,
Some(_code) => break 'events, Ok(None) => (),
None => (),
},
Err(err) => match err.kind() { Err(err) => match err.kind() {
ErrorKind::WouldBlock => (), 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(); 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) { pub fn getpty(columns: u16, lines: u16) -> (RawFd, String) {