diff --git a/Cargo.lock b/Cargo.lock index a1a9eae4dd27..25db822026c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1177,6 +1177,7 @@ dependencies = [ "helix-view", "ignore", "indoc", + "libc", "log", "once_cell", "pulldown-cmark", diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index bca567c28688..279fdcf8ba4e 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -68,6 +68,7 @@ grep-searcher = "0.1.11" [target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100 signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } +libc = "0.2.132" [build-dependencies] helix-loader = { version = "0.6", path = "../helix-loader" } diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs index c7e939959ca4..281c923c1f84 100644 --- a/helix-term/src/application.rs +++ b/helix-term/src/application.rs @@ -40,10 +40,7 @@ use anyhow::{Context, Error}; use crossterm::{event::Event as CrosstermEvent, tty::IsTty}; #[cfg(not(windows))] -use { - signal_hook::{consts::signal, low_level}, - signal_hook_tokio::Signals, -}; +use {signal_hook::consts::signal, signal_hook_tokio::Signals}; #[cfg(windows)] type Signals = futures_util::stream::Empty<()>; @@ -447,7 +444,32 @@ impl Application { match signal { signal::SIGTSTP => { self.restore_term().unwrap(); - low_level::emulate_default_handler(signal::SIGTSTP).unwrap(); + + // SAFETY: + // + // - helix must have permissions to send signals to all processes in its signal + // group, either by already having the requisite permission, or by having the + // user's UID / EUID / SUID match that of the receiving process(es). + let res = unsafe { + // A pid of 0 sends the signal to the entire process group, allowing the user to + // regain control of their terminal if the editor was spawned under another process + // (e.g. when running `git commit`). + // + // We have to send SIGSTOP (not SIGTSTP) to the entire process group, because, + // as mentioned above, the terminal will get stuck if `helix` was spawned from + // an external process and that process waits for `helix` to complete. This may + // be an issue with signal-hook-tokio, but the author of signal-hook believes it + // could be a tokio issue instead: + // https://github.com/vorner/signal-hook/issues/132 + libc::kill(0, signal::SIGSTOP) + }; + + if res != 0 { + let err = std::io::Error::last_os_error(); + eprintln!("{}", err); + let res = err.raw_os_error().unwrap_or(1); + std::process::exit(res); + } } signal::SIGCONT => { self.claim_term().await.unwrap();