Propagate SIGTSTP/SIGCONT to children
This commit is contained in:
parent
ef37a9fb97
commit
154c23a5a4
|
@ -1,8 +1,8 @@
|
|||
[root]
|
||||
name = "watchexec"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
dependencies = [
|
||||
"clap 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.19.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"globset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -12,7 +12,7 @@ dependencies = [
|
|||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"mktemp 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"notify 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"notify 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
|
@ -46,7 +46,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.19.0"
|
||||
version = "2.19.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -225,7 +225,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "notify"
|
||||
version = "3.0.0"
|
||||
version = "3.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -394,7 +394,7 @@ dependencies = [
|
|||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
"checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
|
||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||
"checksum clap 2.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef87e92396a3d29bf7e611c8a595be35ae90d9cb844a3571425900eaca4f51c8"
|
||||
"checksum clap 2.19.2 (registry+https://github.com/rust-lang/crates.io-index)" = "305ad043f009db535a110200541d4567b63e172b1fe030313fbb92565da7ed24"
|
||||
"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
|
||||
"checksum filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5363ab8e4139b8568a6237db5248646e5a8a2f89bd5ccb02092182b11fd3e922"
|
||||
"checksum fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc484842f1e2884faf56f529f960cc12ad8c71ce96cc7abba0a067c98fee344"
|
||||
|
@ -414,7 +414,7 @@ dependencies = [
|
|||
"checksum net2 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "5edf9cb6be97212423aed9413dd4729d62b370b5e1c571750e882cebbbc1e3e2"
|
||||
"checksum nix 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfb3ddedaa14746434a02041940495bf11325c22f6d36125d3bdd56090d50a79"
|
||||
"checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b"
|
||||
"checksum notify 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a29c8a989715f71d2e8272e335a34e657957040cc4a6ecaab218ab717c05835"
|
||||
"checksum notify 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13fdd4a6894329b193f38f03a88823ce721275fdfdb29820c44a30515033524e"
|
||||
"checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
|
||||
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "watchexec"
|
||||
version = "1.5.0"
|
||||
version = "1.6.0"
|
||||
authors = ["Matt Green <mattgreenrocks@gmail.com>"]
|
||||
description = "Executes commands in response to file modifications"
|
||||
documentation = "https://github.com/mattgreen/watchexec"
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
use std::sync::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
static ref CLEANUP: Mutex<Option<Box<Fn() + Send>>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn install_handler<F>(handler: F)
|
||||
where F: Fn() + 'static + Send + Sync
|
||||
{
|
||||
use std::thread;
|
||||
use nix::sys::signal::*;
|
||||
|
||||
// Mask all termination signals
|
||||
// These propagate to all threads started after this point
|
||||
let mut mask = SigSet::empty();
|
||||
mask.add(SIGTERM);
|
||||
mask.add(SIGINT);
|
||||
mask.thread_set_mask().expect("unable to set signal mask");
|
||||
|
||||
set_handler(handler);
|
||||
|
||||
// Spawn a thread to catch these signals
|
||||
thread::spawn(move || {
|
||||
let sig = mask.wait().expect("unable to sigwait");
|
||||
|
||||
// Invoke closure
|
||||
invoke();
|
||||
|
||||
// Restore default behavior for received signal and unmask it
|
||||
let default_action = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
|
||||
|
||||
unsafe {
|
||||
let _ = sigaction(sig, &default_action);
|
||||
}
|
||||
|
||||
let mut new_mask = SigSet::empty();
|
||||
new_mask.add(sig);
|
||||
let _ = new_mask.thread_unblock();
|
||||
|
||||
// Re-raise, killing the process
|
||||
let _ = raise(sig);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn install_handler<F>(handler: F)
|
||||
where F: Fn() + 'static + Send + Sync
|
||||
{
|
||||
use kernel32::SetConsoleCtrlHandler;
|
||||
use winapi::{BOOL, DWORD, FALSE, TRUE};
|
||||
|
||||
pub unsafe extern "system" fn ctrl_handler(_: DWORD) -> BOOL {
|
||||
invoke();
|
||||
|
||||
FALSE
|
||||
}
|
||||
|
||||
set_handler(handler);
|
||||
|
||||
unsafe {
|
||||
SetConsoleCtrlHandler(Some(ctrl_handler), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke() {
|
||||
if let Some(ref handler) = *CLEANUP.lock().unwrap() {
|
||||
handler()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_handler<F>(handler: F)
|
||||
where F: Fn() + 'static + Send + Sync
|
||||
{
|
||||
*CLEANUP.lock().unwrap() = Some(Box::new(handler));
|
||||
}
|
19
src/main.rs
19
src/main.rs
|
@ -21,9 +21,9 @@ extern crate mktemp;
|
|||
|
||||
mod cli;
|
||||
mod gitignore;
|
||||
mod interrupt;
|
||||
mod notification_filter;
|
||||
mod process;
|
||||
mod signal;
|
||||
mod watcher;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
@ -36,6 +36,7 @@ use std::path::{PathBuf};
|
|||
|
||||
use notification_filter::NotificationFilter;
|
||||
use process::Process;
|
||||
use signal::Signal;
|
||||
use watcher::{Event, Watcher};
|
||||
|
||||
fn find_gitignore(path: &Path) -> Option<PathBuf> {
|
||||
|
@ -81,12 +82,22 @@ fn main() {
|
|||
let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
|
||||
let weak_child = Arc::downgrade(&child_process);
|
||||
|
||||
interrupt::install_handler(move || {
|
||||
signal::install_handler(move |sig: Signal| {
|
||||
if let Some(lock) = weak_child.upgrade() {
|
||||
let strong = lock.read().unwrap();
|
||||
if let Some(ref child) = *strong {
|
||||
child.kill();
|
||||
child.wait();
|
||||
match sig {
|
||||
Signal::Terminate => {
|
||||
child.kill();
|
||||
child.wait();
|
||||
},
|
||||
Signal::Stop => {
|
||||
child.pause();
|
||||
},
|
||||
Signal::Continue => {
|
||||
child.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@ pub use self::imp::Process;
|
|||
|
||||
#[cfg(target_family = "unix")]
|
||||
mod imp {
|
||||
use libc::c_int;
|
||||
use libc::pid_t;
|
||||
use std::io::Result;
|
||||
use std::path::PathBuf;
|
||||
|
@ -79,6 +80,25 @@ mod imp {
|
|||
}
|
||||
|
||||
pub fn kill(&self) {
|
||||
use libc::SIGTERM;
|
||||
|
||||
self.signal(SIGTERM);
|
||||
}
|
||||
|
||||
pub fn pause(&self) {
|
||||
use libc::SIGTSTP;
|
||||
|
||||
self.signal(SIGTSTP);
|
||||
}
|
||||
|
||||
|
||||
pub fn resume(&self) {
|
||||
use libc::SIGCONT;
|
||||
|
||||
self.signal(SIGCONT);
|
||||
}
|
||||
|
||||
fn signal(&self, sig: c_int) {
|
||||
use libc::*;
|
||||
|
||||
extern "C" {
|
||||
|
@ -86,8 +106,9 @@ mod imp {
|
|||
}
|
||||
|
||||
unsafe {
|
||||
killpg(self.pgid, SIGTERM);
|
||||
killpg(self.pgid, sig);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn wait(&self) {
|
||||
|
@ -165,6 +186,12 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pause(&self) {
|
||||
}
|
||||
|
||||
pub fn resume(&self) {
|
||||
}
|
||||
|
||||
pub fn wait(&self) {
|
||||
unsafe {
|
||||
let _ = WaitForSingleObject(self.job, INFINITE);
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
use std::sync::Mutex;
|
||||
|
||||
lazy_static! {
|
||||
static ref CLEANUP: Mutex<Option<Box<Fn(self::Signal) + Send>>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
pub enum Signal {
|
||||
Terminate,
|
||||
Stop,
|
||||
Continue
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn install_handler<F>(handler: F)
|
||||
where F: Fn(self::Signal) + 'static + Send + Sync
|
||||
{
|
||||
use std::thread;
|
||||
use nix::sys::signal::*;
|
||||
|
||||
// Mask all signals interesting to us. The mask propagates
|
||||
// to all threads started after this point.
|
||||
let mut mask = SigSet::empty();
|
||||
mask.add(SIGTERM);
|
||||
mask.add(SIGINT);
|
||||
mask.add(SIGTSTP);
|
||||
mask.add(SIGCONT);
|
||||
mask.thread_set_mask().expect("unable to set signal mask");
|
||||
|
||||
set_handler(handler);
|
||||
|
||||
// Spawn a thread to catch these signals
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
let raw_signal = mask.wait().expect("unable to sigwait");
|
||||
|
||||
let sig = match raw_signal {
|
||||
SIGTERM => self::Signal::Terminate,
|
||||
SIGINT => self::Signal::Terminate,
|
||||
SIGTSTP => self::Signal::Stop,
|
||||
SIGCONT => self::Signal::Continue,
|
||||
_ => unreachable!()
|
||||
};
|
||||
|
||||
// Invoke closure
|
||||
invoke(sig);
|
||||
|
||||
// Restore default behavior for received signal and unmask it
|
||||
let default_action = SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty());
|
||||
|
||||
unsafe {
|
||||
let _ = sigaction(raw_signal, &default_action);
|
||||
}
|
||||
|
||||
let mut new_mask = SigSet::empty();
|
||||
new_mask.add(raw_signal);
|
||||
let _ = new_mask.thread_unblock();
|
||||
|
||||
// Re-raise
|
||||
let _ = raise(raw_signal);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn install_handler<F>(handler: F)
|
||||
where F: Fn(self::Signal) + 'static + Send + Sync
|
||||
{
|
||||
use kernel32::SetConsoleCtrlHandler;
|
||||
use winapi::{BOOL, DWORD, FALSE, TRUE};
|
||||
|
||||
pub unsafe extern "system" fn ctrl_handler(_: DWORD) -> BOOL {
|
||||
invoke(self::Signal::Terminate);
|
||||
|
||||
FALSE
|
||||
}
|
||||
|
||||
set_handler(handler);
|
||||
|
||||
unsafe {
|
||||
SetConsoleCtrlHandler(Some(ctrl_handler), TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
fn invoke(sig: self::Signal) {
|
||||
if let Some(ref handler) = *CLEANUP.lock().unwrap() {
|
||||
handler(sig)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_handler<F>(handler: F)
|
||||
where F: Fn(self::Signal) + 'static + Send + Sync
|
||||
{
|
||||
*CLEANUP.lock().unwrap() = Some(Box::new(handler));
|
||||
}
|
Loading…
Reference in New Issue