First commit to add a generic --signal flag

This commit is contained in:
Chris Aumann 2017-03-13 18:55:12 +01:00
parent ffc68fb4df
commit 68caf04269
4 changed files with 68 additions and 31 deletions

View File

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

View File

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

View File

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

View File

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