diff --git a/src/cli.rs b/src/cli.rs index 3e9e906..3fe0281 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -10,7 +10,7 @@ pub struct Args { pub filters: Vec, pub ignores: Vec, pub clear_screen: bool, - pub kill: bool, + pub signal: String, pub restart: bool, pub debug: bool, pub run_initially: bool, @@ -60,6 +60,13 @@ pub fn get_args() -> Args { .help("Restart the process if it's still running") .short("r") .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") .help("Print debugging messages to stderr") .short("d") @@ -91,10 +98,6 @@ pub fn get_args() -> Args { .help("Forces polling mode") .long("force-poll") .value_name("interval")) - .arg(Arg::with_name("kill") - .help("Send SIGKILL to child processes") - .short("k") - .long("kill")) .arg(Arg::with_name("once") .short("1") .hidden(true)) @@ -103,6 +106,11 @@ pub fn get_args() -> Args { 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(".")]); + // 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![]); if let Some(extensions) = args.values_of("extensions") { @@ -134,8 +142,8 @@ pub fn get_args() -> Args { paths: paths, filters: filters, ignores: ignores, + signal: signal, clear_screen: args.is_present("clear"), - kill: args.is_present("kill"), restart: args.is_present("restart"), debug: args.is_present("debug"), run_initially: !args.is_present("postpone"), diff --git a/src/main.rs b/src/main.rs index 2690062..ac87551 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,19 +54,20 @@ fn main() { let args = cli::get_args(); let child_process: Arc>> = Arc::new(RwLock::new(None)); 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| { if let Some(lock) = weak_child.upgrade() { let strong = lock.read().unwrap(); if let Some(ref child) = *strong { match sig { + // TODO: This should be generalized to use new --signal flag + // TODO: Not sure what this is doing tbh :( Signal::Terminate => { - if kill { - child.kill(); - } else { - child.terminate(); - } + // TODO: Removed kill variable for now + child.terminate(); } Signal::Stop => child.pause(), Signal::Continue => child.resume(), @@ -123,7 +124,8 @@ fn main() { } // 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 if args.clear_screen { @@ -138,7 +140,8 @@ fn main() { // Handle once option for integration testing if args.once { - wait_process(&child_process, kill, false); + // Note: signal is cloned here automatically + wait_process(&child_process, signal, false); break; } } @@ -188,17 +191,13 @@ fn wait_fs(rx: &Receiver, filter: &NotificationFilter) -> Vec { paths } -fn wait_process(process: &RwLock>, kill: bool, restart: bool) { +fn wait_process(process: &RwLock>, signal: signal::Signal, restart: bool) { let guard = process.read().unwrap(); if let Some(ref child) = *guard { if restart { - debug!("Stopping child process"); - if kill { - child.kill(); - } else { - child.terminate(); - } + debug!("Stopping child process with {} signal", signal); + child.signal(signal); } debug!("Waiting for process to exit..."); diff --git a/src/process.rs b/src/process.rs index 2cef8f7..5efba17 100644 --- a/src/process.rs +++ b/src/process.rs @@ -13,6 +13,7 @@ mod imp { use std::path::PathBuf; use std::process::Command; use std::sync::*; + use signal::Signal; pub struct Process { pgid: pid_t, @@ -51,12 +52,14 @@ mod imp { }) } + // TODO: Required? pub fn kill(&self) { - self.signal(SIGKILL); + self.c_signal(SIGKILL); } + // TODO: Required? pub fn pause(&self) { - self.signal(SIGTSTP); + self.c_signal(SIGTSTP); } 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) { - 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" { fn killpg(pgrp: pid_t, sig: c_int) -> c_int; } @@ -97,8 +107,9 @@ mod imp { } + // TODO: Is this required? pub fn terminate(&self) { - self.signal(SIGTERM); + self.c_signal(SIGTERM); } pub fn wait(&self) { diff --git a/src/signal.rs b/src/signal.rs index be6c7c9..88468ea 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -1,15 +1,31 @@ use std::sync::Mutex; +use std::fmt; lazy_static! { static ref CLEANUP: Mutex>> = 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 { - Terminate, - Stop, - Continue, - ChildExit, + // TODO: Probably a good idea to use original names here: + // TODO: Add SIGUSR1+2 SIGHUP here? + Terminate, // SIGTERM + 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)] @@ -25,9 +41,12 @@ pub fn install_handler(handler: F) let mut mask = SigSet::empty(); mask.add(SIGTERM); mask.add(SIGINT); + // mask.add(SIGHUP); mask.add(SIGTSTP); mask.add(SIGCONT); mask.add(SIGCHLD); + // mask.add(SIGUSR1); + // mask.add(SIGUSR2); mask.thread_set_mask().expect("unable to set signal mask"); set_handler(handler);