diff --git a/src/admin/server/mod.rs b/src/admin/server/mod.rs index 584d3234..c3a517c0 100644 --- a/src/admin/server/mod.rs +++ b/src/admin/server/mod.rs @@ -49,6 +49,10 @@ pub(crate) enum ServerCommand { /// - Hot-reload the server Reload, + #[cfg(unix)] + /// - Restart the server + Restart, + /// - Shutdown the server Shutdown, } @@ -72,6 +76,8 @@ pub(crate) async fn process(command: ServerCommand, body: Vec<&str>) -> Result admin_notice(body, message).await?, #[cfg(conduit_mods)] ServerCommand::Reload => reload(body).await?, + #[cfg(unix)] + ServerCommand::Restart => restart(body).await?, ServerCommand::Shutdown => shutdown(body).await?, }) } diff --git a/src/admin/server/server_commands.rs b/src/admin/server/server_commands.rs index cf69d79e..0e01d4bf 100644 --- a/src/admin/server/server_commands.rs +++ b/src/admin/server/server_commands.rs @@ -112,6 +112,13 @@ pub(crate) async fn reload(_body: Vec<&str>) -> Result Ok(RoomMessageEventContent::notice_plain("Reloading server...")) } +#[cfg(unix)] +pub(crate) async fn restart(_body: Vec<&str>) -> Result { + services().server.restart()?; + + Ok(RoomMessageEventContent::notice_plain("Restarting server...")) +} + pub(crate) async fn shutdown(_body: Vec<&str>) -> Result { warn!("shutdown command"); services().server.shutdown()?; diff --git a/src/core/server.rs b/src/core/server.rs index ca62b64c..2aa821fa 100644 --- a/src/core/server.rs +++ b/src/core/server.rs @@ -23,6 +23,9 @@ pub struct Server { /// is an observable used on shutdown and modifying is not recommended. pub reloading: AtomicBool, + /// Restart desired; when true, restart it desired after shutdown. + pub restarting: AtomicBool, + /// Handle to the runtime pub runtime: Option, @@ -48,6 +51,7 @@ impl Server { started: SystemTime::now(), stopping: AtomicBool::new(false), reloading: AtomicBool::new(false), + restarting: AtomicBool::new(false), runtime, signal: broadcast::channel::<&'static str>(1).0, log, @@ -75,6 +79,14 @@ impl Server { self.signal("SIGINT") } + pub fn restart(&self) -> Result<()> { + if self.restarting.swap(true, Ordering::AcqRel) { + return Err(Error::Err("Restart already in progress".into())); + } + + self.shutdown() + } + pub fn shutdown(&self) -> Result<()> { if self.stopping.swap(true, Ordering::AcqRel) { return Err(Error::Err("Shutdown already in progress".into())); diff --git a/src/main/main.rs b/src/main/main.rs index 618871a7..1bb46c8e 100644 --- a/src/main/main.rs +++ b/src/main/main.rs @@ -1,11 +1,16 @@ pub(crate) mod clap; mod mods; +mod restart; mod server; mod signal; extern crate conduit_core as conduit; -use std::{cmp, sync::Arc, time::Duration}; +use std::{ + cmp, + sync::{atomic::Ordering, Arc}, + time::Duration, +}; use conduit::{debug_info, error, utils::available_parallelism, Error, Result}; use server::Server; @@ -32,8 +37,13 @@ fn main() -> Result<(), Error> { // explicit drop here to trace thread and tls dtors drop(runtime); - debug_info!("Exit"); + #[cfg(unix)] + if server.server.restarting.load(Ordering::Acquire) { + restart::restart(); + } + + debug_info!("Exit"); Ok(()) } diff --git a/src/main/restart.rs b/src/main/restart.rs new file mode 100644 index 00000000..559d7446 --- /dev/null +++ b/src/main/restart.rs @@ -0,0 +1,17 @@ +#![cfg(unix)] + +use std::{env, os::unix::process::CommandExt, process::Command}; + +use conduit::{debug, info}; + +pub(super) fn restart() -> ! { + let exe = env::current_exe().expect("program path must be identified and available"); + let envs = env::vars(); + let args = env::args().skip(1); + debug!(?exe, ?args, ?envs, "Restart"); + + info!("Restart"); + + let error = Command::new(exe).args(args).envs(envs).exec(); + panic!("{error:?}"); +}