rehash: 'echoircd rehash' CLI + SIGHUP reload the running config in place via a pidfile
This commit is contained in:
parent
42fe82556b
commit
a7ae0d86a9
4 changed files with 96 additions and 3 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -22,3 +22,6 @@ bin/
|
||||||
# local backups / scratch config copies
|
# local backups / scratch config copies
|
||||||
*.bak-*
|
*.bak-*
|
||||||
echoircd.conf.*
|
echoircd.conf.*
|
||||||
|
|
||||||
|
# runtime pidfile
|
||||||
|
*.pid
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ name = "echoircd"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
# SIGHUP → config rehash, without unsafe (the crate keeps signal setup internal).
|
||||||
|
signal-hook = "0.3"
|
||||||
# TLS backend: openssl. The crate keeps all `unsafe` internal, so the daemon
|
# TLS backend: openssl. The crate keeps all `unsafe` internal, so the daemon
|
||||||
# stays `#![forbid(unsafe_code)]`. A pure-Rust `rustls` backend can slot in beside it.
|
# stays `#![forbid(unsafe_code)]`. A pure-Rust `rustls` backend can slot in beside it.
|
||||||
openssl = "0.10"
|
openssl = "0.10"
|
||||||
|
|
|
||||||
21
src/ircd.rs
21
src/ircd.rs
|
|
@ -101,6 +101,8 @@ pub enum Event {
|
||||||
},
|
},
|
||||||
/// Background timer tick — drives ping/idle timeouts.
|
/// Background timer tick — drives ping/idle timeouts.
|
||||||
Tick,
|
Tick,
|
||||||
|
/// Re-read the config file and apply it live (from SIGHUP / the `rehash` CLI).
|
||||||
|
Rehash,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert an extra IRCv3 tag into a wire line's tag block, creating the `@…`
|
/// Insert an extra IRCv3 tag into a wire line's tag block, creating the `@…`
|
||||||
|
|
@ -323,6 +325,7 @@ impl Ircd {
|
||||||
let _ = reply.send(resp);
|
let _ = reply.send(resp);
|
||||||
}
|
}
|
||||||
Event::Tick => self.on_tick(),
|
Event::Tick => self.on_tick(),
|
||||||
|
Event::Rehash => self.on_rehash(),
|
||||||
}
|
}
|
||||||
self.drain_hooks();
|
self.drain_hooks();
|
||||||
}
|
}
|
||||||
|
|
@ -643,6 +646,24 @@ impl Ircd {
|
||||||
|
|
||||||
/// Background timer: PING idle clients, reap the unresponsive and the
|
/// Background timer: PING idle clients, reap the unresponsive and the
|
||||||
/// never-registered.
|
/// never-registered.
|
||||||
|
/// SIGHUP / `rehash` CLI: re-read the config and apply it live, keeping the
|
||||||
|
/// running config if the file can't be read (same policy as /REHASH).
|
||||||
|
fn on_rehash(&mut self) {
|
||||||
|
let path = self.server.conf_path.clone();
|
||||||
|
eprintln!("rehashing server config file.");
|
||||||
|
match Config::try_load(&path) {
|
||||||
|
Some(fresh) => {
|
||||||
|
self.server.announce("The server is rehashing its configuration.");
|
||||||
|
self.server.apply_config(fresh);
|
||||||
|
self.server.announce("Server configuration reloaded.");
|
||||||
|
eprintln!("server configuration is reloaded.");
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!("rehash: could not read {path} — the running configuration was kept.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn on_tick(&mut self) {
|
fn on_tick(&mut self) {
|
||||||
self.server.ping_links(); // keepalive on every server link
|
self.server.ping_links(); // keepalive on every server link
|
||||||
self.server.purge_xlines(); // drop expired server bans
|
self.server.purge_xlines(); // drop expired server bans
|
||||||
|
|
|
||||||
73
src/main.rs
73
src/main.rs
|
|
@ -20,10 +20,51 @@ use echoircd::ircd::{Event, Ircd};
|
||||||
use echoircd::socketengine;
|
use echoircd::socketengine;
|
||||||
use echoircd::tls::{OpensslBackend, TlsBackend};
|
use echoircd::tls::{OpensslBackend, TlsBackend};
|
||||||
|
|
||||||
|
/// `echoircd rehash [config]`: read the running server's pidfile and send it
|
||||||
|
/// SIGHUP so it reloads its config in place, then exit. No unsafe — the signal is
|
||||||
|
/// sent via the `kill` command.
|
||||||
|
fn rehash_cli(cfgpath: &str) -> i32 {
|
||||||
|
let cfg = Config::load(cfgpath);
|
||||||
|
let pidfile = cfg
|
||||||
|
.raw
|
||||||
|
.get("pidfile")
|
||||||
|
.and_then(|v| v.first())
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| "echoircd.pid".to_string());
|
||||||
|
let pid = match std::fs::read_to_string(&pidfile) {
|
||||||
|
Ok(s) => s.trim().to_string(),
|
||||||
|
Err(_) => {
|
||||||
|
eprintln!("echoircd: no pidfile at {pidfile} — is the server running?");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if pid.parse::<u32>().is_err() {
|
||||||
|
eprintln!("echoircd: bad pidfile {pidfile} (contents: {pid:?})");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
println!("rehashing server config file.");
|
||||||
|
let sent = std::process::Command::new("kill")
|
||||||
|
.args(["-s", "HUP", &pid])
|
||||||
|
.status()
|
||||||
|
.map(|st| st.success())
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !sent {
|
||||||
|
eprintln!("echoircd: could not signal pid {pid} — is the server running?");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
println!("server configuration is reloaded.");
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let path = std::env::args()
|
let mut args = std::env::args().skip(1);
|
||||||
.nth(1)
|
let first = args.next();
|
||||||
.unwrap_or_else(|| "echoircd.conf".to_string());
|
// `echoircd rehash [config]` signals a running server instead of booting one.
|
||||||
|
if first.as_deref() == Some("rehash") {
|
||||||
|
let cfgpath = args.next().unwrap_or_else(|| "echoircd.conf".to_string());
|
||||||
|
std::process::exit(rehash_cli(&cfgpath));
|
||||||
|
}
|
||||||
|
let path = first.unwrap_or_else(|| "echoircd.conf".to_string());
|
||||||
let cfg = Config::load(&path);
|
let cfg = Config::load(&path);
|
||||||
|
|
||||||
// precompute the bcrypt constants off-thread so the first hash never stalls the core
|
// precompute the bcrypt constants off-thread so the first hash never stalls the core
|
||||||
|
|
@ -61,6 +102,17 @@ fn main() {
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write a pidfile (default echoircd.pid) so `echoircd rehash` can find us.
|
||||||
|
let pidfile = cfg
|
||||||
|
.raw
|
||||||
|
.get("pidfile")
|
||||||
|
.and_then(|v| v.first())
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_else(|| "echoircd.pid".to_string());
|
||||||
|
if let Err(e) = std::fs::write(&pidfile, format!("{}\n", std::process::id())) {
|
||||||
|
eprintln!("echoircd: could not write pidfile {pidfile}: {e}");
|
||||||
|
}
|
||||||
|
|
||||||
// global queue limits (per-class overrides layer on top of these in the reactor)
|
// global queue limits (per-class overrides layer on top of these in the reactor)
|
||||||
let raw_num = |k: &str, d: usize| {
|
let raw_num = |k: &str, d: usize| {
|
||||||
cfg.raw
|
cfg.raw
|
||||||
|
|
@ -122,6 +174,21 @@ fn main() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// SIGHUP → live config rehash (the mechanism the `rehash` CLI uses).
|
||||||
|
match signal_hook::iterator::Signals::new([signal_hook::consts::SIGHUP]) {
|
||||||
|
Ok(mut signals) => {
|
||||||
|
let sig_tx = tx.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
for _ in signals.forever() {
|
||||||
|
if sig_tx.send(Event::Rehash).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("echoircd: SIGHUP handler unavailable: {e}"),
|
||||||
|
}
|
||||||
|
|
||||||
// reactor worker pool: shared by the plaintext acceptor and the direct-TLS
|
// reactor worker pool: shared by the plaintext acceptor and the direct-TLS
|
||||||
// acceptor, so client I/O (framing + TLS crypto) spreads across cores.
|
// acceptor, so client I/O (framing + TLS crypto) spreads across cores.
|
||||||
let reactors =
|
let reactors =
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue