node: Handle `SIGINT` and `SIGTERM` properly
This ensures that the socket file is cleaned up on exit.
This commit is contained in:
parent
e7c929898a
commit
72c99b7939
|
|
@ -19,13 +19,13 @@ crossbeam-channel = { version = "0.5.6" }
|
|||
cyphernet = { version = "0.1.0", features = ["tor", "dns", "ed25519", "p2p-ed25519"] }
|
||||
fastrand = { version = "1.8.0" }
|
||||
git-ref-format = { version = "0.2", features = ["serde", "macro"] }
|
||||
io-reactor = { version = "0.1.0", features = ["popol"] }
|
||||
lexopt = { version = "0.2.1" }
|
||||
libc = { version = "0.2.137" }
|
||||
log = { version = "0.4.17", features = ["std"] }
|
||||
localtime = { version = "1" }
|
||||
netservices = { version = "0.1.0", features = ["io-reactor", "socket2"] }
|
||||
nonempty = { version = "0.8.1", features = ["serialize"] }
|
||||
io-reactor = { version = "0.1.0", features = ["popol"] }
|
||||
qcheck = { version = "1", default-features = false, optional = true }
|
||||
sqlite = { version = "0.30.3" }
|
||||
sqlite3-src = { version = "0.4.0", features = ["bundled"] } # Ensures static linking
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ pub mod deserializer;
|
|||
pub mod logger;
|
||||
pub mod runtime;
|
||||
pub mod service;
|
||||
pub mod signals;
|
||||
pub mod sql;
|
||||
#[cfg(any(test, feature = "test"))]
|
||||
pub mod test;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use std::{env, net, process};
|
||||
|
||||
use anyhow::Context as _;
|
||||
use crossbeam_channel as chan;
|
||||
use cyphernet::addr::PeerAddr;
|
||||
use localtime::LocalDuration;
|
||||
|
||||
|
|
@ -8,7 +9,7 @@ use radicle::profile;
|
|||
use radicle_node::crypto::ssh::keystore::{Keystore, MemorySigner};
|
||||
use radicle_node::prelude::{Address, NodeId};
|
||||
use radicle_node::Runtime;
|
||||
use radicle_node::{logger, service};
|
||||
use radicle_node::{logger, service, signals};
|
||||
|
||||
pub const HELP_MSG: &str = r#"
|
||||
Usage
|
||||
|
|
@ -117,7 +118,10 @@ fn execute() -> anyhow::Result<()> {
|
|||
)
|
||||
});
|
||||
|
||||
Runtime::init(home, config, options.listen, proxy, daemon, signer)?.run()?;
|
||||
let (notify, signals) = chan::bounded(1);
|
||||
signals::install(notify)?;
|
||||
|
||||
Runtime::init(home, config, options.listen, proxy, daemon, signals, signer)?.run()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use crossbeam_channel as chan;
|
|||
use cyphernet::{Cert, EcSign};
|
||||
use netservices::resource::NetAccept;
|
||||
use radicle::git;
|
||||
use radicle::node::Handle as _;
|
||||
use radicle::profile::Home;
|
||||
use radicle::Storage;
|
||||
use reactor::poller::popol;
|
||||
|
|
@ -77,6 +78,7 @@ pub struct Runtime<G: Signer + EcSign> {
|
|||
pub daemon: net::SocketAddr,
|
||||
pub pool: worker::Pool,
|
||||
pub local_addrs: Vec<net::SocketAddr>,
|
||||
pub signals: chan::Receiver<()>,
|
||||
}
|
||||
|
||||
impl<G: Signer + EcSign + 'static> Runtime<G> {
|
||||
|
|
@ -89,6 +91,7 @@ impl<G: Signer + EcSign + 'static> Runtime<G> {
|
|||
listen: Vec<net::SocketAddr>,
|
||||
proxy: net::SocketAddr,
|
||||
daemon: net::SocketAddr,
|
||||
signals: chan::Receiver<()>,
|
||||
signer: G,
|
||||
) -> Result<Runtime<G>, Error>
|
||||
where
|
||||
|
|
@ -176,6 +179,7 @@ impl<G: Signer + EcSign + 'static> Runtime<G> {
|
|||
daemon,
|
||||
handle,
|
||||
pool,
|
||||
signals,
|
||||
local_addrs,
|
||||
})
|
||||
}
|
||||
|
|
@ -195,9 +199,18 @@ impl<G: Signer + EcSign + 'static> Runtime<G> {
|
|||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
let control = thread::Builder::new()
|
||||
let control = thread::Builder::new().name(self.id.to_human()).spawn({
|
||||
let handle = self.handle.clone();
|
||||
move || control::listen(listener, handle)
|
||||
})?;
|
||||
let _signals = thread::Builder::new()
|
||||
.name(self.id.to_human())
|
||||
.spawn(move || control::listen(listener, self.handle))?;
|
||||
.spawn(move || {
|
||||
if let Ok(()) = self.signals.recv() {
|
||||
log::info!(target: "node", "Termination signal received; shutting down..");
|
||||
self.handle.shutdown().ok();
|
||||
}
|
||||
})?;
|
||||
|
||||
log::info!(target: "node", "Spawning git daemon at {}..", self.storage.path().display());
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
use std::io;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crossbeam_channel as chan;
|
||||
|
||||
/// Signal notifications are sent via this channel.
|
||||
static NOTIFY: Mutex<Option<chan::Sender<()>>> = Mutex::new(None);
|
||||
|
||||
/// Install global signal handlers for `SIGTERM` and `SIGINT`.
|
||||
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(
|
||||
io::ErrorKind::AlreadyExists,
|
||||
"signal handler is already installed",
|
||||
));
|
||||
}
|
||||
*channel = Some(notify);
|
||||
|
||||
unsafe { _install() }?;
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::WouldBlock,
|
||||
"unable to install signal handler",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Install global signal handlers for `SIGTERM` and `SIGINT`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Calls `libc` functions safely.
|
||||
unsafe fn _install() -> io::Result<()> {
|
||||
if libc::signal(libc::SIGTERM, handler as libc::sighandler_t) == libc::SIG_ERR {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
if libc::signal(libc::SIGINT, 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 {
|
||||
return;
|
||||
}
|
||||
if let Ok(guard) = NOTIFY.try_lock() {
|
||||
if let Some(c) = &*guard {
|
||||
c.try_send(()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use crossbeam_channel as chan;
|
||||
|
||||
use radicle::crypto::ssh::{keystore::MemorySigner, Keystore};
|
||||
use radicle::crypto::test::signer::MockSigner;
|
||||
use radicle::crypto::{KeyPair, Seed, Signature, Signer};
|
||||
|
|
@ -249,12 +251,14 @@ impl<G: cyphernet::EcSign<Pk = NodeId, Sig = Signature> + Signer + Clone> Node<G
|
|||
let listen = vec![([0, 0, 0, 0], 0).into()];
|
||||
let proxy = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 9050);
|
||||
let daemon = ([0, 0, 0, 0], fastrand::u16(1025..)).into();
|
||||
let (_, signals) = chan::bounded(1);
|
||||
let rt = Runtime::init(
|
||||
self.home.clone(),
|
||||
config,
|
||||
listen,
|
||||
proxy,
|
||||
daemon,
|
||||
signals,
|
||||
self.signer.clone(),
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
|||
Loading…
Reference in New Issue