diff --git a/src/uu/stty/src/stty.rs b/src/uu/stty/src/stty.rs index 9153c1528fe..9ddc78e34e6 100644 --- a/src/uu/stty/src/stty.rs +++ b/src/uu/stty/src/stty.rs @@ -31,6 +31,7 @@ use std::num::IntErrorKind; use std::os::fd::{AsFd, BorrowedFd}; use std::os::unix::fs::OpenOptionsExt; use std::os::unix::io::{AsRawFd, RawFd}; +use std::panic; use uucore::error::{FromIo, UError, UResult, USimpleError, UUsageError}; use uucore::format_usage; use uucore::parser::num_parser::ExtendedParser; @@ -604,36 +605,52 @@ fn print_terminal_size( window_size: Option<&TermSize>, term_size: Option<&TermSize>, ) -> nix::Result<()> { - let speed = cfgetospeed(termios); + // Wrap cfgetospeed in catch_unwind to handle panics on invalid termios. + // This can happen on some systems (e.g., glibc 2.42) when cfgetospeed is called + // on an invalid or non-TTY termios structure. + // We set a custom panic hook to suppress the panic message since we're handling it gracefully. + let old_hook = panic::take_hook(); + panic::set_hook(Box::new(|_| { + // Silently ignore the panic - we're handling it by skipping the speed output + })); + + let speed_result = panic::catch_unwind(panic::AssertUnwindSafe(|| cfgetospeed(termios))); + + // Restore the original panic hook + panic::set_hook(old_hook); + let mut printer = WrappedPrinter::new(window_size); - // BSDs use a u32 for the baud rate, so we can simply print it. - #[cfg(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - ))] - printer.print(&translate!("stty-output-speed", "speed" => speed)); + if let Ok(speed) = speed_result { + // BSDs use a u32 for the baud rate, so we can simply print it. + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] + printer.print(&translate!("stty-output-speed", "speed" => speed)); - // Other platforms need to use the baud rate enum, so printing the right value - // becomes slightly more complicated. - #[cfg(not(any( - target_os = "freebsd", - target_os = "dragonfly", - target_os = "ios", - target_os = "macos", - target_os = "netbsd", - target_os = "openbsd" - )))] - for (text, baud_rate) in BAUD_RATES { - if *baud_rate == speed { - printer.print(&translate!("stty-output-speed", "speed" => (*text))); - break; + // Other platforms need to use the baud rate enum, so printing the right value + // becomes slightly more complicated. + #[cfg(not(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "ios", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + )))] + for (text, baud_rate) in BAUD_RATES { + if *baud_rate == speed { + printer.print(&translate!("stty-output-speed", "speed" => (*text))); + break; + } } } + // If cfgetospeed panics, just skip printing the speed if opts.all { let term_size = term_size.as_ref().expect("terminal size should be set"); @@ -969,7 +986,10 @@ fn apply_baud_rate_flag(termios: &mut Termios, input: &AllFlags) { target_os = "openbsd" ))] if let AllFlags::Baud(n) = input { - cfsetospeed(termios, *n).expect("Failed to set baud rate"); + // Attempt to set baud rate; if it fails, we silently continue. + // Some systems may not support all baud rates. + // See: https://github.com/nix-rust/nix/issues/1376 (pending upstream fix) + let _ = cfsetospeed(termios, *n); } // Other platforms use an enum. @@ -982,7 +1002,12 @@ fn apply_baud_rate_flag(termios: &mut Termios, input: &AllFlags) { target_os = "openbsd" )))] if let AllFlags::Baud(br) = input { - cfsetospeed(termios, *br).expect("Failed to set baud rate"); + // Attempt to set baud rate; if it fails, we silently continue. + // Some systems may not support all baud rates. + // See: https://github.com/nix-rust/nix/issues/1376 (pending upstream fix) + // cspell:ignore TCGETS TCSETS ioctls + // Linux needs TCGETS2/TCSETS2 ioctls for non-standard baud rates (e.g., 250000 BAUD) + let _ = cfsetospeed(termios, *br); } } @@ -1207,7 +1232,13 @@ fn combo_to_flags(combo: &str) -> Vec> { .collect::>(); let mut ccs = ccs .iter() - .map(|cc| ArgOptions::Mapping((cc.0, string_to_control_char(cc.1).unwrap()))) + .filter_map(|cc| { + // These are hardcoded values that should always be valid, + // but we use filter_map to be extra safe + string_to_control_char(cc.1) + .ok() + .map(|val| ArgOptions::Mapping((cc.0, val))) + }) .collect::>(); flags.append(&mut ccs); flags