use std::sync::Arc;
use std::sync::atomic::{AtomicI32, Ordering};
use std::thread;
use signal_hook::consts::{SIGINT, SIGTERM};
use signal_hook::iterator::{Handle, Signals};
use crate::shell_exec::{forward_signal_to_pid, forward_signal_with_escalation};
pub struct ForegroundSignals {
signals: Signals,
}
impl ForegroundSignals {
pub fn install() -> std::io::Result<Self> {
Ok(Self {
signals: Signals::new([SIGINT, SIGTERM])?,
})
}
pub fn forward_to_pid(self, child_pid: i32, share_parent_pgroup: bool) -> ActiveForwarder {
self.run_listener(move |sig, originating| {
if record_originating(originating, sig) {
if share_parent_pgroup {
forward_signal_to_pid(child_pid, sig);
} else {
forward_signal_with_escalation(child_pid, sig);
}
}
})
}
pub fn forward_to_pgids(self, child_pgids: Vec<i32>) -> ActiveForwarder {
let mut seen_once = false;
self.run_listener(move |sig, originating| {
record_originating(originating, sig);
if !seen_once {
seen_once = true;
for &pgid in &child_pgids {
forward_signal_with_escalation(pgid, sig);
}
} else {
for &pgid in &child_pgids {
let _ = nix::sys::signal::killpg(
nix::unistd::Pid::from_raw(pgid),
nix::sys::signal::Signal::SIGKILL,
);
}
}
})
}
fn run_listener(
self,
mut body: impl FnMut(i32, &AtomicI32) + Send + 'static,
) -> ActiveForwarder {
let handle = self.signals.handle();
let originating = Arc::new(AtomicI32::new(0));
let listener = {
let originating = Arc::clone(&originating);
let mut signals = self.signals;
thread::spawn(move || {
for sig in signals.forever() {
body(sig, &originating);
}
})
};
ActiveForwarder {
handle,
listener,
originating,
}
}
}
fn record_originating(slot: &AtomicI32, sig: i32) -> bool {
slot.compare_exchange(0, sig, Ordering::SeqCst, Ordering::SeqCst)
.is_ok()
}
pub struct ActiveForwarder {
handle: Handle,
listener: thread::JoinHandle<()>,
originating: Arc<AtomicI32>,
}
impl ActiveForwarder {
pub fn stop(self) -> Option<i32> {
self.handle.close();
let _ = self.listener.join();
match self.originating.load(Ordering::SeqCst) {
0 => None,
sig => Some(sig),
}
}
}