Propagate SIGTSTP/SIGCONT to children

This commit is contained in:
Matt Green 2016-12-14 20:19:58 -05:00
parent ef37a9fb97
commit 154c23a5a4
6 changed files with 145 additions and 89 deletions

14
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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));
}

View File

@ -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();
}
}
}
}
});

View File

@ -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);

94
src/signal.rs Normal file
View File

@ -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));
}