diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 51d948e8..8534e5c6 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1524,6 +1524,11 @@ # #listening = true +# Enables configuration reload when the server receives SIGUSR1 on +# supporting platforms. +# +#config_reload_signal = true + [global.tls] # Path to a valid TLS certificate file. diff --git a/src/admin/server/commands.rs b/src/admin/server/commands.rs index 5c0c2a10..910dce6e 100644 --- a/src/admin/server/commands.rs +++ b/src/admin/server/commands.rs @@ -1,6 +1,6 @@ use std::{fmt::Write, path::PathBuf, sync::Arc}; -use conduwuit::{info, utils::time, warn, Config, Err, Result}; +use conduwuit::{info, utils::time, warn, Err, Result}; use ruma::events::room::message::RoomMessageEventContent; use crate::admin_command; @@ -32,15 +32,8 @@ pub(super) async fn reload_config( &self, path: Option, ) -> Result { - use conduwuit::config::check; - let path = path.as_deref().into_iter(); - let new = Config::load(path).and_then(|raw| Config::new(&raw))?; - - let old = &self.services.server.config; - check::reload(old, &new)?; - - self.services.server.config.update(new)?; + self.services.config.reload(path)?; Ok(RoomMessageEventContent::text_plain("Successfully reconfigured.")) } diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 94788fa4..8e8176ab 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -1742,6 +1742,13 @@ pub struct Config { #[serde(default = "true_fn")] pub listening: bool, + /// Enables configuration reload when the server receives SIGUSR1 on + /// supporting platforms. + /// + /// default: true + #[serde(default = "true_fn")] + pub config_reload_signal: bool, + #[serde(flatten)] #[allow(clippy::zero_sized_map_values)] // this is a catchall, the map shouldn't be zero at runtime diff --git a/src/main/signal.rs b/src/main/signal.rs index cecb718b..dfdca1d5 100644 --- a/src/main/signal.rs +++ b/src/main/signal.rs @@ -16,6 +16,7 @@ pub(super) async fn signal(server: Arc) { let mut quit = unix::signal(SignalKind::quit()).expect("SIGQUIT handler"); let mut term = unix::signal(SignalKind::terminate()).expect("SIGTERM handler"); + let mut usr1 = unix::signal(SignalKind::user_defined1()).expect("SIGUSR1 handler"); loop { trace!("Installed signal handlers"); let sig: &'static str; @@ -23,6 +24,7 @@ pub(super) async fn signal(server: Arc) { _ = signal::ctrl_c() => { sig = "SIGINT"; }, _ = quit.recv() => { sig = "SIGQUIT"; }, _ = term.recv() => { sig = "SIGTERM"; }, + _ = usr1.recv() => { sig = "SIGUSR1"; }, } warn!("Received {sig}"); diff --git a/src/service/config/mod.rs b/src/service/config/mod.rs new file mode 100644 index 00000000..ef98f176 --- /dev/null +++ b/src/service/config/mod.rs @@ -0,0 +1,55 @@ +use std::{iter, path::Path, sync::Arc}; + +use async_trait::async_trait; +use conduwuit::{ + config::{check, Config}, + error, implement, Result, Server, +}; + +pub struct Service { + server: Arc, +} + +const SIGNAL: &str = "SIGUSR1"; + +#[async_trait] +impl crate::Service for Service { + fn build(args: crate::Args<'_>) -> Result> { + Ok(Arc::new(Self { server: args.server.clone() })) + } + + async fn worker(self: Arc) -> Result { + while self.server.running() { + if self.server.signal.subscribe().recv().await == Ok(SIGNAL) { + if let Err(e) = self.handle_reload() { + error!("Failed to reload config: {e}"); + } + } + } + + Ok(()) + } + + fn name(&self) -> &str { crate::service::make_name(std::module_path!()) } +} + +#[implement(Service)] +fn handle_reload(&self) -> Result { + if self.server.config.config_reload_signal { + self.reload(iter::empty())?; + } + + Ok(()) +} + +#[implement(Service)] +pub fn reload<'a, I>(&self, paths: I) -> Result> +where + I: Iterator, +{ + let old = self.server.config.clone(); + let new = Config::load(paths).and_then(|raw| Config::new(&raw))?; + + check::reload(&old, &new)?; + self.server.config.update(new) +} diff --git a/src/service/mod.rs b/src/service/mod.rs index 2102921f..71bd0eb4 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -9,6 +9,7 @@ pub mod account_data; pub mod admin; pub mod appservice; pub mod client; +pub mod config; pub mod emergency; pub mod federation; pub mod globals; diff --git a/src/service/services.rs b/src/service/services.rs index cb5cc12f..fb334b96 100644 --- a/src/service/services.rs +++ b/src/service/services.rs @@ -10,7 +10,7 @@ use database::Database; use tokio::sync::Mutex; use crate::{ - account_data, admin, appservice, client, emergency, federation, globals, key_backups, + account_data, admin, appservice, client, config, emergency, federation, globals, key_backups, manager::Manager, media, presence, pusher, resolver, rooms, sending, server_keys, service, service::{Args, Map, Service}, @@ -21,6 +21,7 @@ pub struct Services { pub account_data: Arc, pub admin: Arc, pub appservice: Arc, + pub config: Arc, pub client: Arc, pub emergency: Arc, pub globals: Arc, @@ -68,6 +69,7 @@ impl Services { appservice: build!(appservice::Service), resolver: build!(resolver::Service), client: build!(client::Service), + config: build!(config::Service), emergency: build!(emergency::Service), globals: build!(globals::Service), key_backups: build!(key_backups::Service),