Make SIGTERM handler async-signal-safe again
Some checks failed
make test / ubuntu (push) Waiting to run
make test / ubuntu-32bit-static-pcre2 (push) Waiting to run
make test / ubuntu-asan (push) Waiting to run
make test / macos (push) Waiting to run
Rust checks / rustfmt (push) Waiting to run
Rust checks / clippy (push) Waiting to run
Lock threads / lock (push) Has been cancelled

This commit is contained in:
Johannes Altmanninger 2024-10-21 09:16:02 +02:00
parent ae7b401029
commit 30cba03bf9
9 changed files with 39 additions and 39 deletions

View File

@ -21,7 +21,6 @@ use bitflags::bitflags;
use core::slice; use core::slice;
use libc::{EIO, O_WRONLY, SIGTTOU, SIG_IGN, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use libc::{EIO, O_WRONLY, SIGTTOU, SIG_IGN, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::env;
use std::ffi::{CStr, CString, OsStr, OsString}; use std::ffi::{CStr, CString, OsStr, OsString};
use std::mem; use std::mem;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -30,6 +29,7 @@ use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering}; use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
use std::sync::{Arc, MutexGuard}; use std::sync::{Arc, MutexGuard};
use std::time; use std::time;
use std::{env, process};
pub const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME"); pub const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");
@ -1634,12 +1634,16 @@ pub fn fish_reserved_codepoint(c: char) -> bool {
|| (c >= key::Backspace && c < ENCODE_DIRECT_END) || (c >= key::Backspace && c < ENCODE_DIRECT_END)
} }
pub fn redirect_tty_output() { pub fn redirect_tty_output(in_signal_handler: bool) {
unsafe { unsafe {
let mut t: libc::termios = mem::zeroed(); let mut t: libc::termios = mem::zeroed();
let s = CString::new("/dev/null").unwrap(); let s = CStr::from_bytes_with_nul(b"/dev/null\0").unwrap();
let fd = libc::open(s.as_ptr(), O_WRONLY); let fd = libc::open(s.as_ptr(), O_WRONLY);
assert!(fd != -1, "Could not open /dev/null!"); if in_signal_handler && fd == -1 {
process::abort();
} else {
assert!(fd != -1, "Could not open /dev/null!");
}
for stdfd in [STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO] { for stdfd in [STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO] {
if libc::tcgetattr(stdfd, &mut t) == -1 && errno::errno().0 == EIO { if libc::tcgetattr(stdfd, &mut t) == -1 && errno::errno().0 == EIO {
libc::dup2(fd, stdfd); libc::dup2(fd, stdfd);

View File

@ -440,7 +440,7 @@ fn launch_process_nofork(vars: &EnvStack, p: &Process) -> ! {
let actual_cmd = wcs2zstring(&p.actual_cmd); let actual_cmd = wcs2zstring(&p.actual_cmd);
// Ensure the terminal modes are what they were before we changed them. // Ensure the terminal modes are what they were before we changed them.
restore_term_mode(); restore_term_mode(false);
// Bounce to launch_process. This never returns. // Bounce to launch_process. This never returns.
safe_launch_process(p, &actual_cmd, &argv, &*envp); safe_launch_process(p, &actual_cmd, &argv, &*envp);
} }

View File

@ -2,7 +2,7 @@
// This concerns posix_spawn support, and async-signal // This concerns posix_spawn support, and async-signal
// safe code which happens in between fork and exec. // safe code which happens in between fork and exec.
mod flog_safe; pub mod flog_safe;
pub mod postfork; pub mod postfork;
pub mod spawn; pub mod spawn;
use crate::proc::Job; use crate::proc::Job;

View File

@ -7,6 +7,7 @@ use crate::common::{
use crate::env::{EnvStack, Environment}; use crate::env::{EnvStack, Environment};
use crate::fd_readable_set::FdReadableSet; use crate::fd_readable_set::FdReadableSet;
use crate::flog::FLOG; use crate::flog::FLOG;
use crate::fork_exec::flog_safe::FLOG_SAFE;
use crate::global_safety::RelaxedAtomicBool; use crate::global_safety::RelaxedAtomicBool;
use crate::key::{ use crate::key::{
self, alt, canonicalize_control_char, canonicalize_keyed_control_char, function_key, shift, self, alt, canonicalize_control_char, canonicalize_keyed_control_char, function_key, shift,
@ -485,13 +486,7 @@ pub fn terminal_protocols_enable_ifn() {
"\x1b=", // set application keypad mode, so the keypad keys send unique codes "\x1b=", // set application keypad mode, so the keypad keys send unique codes
) )
}; };
FLOG!( FLOG!(term_protocols, "Enabling extended keys and bracketed paste");
term_protocols,
format!(
"Enabling extended keys and bracketed paste: {:?}",
sequences
)
);
let _ = write_to_fd(sequences.as_bytes(), STDOUT_FILENO); let _ = write_to_fd(sequences.as_bytes(), STDOUT_FILENO);
if IS_TMUX.load() { if IS_TMUX.load() {
let _ = write_to_fd("\x1b[?1004h".as_bytes(), STDOUT_FILENO); let _ = write_to_fd("\x1b[?1004h".as_bytes(), STDOUT_FILENO);
@ -513,12 +508,9 @@ pub(crate) fn terminal_protocols_disable_ifn() {
"\x1b>", // application keypad mode "\x1b>", // application keypad mode
) )
}; };
FLOG!( FLOG_SAFE!(
term_protocols, term_protocols,
format!( "Disabling extended keys and bracketed paste"
"Disabling extended keys and bracketed paste: {:?}",
sequences
)
); );
let _ = write_to_fd(sequences.as_bytes(), STDOUT_FILENO); let _ = write_to_fd(sequences.as_bytes(), STDOUT_FILENO);
if IS_TMUX.load() { if IS_TMUX.load() {

View File

@ -13,7 +13,7 @@ use crate::{
threads::{asan_maybe_exit, is_main_thread}, threads::{asan_maybe_exit, is_main_thread},
}; };
pub static AT_EXIT: OnceCell<Box<dyn Fn() + Send + Sync>> = OnceCell::new(); pub static AT_EXIT: OnceCell<Box<dyn Fn(bool) + Send + Sync>> = OnceCell::new();
pub fn panic_handler(main: impl FnOnce() -> i32 + UnwindSafe) -> ! { pub fn panic_handler(main: impl FnOnce() -> i32 + UnwindSafe) -> ! {
if isatty(STDIN_FILENO) { if isatty(STDIN_FILENO) {
@ -28,7 +28,7 @@ pub fn panic_handler(main: impl FnOnce() -> i32 + UnwindSafe) -> ! {
return; return;
} }
if let Some(at_exit) = AT_EXIT.get() { if let Some(at_exit) = AT_EXIT.get() {
(at_exit)(); (at_exit)(false);
} }
eprintf!( eprintf!(
"%s crashed, please report a bug.", "%s crashed, please report a bug.",
@ -54,7 +54,7 @@ pub fn panic_handler(main: impl FnOnce() -> i32 + UnwindSafe) -> ! {
} }
let exit_status = main(); let exit_status = main();
if let Some(at_exit) = AT_EXIT.get() { if let Some(at_exit) = AT_EXIT.get() {
(at_exit)(); (at_exit)(false);
} }
asan_maybe_exit(exit_status); asan_maybe_exit(exit_status);
std::process::exit(exit_status) std::process::exit(exit_status)

View File

@ -425,7 +425,7 @@ impl TtyTransfer {
} }
EBADF => { EBADF => {
// stdin has been closed. Workaround a glibc bug - see #3644. // stdin has been closed. Workaround a glibc bug - see #3644.
redirect_tty_output(); redirect_tty_output(false);
return false; return false;
} }
_ => { _ => {

View File

@ -200,7 +200,7 @@ fn redirect_tty_after_sighup() {
assert!(reader_received_sighup(), "SIGHUP not received"); assert!(reader_received_sighup(), "SIGHUP not received");
static TTY_REDIRECTED: RelaxedAtomicBool = RelaxedAtomicBool::new(false); static TTY_REDIRECTED: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
if !TTY_REDIRECTED.swap(true) { if !TTY_REDIRECTED.swap(true) {
redirect_tty_output(); redirect_tty_output(false);
} }
} }
@ -585,7 +585,7 @@ pub fn reader_read(parser: &Parser, fd: RawFd, io: &IoChain) -> c_int {
if isatty(STDIN_FILENO) { if isatty(STDIN_FILENO) {
interactive = true; interactive = true;
} else if unsafe { libc::tcgetattr(STDIN_FILENO, &mut t) } == -1 && errno().0 == EIO { } else if unsafe { libc::tcgetattr(STDIN_FILENO, &mut t) } == -1 && errno().0 == EIO {
redirect_tty_output(); redirect_tty_output(false);
interactive = true; interactive = true;
} }
} }
@ -793,7 +793,11 @@ pub fn reader_init(will_restore_foreground_pgroup: bool) {
#[cfg(not(test))] #[cfg(not(test))]
assert!(AT_EXIT.get().is_none()); assert!(AT_EXIT.get().is_none());
AT_EXIT.get_or_init(|| Box::new(move || reader_deinit(will_restore_foreground_pgroup))); AT_EXIT.get_or_init(|| {
Box::new(move |in_signal_handler| {
reader_deinit(in_signal_handler, will_restore_foreground_pgroup)
})
});
// Set the mode used for program execution, initialized to the current mode. // Set the mode used for program execution, initialized to the current mode.
let mut tty_modes_for_external_cmds = TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap(); let mut tty_modes_for_external_cmds = TTY_MODES_FOR_EXTERNAL_CMDS.lock().unwrap();
@ -821,8 +825,8 @@ pub fn reader_init(will_restore_foreground_pgroup: bool) {
} }
} }
pub fn reader_deinit(restore_foreground_pgroup: bool) { pub fn reader_deinit(in_signal_handler: bool, restore_foreground_pgroup: bool) {
restore_term_mode(); restore_term_mode(in_signal_handler);
crate::input_common::terminal_protocols_disable_ifn(); crate::input_common::terminal_protocols_disable_ifn();
if restore_foreground_pgroup { if restore_foreground_pgroup {
restore_term_foreground_process_group_for_exit(); restore_term_foreground_process_group_for_exit();
@ -832,7 +836,7 @@ pub fn reader_deinit(restore_foreground_pgroup: bool) {
/// Restore the term mode if we own the terminal and are interactive (#8705). /// Restore the term mode if we own the terminal and are interactive (#8705).
/// It's important we do this before restore_foreground_process_group, /// It's important we do this before restore_foreground_process_group,
/// otherwise we won't think we own the terminal. /// otherwise we won't think we own the terminal.
pub fn restore_term_mode() { pub fn restore_term_mode(in_signal_handler: bool) {
if !is_interactive_session() || unsafe { libc::getpgrp() != libc::tcgetpgrp(STDIN_FILENO) } { if !is_interactive_session() || unsafe { libc::getpgrp() != libc::tcgetpgrp(STDIN_FILENO) } {
return; return;
} }
@ -845,7 +849,7 @@ pub fn restore_term_mode() {
) == -1 ) == -1
} && errno().0 == EIO } && errno().0 == EIO
{ {
redirect_tty_output(); redirect_tty_output(in_signal_handler);
} }
} }
@ -1889,14 +1893,14 @@ impl<'a> Reader<'a> {
// Get the current terminal modes. These will be restored when the function returns. // Get the current terminal modes. These will be restored when the function returns.
let mut old_modes: libc::termios = unsafe { std::mem::zeroed() }; let mut old_modes: libc::termios = unsafe { std::mem::zeroed() };
if unsafe { libc::tcgetattr(zelf.conf.inputfd, &mut old_modes) } == -1 && errno().0 == EIO { if unsafe { libc::tcgetattr(zelf.conf.inputfd, &mut old_modes) } == -1 && errno().0 == EIO {
redirect_tty_output(); redirect_tty_output(false);
} }
// Set the new modes. // Set the new modes.
if unsafe { libc::tcsetattr(zelf.conf.inputfd, TCSANOW, &*shell_modes()) } == -1 { if unsafe { libc::tcsetattr(zelf.conf.inputfd, TCSANOW, &*shell_modes()) } == -1 {
let err = errno().0; let err = errno().0;
if err == EIO { if err == EIO {
redirect_tty_output(); redirect_tty_output(false);
} }
// This check is required to work around certain issues with fish's approach to // This check is required to work around certain issues with fish's approach to
@ -1974,7 +1978,7 @@ impl<'a> Reader<'a> {
&& is_interactive_session() && is_interactive_session()
{ {
if errno().0 == EIO { if errno().0 == EIO {
redirect_tty_output(); redirect_tty_output(false);
} }
perror("tcsetattr"); // return to previous mode perror("tcsetattr"); // return to previous mode
} }
@ -3758,7 +3762,7 @@ fn term_donate(quiet: bool /* = false */) {
} == -1 } == -1
{ {
if errno().0 == EIO { if errno().0 == EIO {
redirect_tty_output(); redirect_tty_output(false);
} }
if errno().0 != EINTR { if errno().0 != EINTR {
if !quiet { if !quiet {
@ -3804,7 +3808,7 @@ fn term_steal(copy_modes: bool) {
} }
while unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, &*shell_modes()) } == -1 { while unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, &*shell_modes()) } == -1 {
if errno().0 == EIO { if errno().0 == EIO {
redirect_tty_output(); redirect_tty_output(false);
} }
if errno().0 != EINTR { if errno().0 != EINTR {
FLOG!(warning, wgettext!("Could not set terminal mode for shell")); FLOG!(warning, wgettext!("Could not set terminal mode for shell"));
@ -3875,7 +3879,7 @@ fn acquire_tty_or_exit(shell_pgid: libc::pid_t) {
break; break;
} }
// No TTY, cannot be interactive? // No TTY, cannot be interactive?
redirect_tty_output(); redirect_tty_output(false);
FLOG!( FLOG!(
warning, warning,
wgettext!("No TTY for interactive shell (tcgetpgrp failed)") wgettext!("No TTY for interactive shell (tcgetpgrp failed)")
@ -3941,7 +3945,7 @@ fn reader_interactive_init(parser: &Parser) {
// Take control of the terminal // Take control of the terminal
if unsafe { libc::tcsetpgrp(STDIN_FILENO, shell_pgid) } == -1 { if unsafe { libc::tcsetpgrp(STDIN_FILENO, shell_pgid) } == -1 {
if errno().0 == ENOTTY { if errno().0 == ENOTTY {
redirect_tty_output(); redirect_tty_output(false);
} }
FLOG!(error, wgettext!("Failed to take control of the terminal")); FLOG!(error, wgettext!("Failed to take control of the terminal"));
perror("tcsetpgrp"); perror("tcsetpgrp");
@ -3951,7 +3955,7 @@ fn reader_interactive_init(parser: &Parser) {
// Configure terminal attributes // Configure terminal attributes
if unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, &*shell_modes()) } == -1 { if unsafe { libc::tcsetattr(STDIN_FILENO, TCSANOW, &*shell_modes()) } == -1 {
if errno().0 == EIO { if errno().0 == EIO {
redirect_tty_output(); redirect_tty_output(false);
} }
FLOG!(warning, wgettext!("Failed to set startup terminal mode!")); FLOG!(warning, wgettext!("Failed to set startup terminal mode!"));
perror("tcsetattr"); perror("tcsetattr");

View File

@ -89,7 +89,7 @@ extern "C" fn fish_signal_handler(
// Handle sigterm. The only thing we do is restore the front process ID, then die. // Handle sigterm. The only thing we do is restore the front process ID, then die.
if !observed { if !observed {
if let Some(at_exit) = AT_EXIT.get() { if let Some(at_exit) = AT_EXIT.get() {
(at_exit)(); (at_exit)(true);
} }
// Safety: signal() and raise() are async-signal-safe. // Safety: signal() and raise() are async-signal-safe.
unsafe { unsafe {

View File

@ -111,7 +111,7 @@ pub mod prelude {
}); });
reader_init(false); reader_init(false);
ScopeGuard::new((), |()| { ScopeGuard::new((), |()| {
reader_deinit(false); reader_deinit(false, false);
}) })
} }