signals: Move signal handling to its own crate

We're going to share this code with `radicle-term`.
This commit is contained in:
cloudhead 2024-05-03 12:15:16 +02:00
parent 425e7b5092
commit 2801403182
No known key found for this signature in database
8 changed files with 71 additions and 13 deletions

9
Cargo.lock generated
View File

@ -2551,6 +2551,7 @@ dependencies = [
"radicle-crypto", "radicle-crypto",
"radicle-fetch", "radicle-fetch",
"radicle-git-ext", "radicle-git-ext",
"radicle-signals",
"scrypt", "scrypt",
"serde", "serde",
"serde_json", "serde_json",
@ -2572,6 +2573,14 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "radicle-signals"
version = "0.9.0"
dependencies = [
"crossbeam-channel",
"libc",
]
[[package]] [[package]]
name = "radicle-ssh" name = "radicle-ssh"
version = "0.9.0" version = "0.9.0"

View File

@ -14,6 +14,7 @@ members = [
"radicle-remote-helper", "radicle-remote-helper",
"radicle-ssh", "radicle-ssh",
"radicle-tools", "radicle-tools",
"radicle-signals",
] ]
default-members = [ default-members = [
"radicle", "radicle",
@ -25,6 +26,7 @@ default-members = [
"radicle-ssh", "radicle-ssh",
"radicle-remote-helper", "radicle-remote-helper",
"radicle-term", "radicle-term",
"radicle-signals",
] ]
resolver = "2" resolver = "2"

View File

@ -50,6 +50,10 @@ features = ["logger"]
path = "../radicle-fetch" path = "../radicle-fetch"
version = "0.9.0" version = "0.9.0"
[dependencies.radicle-signals]
path = "../radicle-signals"
version = "0"
[dev-dependencies] [dev-dependencies]
radicle = { path = "../radicle", version = "0", features = ["test"] } radicle = { path = "../radicle", version = "0", features = ["test"] }
radicle-crypto = { path = "../radicle-crypto", version = "0", features = ["test", "cyphernet"] } radicle-crypto = { path = "../radicle-crypto", version = "0", features = ["test", "cyphernet"] }

View File

@ -3,7 +3,6 @@ pub mod control;
pub mod deserializer; pub mod deserializer;
pub mod runtime; pub mod runtime;
pub mod service; pub mod service;
pub mod signals;
#[cfg(any(test, feature = "test"))] #[cfg(any(test, feature = "test"))]
pub mod test; pub mod test;
#[cfg(test)] #[cfg(test)]

View File

@ -9,8 +9,8 @@ use radicle::prelude::Signer;
use radicle::profile; use radicle::profile;
use radicle::version::Version; use radicle::version::Version;
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner}; use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
use radicle_node::signals;
use radicle_node::Runtime; use radicle_node::Runtime;
use radicle_signals as signals;
pub const VERSION: Version = Version { pub const VERSION: Version = Version {
name: env!("CARGO_PKG_NAME"), name: env!("CARGO_PKG_NAME"),

View File

@ -10,6 +10,7 @@ use crossbeam_channel as chan;
use cyphernet::Ecdh; use cyphernet::Ecdh;
use netservices::resource::NetAccept; use netservices::resource::NetAccept;
use radicle_fetch::FetchLimit; use radicle_fetch::FetchLimit;
use radicle_signals::Signal;
use reactor::poller::popol; use reactor::poller::popol;
use reactor::Reactor; use reactor::Reactor;
use thiserror::Error; use thiserror::Error;
@ -128,7 +129,7 @@ pub struct Runtime {
pub reactor: Reactor<wire::Control, popol::Poller>, pub reactor: Reactor<wire::Control, popol::Poller>,
pub pool: worker::Pool, pub pool: worker::Pool,
pub local_addrs: Vec<net::SocketAddr>, pub local_addrs: Vec<net::SocketAddr>,
pub signals: chan::Receiver<()>, pub signals: chan::Receiver<Signal>,
} }
impl Runtime { impl Runtime {
@ -139,7 +140,7 @@ impl Runtime {
home: Home, home: Home,
config: service::Config, config: service::Config,
listen: Vec<net::SocketAddr>, listen: Vec<net::SocketAddr>,
signals: chan::Receiver<()>, signals: chan::Receiver<Signal>,
signer: G, signer: G,
) -> Result<Runtime, Error> ) -> Result<Runtime, Error>
where where
@ -306,7 +307,7 @@ impl Runtime {
|| control::listen(self.control, handle) || control::listen(self.control, handle)
}); });
let _signals = thread::spawn(&self.id, "signals", move || { 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.."); log::info!(target: "node", "Termination signal received; shutting down..");
self.handle.shutdown().ok(); self.handle.shutdown().ok();
} }

View File

@ -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" }

View File

@ -3,11 +3,38 @@ use std::sync::Mutex;
use crossbeam_channel as chan; use crossbeam_channel as chan;
/// Signal notifications are sent via this channel. /// Operating system signal.
static NOTIFY: Mutex<Option<chan::Sender<()>>> = Mutex::new(None); #[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`. impl TryFrom<i32> for Signal {
pub fn install(notify: chan::Sender<()>) -> io::Result<()> { type Error = i32;
fn try_from(value: i32) -> Result<Self, Self::Error> {
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<Option<chan::Sender<Signal>>> = Mutex::new(None);
/// Install global signal handlers.
pub fn install(notify: chan::Sender<Signal>) -> io::Result<()> {
if let Ok(mut channel) = NOTIFY.try_lock() { if let Ok(mut channel) = NOTIFY.try_lock() {
if channel.is_some() { if channel.is_some() {
return Err(io::Error::new( return Err(io::Error::new(
@ -27,7 +54,7 @@ pub fn install(notify: chan::Sender<()>) -> io::Result<()> {
Ok(()) Ok(())
} }
/// Install global signal handlers for `SIGTERM` and `SIGINT`. /// Install global signal handlers.
/// ///
/// # Safety /// # Safety
/// ///
@ -39,17 +66,23 @@ unsafe fn _install() -> io::Result<()> {
if libc::signal(libc::SIGINT, handler as libc::sighandler_t) == libc::SIG_ERR { if libc::signal(libc::SIGINT, handler as libc::sighandler_t) == libc::SIG_ERR {
return Err(io::Error::last_os_error()); 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(()) Ok(())
} }
/// Called by `libc` when a signal is received. /// 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) { 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; return;
} };
if let Ok(guard) = NOTIFY.try_lock() { if let Ok(guard) = NOTIFY.try_lock() {
if let Some(c) = &*guard { if let Some(c) = &*guard {
c.try_send(()).ok(); c.try_send(sig).ok();
} }
} }
} }