From 6ed3fe5250129feba03211188d6901a87a5a0c1e Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Fri, 30 Apr 2021 12:37:56 +0200 Subject: [PATCH] refactor: signals handling --- Cargo.lock | 47 ++++++++++++++++++++++++++++++++++++----------- Cargo.toml | 8 +++++++- src/lib.rs | 1 + src/server.rs | 58 +++++++++++++++++++++++++++++++++++----------------------- src/signals.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 35 deletions(-) create mode 100644 src/signals.rs diff --git a/Cargo.lock b/Cargo.lock index 1d8ce00..79aaa67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,12 @@ checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" [[package]] name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" @@ -332,7 +338,7 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", ] [[package]] @@ -389,6 +395,19 @@ dependencies = [ ] [[package]] +name = "nix" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] name = "ntapi" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -573,7 +592,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfebf75d25bd900fd1e7d11501efab59bc846dbc76196839663e6637bba9f25f" dependencies = [ "block-buffer", - "cfg-if", + "cfg-if 1.0.0", "cpuid-bool", "digest", "opaque-debug", @@ -589,12 +608,13 @@ dependencies = [ ] [[package]] -name = "signal-hook-registry" -version = "1.3.0" +name = "signal" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +checksum = "2f6ce83b159ab6984d2419f495134972b48754d13ff2e3f8c998339942b56ed9" dependencies = [ "libc", + "nix", ] [[package]] @@ -624,9 +644,11 @@ dependencies = [ "hyper", "jemallocator", "mime_guess", + "nix", "num_cpus", "once_cell", "percent-encoding", + "signal", "structopt", "tokio", "tokio-util", @@ -710,11 +732,8 @@ dependencies = [ "memchr", "mio", "num_cpus", - "once_cell", "pin-project-lite", - "signal-hook-registry", "tokio-macros", - "winapi", ] [[package]] @@ -754,7 +773,7 @@ version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -858,9 +877,9 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "version_check" @@ -869,6 +888,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] name = "want" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 3e393ce..25a0772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ path = "src/bin/server.rs" [dependencies] hyper = { version = "0.14", features = ["stream", "http1", "tcp", "server"] } -tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal", "fs", "io-util"], default-features = false } +tokio = { version = "1", features = ["rt-multi-thread", "macros", "fs", "io-util"], default-features = false } futures = { version = "0.3", default-features = false } headers = "0.3" tokio-util = { version = "0.6", features = ["io"] } @@ -40,6 +40,12 @@ structopt = { version = "0.3", default-features = false } num_cpus = { version = "1.13" } once_cell = "1.7" +[target.'cfg(not(windows))'.dependencies.nix] +version = "0.14" + +[target.'cfg(not(windows))'.dependencies.signal] +version = "0.7" + [target.'cfg(all(target_env = "musl", target_pointer_width = "64"))'.dependencies.jemallocator] version = "0.3" diff --git a/src/lib.rs b/src/lib.rs index b2bc1a3..21caaec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ pub mod fs; pub mod helpers; pub mod logger; pub mod server; +pub mod signals; #[macro_use] pub mod error; diff --git a/src/server.rs b/src/server.rs index c18aaba..82946a9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -3,7 +3,6 @@ use hyper::service::{make_service_fn, service_fn}; use std::net::{IpAddr, SocketAddr}; use std::sync::Arc; use structopt::StructOpt; -use tokio::signal; use crate::{ config::{Config, CONFIG}, @@ -76,30 +75,26 @@ impl Server { // TODO: HTTP/2 + TLS - // Spawn a new Tokio asynchronous server task determined by the given options - - let span = tracing::info_span!("Server::run", ?addr, threads = ?self.threads); - tracing::info!(parent: &span, "listening on http://{}", addr); + // Spawn a new Tokio asynchronous server task with its given options + tokio::task::spawn(async move { + let span = tracing::info_span!("Server::run", ?addr, threads = ?self.threads); + tracing::info!(parent: &span, "listening on http://{}", addr); + + let root_dir = ArcPath(Arc::new(root_dir)); + let create_service = make_service_fn(move |_| { + let root_dir = root_dir.clone(); + async move { + Ok::<_, error::Error>(service_fn(move |req| { + let root_dir = root_dir.clone(); + async move { handle(root_dir.as_ref(), req).await } + })) + } + }); - let root_dir = ArcPath(Arc::new(root_dir)); - let create_service = make_service_fn(move |_| { - let root_dir = root_dir.clone(); - async move { - Ok::<_, error::Error>(service_fn(move |req| { - let root_dir = root_dir.clone(); - async move { handle(root_dir.as_ref(), req).await } - })) - } + HyperServer::bind(&addr).serve(create_service).await }); - HyperServer::bind(&addr) - .serve(create_service) - .with_graceful_shutdown(async { - signal::ctrl_c() - .await - .expect("failed to install CTRL+C signal handler"); - tracing::warn!(parent: &span, "CTRL+C signal caught and execution exited"); - }) - .await?; + + handle_signals(); Ok(()) } @@ -110,3 +105,20 @@ impl Default for Server { Self::new() } } + +#[cfg(not(windows))] +/// Handle incoming signals for Unix-like OS's only +fn handle_signals() { + use crate::signals; + + signals::wait(|sig: signals::Signal| { + let code = signals::as_int(sig); + tracing::warn!("Signal {} caught. Server execution exited.", code); + std::process::exit(code) + }); +} + +#[cfg(windows)] +fn handle_signals() { + // TODO: Windows signals... +} diff --git a/src/signals.rs b/src/signals.rs new file mode 100644 index 0000000..6947b11 --- /dev/null +++ b/src/signals.rs @@ -0,0 +1,51 @@ +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; + +pub use signal::Signal; + +/// It waits for an incoming Termination Signal like Ctrl+C (SIGINT), SIGTERM, etc +pub fn wait(func: F) +where + F: Fn(signal::Signal), +{ + let sig_trap = signal::trap::Trap::trap(&[SIGTERM, SIGINT, SIGCHLD]); + for sig in sig_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 => func(sig), + } + } +} + +/// It casts a given `signal::Signal` to `i32`. +pub fn as_int(sig: signal::Signal) -> i32 { + sig as c_int +} -- libgit2 1.7.2