diff --git a/Cargo.lock b/Cargo.lock index 70c3c4c9..5f7ca9f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2551,6 +2551,7 @@ dependencies = [ "radicle-crypto", "radicle-fetch", "radicle-git-ext", + "radicle-signals", "scrypt", "serde", "serde_json", @@ -2572,6 +2573,14 @@ dependencies = [ "thiserror", ] +[[package]] +name = "radicle-signals" +version = "0.9.0" +dependencies = [ + "crossbeam-channel", + "libc", +] + [[package]] name = "radicle-ssh" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index d08e756c..f9b60eec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ members = [ "radicle-remote-helper", "radicle-ssh", "radicle-tools", + "radicle-signals", ] default-members = [ "radicle", @@ -25,6 +26,7 @@ default-members = [ "radicle-ssh", "radicle-remote-helper", "radicle-term", + "radicle-signals", ] resolver = "2" diff --git a/radicle-node/Cargo.toml b/radicle-node/Cargo.toml index 5cf5d48f..c314e132 100644 --- a/radicle-node/Cargo.toml +++ b/radicle-node/Cargo.toml @@ -50,6 +50,10 @@ features = ["logger"] path = "../radicle-fetch" version = "0.9.0" +[dependencies.radicle-signals] +path = "../radicle-signals" +version = "0" + [dev-dependencies] radicle = { path = "../radicle", version = "0", features = ["test"] } radicle-crypto = { path = "../radicle-crypto", version = "0", features = ["test", "cyphernet"] } diff --git a/radicle-node/src/lib.rs b/radicle-node/src/lib.rs index af262320..0d6f7083 100644 --- a/radicle-node/src/lib.rs +++ b/radicle-node/src/lib.rs @@ -3,7 +3,6 @@ pub mod control; pub mod deserializer; pub mod runtime; pub mod service; -pub mod signals; #[cfg(any(test, feature = "test"))] pub mod test; #[cfg(test)] diff --git a/radicle-node/src/main.rs b/radicle-node/src/main.rs index 3f4e8c87..cc99ea94 100644 --- a/radicle-node/src/main.rs +++ b/radicle-node/src/main.rs @@ -9,8 +9,8 @@ use radicle::prelude::Signer; use radicle::profile; use radicle::version::Version; use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner}; -use radicle_node::signals; use radicle_node::Runtime; +use radicle_signals as signals; pub const VERSION: Version = Version { name: env!("CARGO_PKG_NAME"), diff --git a/radicle-node/src/runtime.rs b/radicle-node/src/runtime.rs index 3b55ac68..d650e572 100644 --- a/radicle-node/src/runtime.rs +++ b/radicle-node/src/runtime.rs @@ -10,6 +10,7 @@ use crossbeam_channel as chan; use cyphernet::Ecdh; use netservices::resource::NetAccept; use radicle_fetch::FetchLimit; +use radicle_signals::Signal; use reactor::poller::popol; use reactor::Reactor; use thiserror::Error; @@ -128,7 +129,7 @@ pub struct Runtime { pub reactor: Reactor, pub pool: worker::Pool, pub local_addrs: Vec, - pub signals: chan::Receiver<()>, + pub signals: chan::Receiver, } impl Runtime { @@ -139,7 +140,7 @@ impl Runtime { home: Home, config: service::Config, listen: Vec, - signals: chan::Receiver<()>, + signals: chan::Receiver, signer: G, ) -> Result where @@ -306,7 +307,7 @@ impl Runtime { || control::listen(self.control, handle) }); let _signals = thread::spawn(&self.id, "signals", move || { - if let Ok(()) = self.signals.recv() { + if let Ok(Signal::Terminate | Signal::Interrupt) = self.signals.recv() { log::info!(target: "node", "Termination signal received; shutting down.."); self.handle.shutdown().ok(); } diff --git a/radicle-signals/Cargo.toml b/radicle-signals/Cargo.toml new file mode 100644 index 00000000..af891795 --- /dev/null +++ b/radicle-signals/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "radicle-signals" +homepage = "https://radicle.xyz" +repository = "https://app.radicle.xyz/seeds/seed.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5" +edition = "2021" +version = "0.9.0" + +[dependencies] +crossbeam-channel = { version = "0.5.6" } +libc = { version = "0.2" } diff --git a/radicle-node/src/signals.rs b/radicle-signals/src/lib.rs similarity index 51% rename from radicle-node/src/signals.rs rename to radicle-signals/src/lib.rs index d079b227..98591cd5 100644 --- a/radicle-node/src/signals.rs +++ b/radicle-signals/src/lib.rs @@ -3,11 +3,38 @@ use std::sync::Mutex; use crossbeam_channel as chan; -/// Signal notifications are sent via this channel. -static NOTIFY: Mutex>> = Mutex::new(None); +/// Operating system signal. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Signal { + /// `SIGINT`. + Interrupt, + /// `SIGTERM`. + Terminate, + /// `SIGHUP`. + Hangup, + /// `SIGWINCH`. + WindowChanged, +} -/// Install global signal handlers for `SIGTERM` and `SIGINT`. -pub fn install(notify: chan::Sender<()>) -> io::Result<()> { +impl TryFrom for Signal { + type Error = i32; + + fn try_from(value: i32) -> Result { + match value { + libc::SIGTERM => Ok(Self::Terminate), + libc::SIGINT => Ok(Self::Interrupt), + libc::SIGWINCH => Ok(Self::WindowChanged), + libc::SIGHUP => Ok(Self::Hangup), + _ => Err(value), + } + } +} + +/// Signal notifications are sent via this channel. +static NOTIFY: Mutex>> = Mutex::new(None); + +/// Install global signal handlers. +pub fn install(notify: chan::Sender) -> io::Result<()> { if let Ok(mut channel) = NOTIFY.try_lock() { if channel.is_some() { return Err(io::Error::new( @@ -27,7 +54,7 @@ pub fn install(notify: chan::Sender<()>) -> io::Result<()> { Ok(()) } -/// Install global signal handlers for `SIGTERM` and `SIGINT`. +/// Install global signal handlers. /// /// # Safety /// @@ -39,17 +66,23 @@ unsafe fn _install() -> io::Result<()> { if libc::signal(libc::SIGINT, handler as libc::sighandler_t) == libc::SIG_ERR { return Err(io::Error::last_os_error()); } + if libc::signal(libc::SIGHUP, handler as libc::sighandler_t) == libc::SIG_ERR { + return Err(io::Error::last_os_error()); + } + if libc::signal(libc::SIGWINCH, handler as libc::sighandler_t) == libc::SIG_ERR { + return Err(io::Error::last_os_error()); + } Ok(()) } /// Called by `libc` when a signal is received. extern "C" fn handler(sig: libc::c_int, _info: *mut libc::siginfo_t, _data: *mut libc::c_void) { - if sig != libc::SIGTERM && sig != libc::SIGINT { + let Ok(sig) = sig.try_into() else { return; - } + }; if let Ok(guard) = NOTIFY.try_lock() { if let Some(c) = &*guard { - c.try_send(()).ok(); + c.try_send(sig).ok(); } } }