add server restart support w/ admin command

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2024-06-16 19:46:32 +00:00
parent 2cb31275f0
commit c6f4b20e17
5 changed files with 54 additions and 2 deletions

View file

@ -49,6 +49,10 @@ pub(crate) enum ServerCommand {
/// - Hot-reload the server /// - Hot-reload the server
Reload, Reload,
#[cfg(unix)]
/// - Restart the server
Restart,
/// - Shutdown the server /// - Shutdown the server
Shutdown, Shutdown,
} }
@ -72,6 +76,8 @@ pub(crate) async fn process(command: ServerCommand, body: Vec<&str>) -> Result<R
} => admin_notice(body, message).await?, } => admin_notice(body, message).await?,
#[cfg(conduit_mods)] #[cfg(conduit_mods)]
ServerCommand::Reload => reload(body).await?, ServerCommand::Reload => reload(body).await?,
#[cfg(unix)]
ServerCommand::Restart => restart(body).await?,
ServerCommand::Shutdown => shutdown(body).await?, ServerCommand::Shutdown => shutdown(body).await?,
}) })
} }

View file

@ -112,6 +112,13 @@ pub(crate) async fn reload(_body: Vec<&str>) -> Result<RoomMessageEventContent>
Ok(RoomMessageEventContent::notice_plain("Reloading server...")) Ok(RoomMessageEventContent::notice_plain("Reloading server..."))
} }
#[cfg(unix)]
pub(crate) async fn restart(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
services().server.restart()?;
Ok(RoomMessageEventContent::notice_plain("Restarting server..."))
}
pub(crate) async fn shutdown(_body: Vec<&str>) -> Result<RoomMessageEventContent> { pub(crate) async fn shutdown(_body: Vec<&str>) -> Result<RoomMessageEventContent> {
warn!("shutdown command"); warn!("shutdown command");
services().server.shutdown()?; services().server.shutdown()?;

View file

@ -23,6 +23,9 @@ pub struct Server {
/// is an observable used on shutdown and modifying is not recommended. /// is an observable used on shutdown and modifying is not recommended.
pub reloading: AtomicBool, pub reloading: AtomicBool,
/// Restart desired; when true, restart it desired after shutdown.
pub restarting: AtomicBool,
/// Handle to the runtime /// Handle to the runtime
pub runtime: Option<runtime::Handle>, pub runtime: Option<runtime::Handle>,
@ -48,6 +51,7 @@ impl Server {
started: SystemTime::now(), started: SystemTime::now(),
stopping: AtomicBool::new(false), stopping: AtomicBool::new(false),
reloading: AtomicBool::new(false), reloading: AtomicBool::new(false),
restarting: AtomicBool::new(false),
runtime, runtime,
signal: broadcast::channel::<&'static str>(1).0, signal: broadcast::channel::<&'static str>(1).0,
log, log,
@ -75,6 +79,14 @@ impl Server {
self.signal("SIGINT") 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<()> { pub fn shutdown(&self) -> Result<()> {
if self.stopping.swap(true, Ordering::AcqRel) { if self.stopping.swap(true, Ordering::AcqRel) {
return Err(Error::Err("Shutdown already in progress".into())); return Err(Error::Err("Shutdown already in progress".into()));

View file

@ -1,11 +1,16 @@
pub(crate) mod clap; pub(crate) mod clap;
mod mods; mod mods;
mod restart;
mod server; mod server;
mod signal; mod signal;
extern crate conduit_core as conduit; 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 conduit::{debug_info, error, utils::available_parallelism, Error, Result};
use server::Server; use server::Server;
@ -32,8 +37,13 @@ fn main() -> Result<(), Error> {
// explicit drop here to trace thread and tls dtors // explicit drop here to trace thread and tls dtors
drop(runtime); drop(runtime);
debug_info!("Exit");
#[cfg(unix)]
if server.server.restarting.load(Ordering::Acquire) {
restart::restart();
}
debug_info!("Exit");
Ok(()) Ok(())
} }

17
src/main/restart.rs Normal file
View file

@ -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:?}");
}