From a2a4e989a2f28ffe47ee479c5c7e6f680a3c920e Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Wed, 4 Mar 2020 01:58:56 +0100 Subject: [PATCH] feat: termination signal manager (resolves #13) --- Cargo.lock | 29 +++++++++++++++-------------- Cargo.toml | 3 ++- src/main.rs | 34 +++++++++++++++------------------- src/signal_manager.rs | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 34 deletions(-) create mode 100644 src/signal_manager.rs diff --git a/Cargo.lock b/Cargo.lock index 9d29a7b..f5dd5a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -157,16 +157,6 @@ dependencies = [ ] [[package]] -name = "ctrlc" -version = "3.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4ba686dff9fa4c1c9636ce1010b0cf98ceb421361b0bb3d6faeec43bd217a7" -dependencies = [ - "nix", - "winapi", -] - -[[package]] name = "env_logger" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -316,7 +306,7 @@ dependencies = [ [[package]] name = "iron_staticfile_middleware" version = "0.2.0" -source = "git+https://github.com/joseluisq/iron-staticfile-middleware.git#562b241a80a7e216ccdbe1d4d603f1a0b621d5b1" +source = "git+https://github.com/joseluisq/iron-staticfile-middleware.git#4e02bc6eda9cf88bf4b7866581ae83efda7f3110" dependencies = [ "iron", "log 0.4.8", @@ -431,9 +421,9 @@ dependencies = [ [[package]] name = "nix" -version = "0.17.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" dependencies = [ "bitflags", "cc", @@ -863,6 +853,16 @@ dependencies = [ ] [[package]] +name = "signal" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f6ce83b159ab6984d2419f495134972b48754d13ff2e3f8c998339942b56ed9" +dependencies = [ + "libc", + "nix", +] + +[[package]] name = "siphasher" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -879,14 +879,15 @@ name = "static-web-server" version = "1.5.0" dependencies = [ "chrono", - "ctrlc", "env_logger", "flate2", "hyper-native-tls", "iron", "iron_staticfile_middleware", "log 0.4.8", + "nix", "openssl", + "signal", "structopt", ] diff --git a/Cargo.toml b/Cargo.toml index 976047d..50ad453 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,8 @@ structopt = "0.3" flate2 = "1.0" iron_staticfile_middleware = { git = "https://github.com/joseluisq/iron-staticfile-middleware.git" } hyper-native-tls = "0.3" -ctrlc = "3.1" +nix = "0.14" +signal = "0.7" [dev-dependencies] openssl = { version = "0.10", features = ["vendored"] } diff --git a/src/main.rs b/src/main.rs index c9091bb..08c6c61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,11 @@ extern crate chrono; -extern crate ctrlc; extern crate env_logger; extern crate flate2; extern crate hyper_native_tls; extern crate iron; extern crate iron_staticfile_middleware; +extern crate nix; +extern crate signal; #[macro_use] extern crate log; @@ -14,6 +15,7 @@ mod config; mod error_page; mod gzip; mod logger; +mod signal_manager; mod staticfiles; use crate::config::Options; @@ -24,21 +26,23 @@ use iron::prelude::*; use log::LevelFilter; use staticfiles::*; use std::io::Write; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; use structopt::StructOpt; -fn on_server_running(server_name: &str, proto: &str, addr: &str, running: Arc) { +fn on_server_running(server_name: &str, proto: &str, addr: &str) { + // Notify when server is running info!( "Static {} Server `{}` is running on {}", proto, server_name, addr ); - println!("Waiting for Ctrl-C signal..."); - while running.load(Ordering::SeqCst) {} + // Wait for incoming signals (E.g Ctrl+C (SIGINT), SIGTERM, etc + signal_manager::wait_for_signal(|sig: signal::Signal| { + let code = signal_manager::signal_to_int(sig); - println!("Exiting server execution..."); - std::process::exit(0) + println!(); + warn!("SIGINT {} caught. HTTP Server execution exited.", code); + std::process::exit(code) + }) } fn main() { @@ -59,16 +63,8 @@ fn main() { let addr = &format!("{}{}{}", opts.host.to_string(), ":", opts.port.to_string()); let proto = if opts.tls { "HTTPS" } else { "HTTP" }; - // Handle Ctrl+C interrupt signals - let running = Arc::new(AtomicBool::new(true)); - let r = running.clone(); - - ctrlc::set_handler(move || { - r.store(false, Ordering::SeqCst); - }) - .expect("Error setting Ctrl-C handler"); - // Configure & launch the HTTP server + let files = StaticFiles::new(StaticFilesOptions { root_dir: opts.root, assets_dir: opts.assets, @@ -80,12 +76,12 @@ fn main() { let ssl = NativeTlsServer::new(opts.tls_pkcs12, &opts.tls_pkcs12_passwd).unwrap(); match Iron::new(files.handle()).https(addr, ssl) { - Result::Ok(_) => on_server_running(&opts.name, &proto, addr, running), + Result::Ok(_) => on_server_running(&opts.name, &proto, addr), Result::Err(err) => panic!("{:?}", err), } } else { match Iron::new(files.handle()).http(addr) { - Result::Ok(_) => on_server_running(&opts.name, &proto, addr, running), + Result::Ok(_) => on_server_running(&opts.name, &proto, addr), Result::Err(err) => panic!("{:?}", err), } } diff --git a/src/signal_manager.rs b/src/signal_manager.rs new file mode 100644 index 0000000..06be40c --- /dev/null +++ b/src/signal_manager.rs @@ -0,0 +1,56 @@ +use nix::errno::Errno; +use nix::libc::c_int; +use nix::sys::signal::{SIGCHLD, SIGINT, SIGTERM}; +use nix::sys::wait::WaitStatus::{Exited, Signaled, StillAlive}; +use nix::sys::wait::{waitpid, WaitPidFlag}; +use nix::Error; +use signal::trap::Trap; + +/// It waits for an incoming Termination Signal like Ctrl+C (SIGINT), SIGTERM, etc +pub fn wait_for_signal(f: F) +where + F: Fn(signal::Signal), +{ + let signal_trap = Trap::trap(&[SIGTERM, SIGINT, SIGCHLD]); + + for sig in signal_trap { + match sig { + SIGCHLD => { + // Current std::process::Command ip does not have a way to find + // process id, so we just wait until we have no children + loop { + match waitpid(None, Some(WaitPidFlag::WNOHANG)) { + Ok(Exited(pid, status)) => { + println!("{} exited with status {}", pid, status); + continue; + } + Ok(Signaled(pid, sig, _)) => { + println!("{} killed by {}", pid, sig as c_int); + continue; + } + Ok(StillAlive) => { + break; + } + Ok(status) => { + println!("Temporary status {:?}", status); + continue; + } + Err(Error::Sys(Errno::ECHILD)) => { + return; + } + Err(e) => { + panic!("Error {:?}", e); + } + } + } + } + + sig => f(sig), + } + } +} + +/// It casts a `signal::Signal` to `i32` +pub fn signal_to_int(sig: signal::Signal) -> i32 { + sig as c_int +} -- libgit2 1.7.2