diff --git a/src/lib.rs b/src/lib.rs index 848ef67..468e959 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -325,10 +325,11 @@ impl Log for StdErrLog { Level::Trace => Color::Blue, }; { + // A failure here indicates the stream closed. Do not panic. writer .get_mut() .set_color(ColorSpec::new().set_fg(Some(color))) - .expect("failed to set color"); + .ok(); } if self.show_module_names { @@ -359,7 +360,8 @@ impl Log for StdErrLog { } let _ = writeln!(writer, "{}", record.args()); { - writer.get_mut().reset().expect("failed to reset the color"); + // A failure here indicates the stream closed. Do not panic. + writer.get_mut().reset().ok(); } } diff --git a/tests/pty_closed.rs b/tests/pty_closed.rs new file mode 100644 index 0000000..10faed0 --- /dev/null +++ b/tests/pty_closed.rs @@ -0,0 +1,69 @@ +#[cfg(unix)] +mod unix { + use std::fs::File; + use std::io::Error; + use std::os::unix::io::{AsRawFd, FromRawFd}; + + use log::info; + + fn check_ret(ret: libc::c_int, err_msg: &str) { + if ret < 0 { + panic!("{}: {}", err_msg, Error::last_os_error()); + } + } + + fn wrap_fd(fd: libc::c_int, err_msg: &str) -> File { + check_ret(fd, err_msg); + // Use File as a owned fd. + unsafe { File::from_raw_fd(fd) } + } + + #[test] + fn log_after_pty_close() { + let pt = wrap_fd(unsafe { libc::getpt() }, "getpt"); + check_ret(unsafe { libc::grantpt(pt.as_raw_fd()) }, "grantpt"); + check_ret(unsafe { libc::unlockpt(pt.as_raw_fd()) }, "unlockpt"); + + let name = unsafe { libc::ptsname(pt.as_raw_fd()) }; + if name.is_null() { + panic!("ptsname: {}", Error::last_os_error()); + } + + let client = wrap_fd(unsafe { libc::open(name, libc::O_RDWR) }, "open client pty"); + + // Back up stderr + let dup_stderr = wrap_fd(unsafe { libc::dup(2) }, "dup stderr"); + let dup_stderr_panic = wrap_fd(unsafe { libc::dup(2) }, "dup stderr"); + + // Set up the panic handler to restore stderr. + let default_panic = std::panic::take_hook(); + std::panic::set_hook(Box::new(move |info| { + check_ret( + unsafe { libc::dup2(dup_stderr_panic.as_raw_fd(), 2) }, + "dup2 restore stderr", + ); + default_panic(info); + })); + + // Replace stderr with pty + check_ret( + unsafe { libc::dup2(client.as_raw_fd(), 2) }, + "dup2 restore stderr", + ); + + stderrlog::new().verbosity(50).init().unwrap(); + info!("This should work."); + + println!("Closing PTY"); + drop(pt); + + println!("Sending log after PTY closed"); + info!("This should trigger an EIO."); + + // Restore stderr + check_ret( + unsafe { libc::dup2(dup_stderr.as_raw_fd(), 2) }, + "dup2 restore stderr", + ); + } +}