on master help sign in
helix-term: send the STOP signal to all processes in the process group (#3546)
3493473Cole Helbling
Cargo.lock+1
@@ -1177,6 +1177,7 @@ dependencies = [
1177 "helix-view",
1177 "helix-view",
1178 "ignore",
1178 "ignore",
1179 "indoc",
1179 "indoc",
1180+ "libc",
1180 "log",
1181 "log",
1181 "once_cell",
1182 "once_cell",
1182 "pulldown-cmark",
1183 "pulldown-cmark",
helix-term/Cargo.toml+1
@@ -68,6 +68,7 @@ grep-searcher = "0.1.11"
68
68
69[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
69[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
70signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
70signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
71+libc = "0.2.132"
71
72
72[build-dependencies]
73[build-dependencies]
73helix-loader = { version = "0.6", path = "../helix-loader" }
74helix-loader = { version = "0.6", path = "../helix-loader" }
helix-term/src/application.rs+27−5
@@ -40,10 +40,7 @@ use anyhow::{Context, Error};
40
40
41use crossterm::{event::Event as CrosstermEvent, tty::IsTty};
41use crossterm::{event::Event as CrosstermEvent, tty::IsTty};
42#[cfg(not(windows))]
42#[cfg(not(windows))]
43−use {
44− signal_hook::{consts::signal, low_level},
45− signal_hook_tokio::Signals,
43+use {signal_hook::consts::signal, signal_hook_tokio::Signals};
46−};
47#[cfg(windows)]
44#[cfg(windows)]
48type Signals = futures_util::stream::Empty<()>;
45type Signals = futures_util::stream::Empty<()>;
49
46
@@ -447,7 +444,32 @@ impl Application {
447 match signal {
444 match signal {
448 signal::SIGTSTP => {
445 signal::SIGTSTP => {
449 self.restore_term().unwrap();
446 self.restore_term().unwrap();
450− low_level::emulate_default_handler(signal::SIGTSTP).unwrap();
447+
448+ // SAFETY:
449+ //
450+ // - helix must have permissions to send signals to all processes in its signal
451+ // group, either by already having the requisite permission, or by having the
452+ // user's UID / EUID / SUID match that of the receiving process(es).
453+ let res = unsafe {
454+ // A pid of 0 sends the signal to the entire process group, allowing the user to
455+ // regain control of their terminal if the editor was spawned under another process
456+ // (e.g. when running `git commit`).
457+ //
458+ // We have to send SIGSTOP (not SIGTSTP) to the entire process group, because,
459+ // as mentioned above, the terminal will get stuck if `helix` was spawned from
460+ // an external process and that process waits for `helix` to complete. This may
461+ // be an issue with signal-hook-tokio, but the author of signal-hook believes it
462+ // could be a tokio issue instead:
463+ // https://github.com/vorner/signal-hook/issues/132
464+ libc::kill(0, signal::SIGSTOP)
465+ };
466+
467+ if res != 0 {
468+ let err = std::io::Error::last_os_error();
469+ eprintln!("{}", err);
470+ let res = err.raw_os_error().unwrap_or(1);
471+ std::process::exit(res);
472+ }
451 }
473 }
452 signal::SIGCONT => {
474 signal::SIGCONT => {
453 self.claim_term().await.unwrap();
475 self.claim_term().await.unwrap();