diff --git a/Cargo.lock b/Cargo.lock index 9655d33..c7fd26f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 127862a..4a35292 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "watchexec" -version = "1.5.0" +version = "1.6.0" authors = ["Matt Green "] description = "Executes commands in response to file modifications" documentation = "https://github.com/mattgreen/watchexec" diff --git a/src/interrupt.rs b/src/interrupt.rs deleted file mode 100644 index 8e3e34a..0000000 --- a/src/interrupt.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::sync::Mutex; - -lazy_static! { - static ref CLEANUP: Mutex>> = Mutex::new(None); -} - -#[cfg(unix)] -pub fn install_handler(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(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(handler: F) - where F: Fn() + 'static + Send + Sync -{ - *CLEANUP.lock().unwrap() = Some(Box::new(handler)); -} diff --git a/src/main.rs b/src/main.rs index 9a7b888..d12b66b 100644 --- a/src/main.rs +++ b/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 { @@ -81,12 +82,22 @@ fn main() { let child_process: Arc>> = 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(); + } + } } } }); diff --git a/src/process.rs b/src/process.rs index f82f1dd..ea23875 100644 --- a/src/process.rs +++ b/src/process.rs @@ -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); diff --git a/src/signal.rs b/src/signal.rs new file mode 100644 index 0000000..867a12d --- /dev/null +++ b/src/signal.rs @@ -0,0 +1,94 @@ +use std::sync::Mutex; + +lazy_static! { + static ref CLEANUP: Mutex>> = Mutex::new(None); +} + +pub enum Signal { + Terminate, + Stop, + Continue +} + +#[cfg(unix)] +pub fn install_handler(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(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(handler: F) + where F: Fn(self::Signal) + 'static + Send + Sync +{ + *CLEANUP.lock().unwrap() = Some(Box::new(handler)); +}