mirror of
https://github.com/watchexec/watchexec.git
synced 2024-09-29 22:51:33 +02:00
First commit to add a generic --signal flag
This commit is contained in:
parent
ffc68fb4df
commit
68caf04269
20
src/cli.rs
20
src/cli.rs
@ -10,7 +10,7 @@ pub struct Args {
|
|||||||
pub filters: Vec<String>,
|
pub filters: Vec<String>,
|
||||||
pub ignores: Vec<String>,
|
pub ignores: Vec<String>,
|
||||||
pub clear_screen: bool,
|
pub clear_screen: bool,
|
||||||
pub kill: bool,
|
pub signal: String,
|
||||||
pub restart: bool,
|
pub restart: bool,
|
||||||
pub debug: bool,
|
pub debug: bool,
|
||||||
pub run_initially: bool,
|
pub run_initially: bool,
|
||||||
@ -60,6 +60,13 @@ pub fn get_args() -> Args {
|
|||||||
.help("Restart the process if it's still running")
|
.help("Restart the process if it's still running")
|
||||||
.short("r")
|
.short("r")
|
||||||
.long("restart"))
|
.long("restart"))
|
||||||
|
.arg(Arg::with_name("signal") // TODO: --signal only makes sense when used with --restart
|
||||||
|
.help("Signal to send when --restart is used, defaults to SIGTERM")
|
||||||
|
.short("s")
|
||||||
|
.long("signal")
|
||||||
|
.takes_value(true)
|
||||||
|
.number_of_values(1)
|
||||||
|
.value_name("signal"))
|
||||||
.arg(Arg::with_name("debug")
|
.arg(Arg::with_name("debug")
|
||||||
.help("Print debugging messages to stderr")
|
.help("Print debugging messages to stderr")
|
||||||
.short("d")
|
.short("d")
|
||||||
@ -91,10 +98,6 @@ pub fn get_args() -> Args {
|
|||||||
.help("Forces polling mode")
|
.help("Forces polling mode")
|
||||||
.long("force-poll")
|
.long("force-poll")
|
||||||
.value_name("interval"))
|
.value_name("interval"))
|
||||||
.arg(Arg::with_name("kill")
|
|
||||||
.help("Send SIGKILL to child processes")
|
|
||||||
.short("k")
|
|
||||||
.long("kill"))
|
|
||||||
.arg(Arg::with_name("once")
|
.arg(Arg::with_name("once")
|
||||||
.short("1")
|
.short("1")
|
||||||
.hidden(true))
|
.hidden(true))
|
||||||
@ -103,6 +106,11 @@ pub fn get_args() -> Args {
|
|||||||
let cmd = values_t!(args.values_of("command"), String).unwrap().join(" ");
|
let cmd = values_t!(args.values_of("command"), String).unwrap().join(" ");
|
||||||
let paths = values_t!(args.values_of("path"), String).unwrap_or(vec![String::from(".")]);
|
let paths = values_t!(args.values_of("path"), String).unwrap_or(vec![String::from(".")]);
|
||||||
|
|
||||||
|
// TODO: I suppose there must be a better way of getting a string and a default value in clap
|
||||||
|
let signal = values_t!(args.values_of("signal"), String)
|
||||||
|
.unwrap_or(vec![String::from("SIGTERM")]) // TODO: Use SIGHUP as default?
|
||||||
|
.join(" ");
|
||||||
|
|
||||||
let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(vec![]);
|
let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(vec![]);
|
||||||
|
|
||||||
if let Some(extensions) = args.values_of("extensions") {
|
if let Some(extensions) = args.values_of("extensions") {
|
||||||
@ -134,8 +142,8 @@ pub fn get_args() -> Args {
|
|||||||
paths: paths,
|
paths: paths,
|
||||||
filters: filters,
|
filters: filters,
|
||||||
ignores: ignores,
|
ignores: ignores,
|
||||||
|
signal: signal,
|
||||||
clear_screen: args.is_present("clear"),
|
clear_screen: args.is_present("clear"),
|
||||||
kill: args.is_present("kill"),
|
|
||||||
restart: args.is_present("restart"),
|
restart: args.is_present("restart"),
|
||||||
debug: args.is_present("debug"),
|
debug: args.is_present("debug"),
|
||||||
run_initially: !args.is_present("postpone"),
|
run_initially: !args.is_present("postpone"),
|
||||||
|
27
src/main.rs
27
src/main.rs
@ -54,20 +54,21 @@ fn main() {
|
|||||||
let args = cli::get_args();
|
let args = cli::get_args();
|
||||||
let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
|
let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
|
||||||
let weak_child = Arc::downgrade(&child_process);
|
let weak_child = Arc::downgrade(&child_process);
|
||||||
let kill = args.kill;
|
|
||||||
|
// Convert signal string to the corresponding integer
|
||||||
|
let signal = signal::new(&*args.signal);
|
||||||
|
|
||||||
signal::install_handler(move |sig: Signal| {
|
signal::install_handler(move |sig: Signal| {
|
||||||
if let Some(lock) = weak_child.upgrade() {
|
if let Some(lock) = weak_child.upgrade() {
|
||||||
let strong = lock.read().unwrap();
|
let strong = lock.read().unwrap();
|
||||||
if let Some(ref child) = *strong {
|
if let Some(ref child) = *strong {
|
||||||
match sig {
|
match sig {
|
||||||
|
// TODO: This should be generalized to use new --signal flag
|
||||||
|
// TODO: Not sure what this is doing tbh :(
|
||||||
Signal::Terminate => {
|
Signal::Terminate => {
|
||||||
if kill {
|
// TODO: Removed kill variable for now
|
||||||
child.kill();
|
|
||||||
} else {
|
|
||||||
child.terminate();
|
child.terminate();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Signal::Stop => child.pause(),
|
Signal::Stop => child.pause(),
|
||||||
Signal::Continue => child.resume(),
|
Signal::Continue => child.resume(),
|
||||||
Signal::ChildExit => child.reap(),
|
Signal::ChildExit => child.reap(),
|
||||||
@ -123,7 +124,8 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Wait for current child process to exit
|
// Wait for current child process to exit
|
||||||
wait_process(&child_process, kill, args.restart);
|
// Note: signal is cloned here automatically
|
||||||
|
wait_process(&child_process, signal, args.restart);
|
||||||
|
|
||||||
// Launch child process
|
// Launch child process
|
||||||
if args.clear_screen {
|
if args.clear_screen {
|
||||||
@ -138,7 +140,8 @@ fn main() {
|
|||||||
|
|
||||||
// Handle once option for integration testing
|
// Handle once option for integration testing
|
||||||
if args.once {
|
if args.once {
|
||||||
wait_process(&child_process, kill, false);
|
// Note: signal is cloned here automatically
|
||||||
|
wait_process(&child_process, signal, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,17 +191,13 @@ fn wait_fs(rx: &Receiver<Event>, filter: &NotificationFilter) -> Vec<PathBuf> {
|
|||||||
paths
|
paths
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_process(process: &RwLock<Option<Process>>, kill: bool, restart: bool) {
|
fn wait_process(process: &RwLock<Option<Process>>, signal: signal::Signal, restart: bool) {
|
||||||
let guard = process.read().unwrap();
|
let guard = process.read().unwrap();
|
||||||
|
|
||||||
if let Some(ref child) = *guard {
|
if let Some(ref child) = *guard {
|
||||||
if restart {
|
if restart {
|
||||||
debug!("Stopping child process");
|
debug!("Stopping child process with {} signal", signal);
|
||||||
if kill {
|
child.signal(signal);
|
||||||
child.kill();
|
|
||||||
} else {
|
|
||||||
child.terminate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Waiting for process to exit...");
|
debug!("Waiting for process to exit...");
|
||||||
|
@ -13,6 +13,7 @@ mod imp {
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::sync::*;
|
use std::sync::*;
|
||||||
|
use signal::Signal;
|
||||||
|
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
pgid: pid_t,
|
pgid: pid_t,
|
||||||
@ -51,12 +52,14 @@ mod imp {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Required?
|
||||||
pub fn kill(&self) {
|
pub fn kill(&self) {
|
||||||
self.signal(SIGKILL);
|
self.c_signal(SIGKILL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Required?
|
||||||
pub fn pause(&self) {
|
pub fn pause(&self) {
|
||||||
self.signal(SIGTSTP);
|
self.c_signal(SIGTSTP);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reap(&self) {
|
pub fn reap(&self) {
|
||||||
@ -82,11 +85,18 @@ mod imp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Is this required? - This can probably be streamlined with just using --signal SIGCONT
|
||||||
pub fn resume(&self) {
|
pub fn resume(&self) {
|
||||||
self.signal(SIGCONT);
|
self.c_signal(SIGCONT);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signal(&self, sig: c_int) {
|
pub fn signal(&self, signal: Signal) {
|
||||||
|
// TODO: Sending dummy signal for now
|
||||||
|
println!("DEBUG: {}", signal);
|
||||||
|
self.c_signal(SIGCONT);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn c_signal(&self, sig: c_int) {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn killpg(pgrp: pid_t, sig: c_int) -> c_int;
|
fn killpg(pgrp: pid_t, sig: c_int) -> c_int;
|
||||||
}
|
}
|
||||||
@ -97,8 +107,9 @@ mod imp {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Is this required?
|
||||||
pub fn terminate(&self) {
|
pub fn terminate(&self) {
|
||||||
self.signal(SIGTERM);
|
self.c_signal(SIGTERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait(&self) {
|
pub fn wait(&self) {
|
||||||
|
@ -1,15 +1,31 @@
|
|||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CLEANUP: Mutex<Option<Box<Fn(self::Signal) + Send>>> = Mutex::new(None);
|
static ref CLEANUP: Mutex<Option<Box<Fn(self::Signal) + Send>>> = Mutex::new(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
// TODO: Probably a good idea to use
|
||||||
|
// https://nix-rust.github.io/nix/nix/sys/signal/enum.Signal.html
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Signal {
|
pub enum Signal {
|
||||||
Terminate,
|
// TODO: Probably a good idea to use original names here:
|
||||||
Stop,
|
// TODO: Add SIGUSR1+2 SIGHUP here?
|
||||||
Continue,
|
Terminate, // SIGTERM
|
||||||
ChildExit,
|
Stop, // SIGTSTP
|
||||||
|
Continue, // SIGCONT
|
||||||
|
ChildExit, // SIGCHLD
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Signal {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{:?}", self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(signal_name: &str) -> Signal {
|
||||||
|
println!("Using signal {}", signal_name);
|
||||||
|
Signal::Terminate
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@ -25,9 +41,12 @@ pub fn install_handler<F>(handler: F)
|
|||||||
let mut mask = SigSet::empty();
|
let mut mask = SigSet::empty();
|
||||||
mask.add(SIGTERM);
|
mask.add(SIGTERM);
|
||||||
mask.add(SIGINT);
|
mask.add(SIGINT);
|
||||||
|
// mask.add(SIGHUP);
|
||||||
mask.add(SIGTSTP);
|
mask.add(SIGTSTP);
|
||||||
mask.add(SIGCONT);
|
mask.add(SIGCONT);
|
||||||
mask.add(SIGCHLD);
|
mask.add(SIGCHLD);
|
||||||
|
// mask.add(SIGUSR1);
|
||||||
|
// mask.add(SIGUSR2);
|
||||||
mask.thread_set_mask().expect("unable to set signal mask");
|
mask.thread_set_mask().expect("unable to set signal mask");
|
||||||
|
|
||||||
set_handler(handler);
|
set_handler(handler);
|
||||||
|
Loading…
Reference in New Issue
Block a user