From ae66f5db43a10b27e4da4e374ee08c87527d8336 Mon Sep 17 00:00:00 2001 From: crabman Date: Mon, 12 Feb 2024 09:49:17 +0100 Subject: [PATCH] allow using other signals for graceful shutdown --- server-cli/src/main.rs | 17 +++++++++++----- server-cli/src/settings.rs | 11 ++++++++++ server-cli/src/shutdown_coordinator.rs | 28 +++++++++++++------------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/server-cli/src/main.rs b/server-cli/src/main.rs index 68f462eb31..49eefc712c 100644 --- a/server-cli/src/main.rs +++ b/server-cli/src/main.rs @@ -55,10 +55,7 @@ fn main() -> io::Result<()> { // noninteractive implies basic let basic = basic || noninteractive; - let sigusr1_signal = Arc::new(AtomicBool::new(false)); - - #[cfg(any(target_os = "linux", target_os = "macos"))] - let _ = signal_hook::flag::register(signal_hook::consts::SIGUSR1, Arc::clone(&sigusr1_signal)); + let shutdown_signal = Arc::new(AtomicBool::new(false)); let (_guards, _guards2) = if basic { (Vec::new(), common_frontend::init_stdout(None)) @@ -69,6 +66,16 @@ fn main() -> io::Result<()> { // Load settings let settings = settings::Settings::load(); + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + for signal in &settings.shutdown_signals { + let _ = signal_hook::flag::register( + *signal as core::ffi::c_int, + Arc::clone(&shutdown_signal), + ); + } + } + // Determine folder to save server data in let server_data_dir = { let mut path = common_base::userdata_dir_workspace!(); @@ -234,7 +241,7 @@ fn main() -> io::Result<()> { "Server is ready to accept connections." ); - let mut shutdown_coordinator = ShutdownCoordinator::new(Arc::clone(&sigusr1_signal)); + let mut shutdown_coordinator = ShutdownCoordinator::new(Arc::clone(&shutdown_signal)); // Set up an fps clock let mut clock = Clock::new(Duration::from_secs_f64(1.0 / TPS as f64)); diff --git a/server-cli/src/settings.rs b/server-cli/src/settings.rs index 00e30f5354..2b24aa4bf0 100644 --- a/server-cli/src/settings.rs +++ b/server-cli/src/settings.rs @@ -6,6 +6,15 @@ use std::{ }; use tracing::warn; +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +#[repr(i32)] +#[allow(clippy::upper_case_acronyms)] +pub enum ShutdownSignal { + SIGUSR1 = signal_hook::consts::SIGUSR1, + SIGUSR2 = signal_hook::consts::SIGUSR2, + SIGTERM = signal_hook::consts::SIGTERM, +} + #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] pub struct Settings { @@ -15,6 +24,7 @@ pub struct Settings { /// SECRET API HEADER used to access the chat api, if disabled the API is /// unreachable pub web_chat_secret: Option, + pub shutdown_signals: Vec, } impl Default for Settings { @@ -24,6 +34,7 @@ impl Default for Settings { update_shutdown_message: "The server is restarting for an update".to_owned(), web_address: SocketAddr::from((Ipv4Addr::LOCALHOST, 14005)), web_chat_secret: None, + shutdown_signals: vec![ShutdownSignal::SIGUSR1], } } } diff --git a/server-cli/src/shutdown_coordinator.rs b/server-cli/src/shutdown_coordinator.rs index d655809859..c6cc158e71 100644 --- a/server-cli/src/shutdown_coordinator.rs +++ b/server-cli/src/shutdown_coordinator.rs @@ -13,8 +13,8 @@ use std::{ use tracing::{error, info}; /// Coordinates the shutdown procedure for the server, which can be initiated by -/// either the TUI console interface or by sending the server the SIGUSR1 signal -/// which indicates the server is restarting due to an update. +/// either the TUI console interface or by sending the server the SIGUSR1 (or +/// others) signal which indicates the server is restarting due to an update. pub(crate) struct ShutdownCoordinator { /// The instant that the last shutdown message was sent, used for /// calculating when to send the next shutdown message @@ -28,19 +28,19 @@ pub(crate) struct ShutdownCoordinator { /// The message to use for the shutdown warning message that is sent to all /// connected players shutdown_message: String, - /// Provided by `signal_hook` to allow observation of the SIGUSR1 signal - sigusr1_signal: Arc, + /// Provided by `signal_hook` to allow observation of a shutdown signal + shutdown_signal: Arc, } impl ShutdownCoordinator { - pub fn new(sigusr1_signal: Arc) -> Self { + pub fn new(shutdown_signal: Arc) -> Self { Self { last_shutdown_msg: Instant::now(), msg_interval: Duration::from_secs(30), shutdown_initiated_at: None, shutdown_grace_period: Duration::from_secs(0), shutdown_message: String::new(), - sigusr1_signal, + shutdown_signal, } } @@ -81,8 +81,8 @@ impl ShutdownCoordinator { /// returns `true` which triggers the loop in `main.rs` to break and /// exit the server process. pub fn check(&mut self, server: &mut Server, settings: &Settings) -> bool { - // Check whether SIGUSR1 has been set - self.check_sigusr1_signal(server, settings); + // Check whether shutdown has been set + self.check_shutdown_signal(server, settings); // If a shutdown is in progress, check whether it's time to send another warning // message or shut down if the grace period has expired. @@ -109,14 +109,14 @@ impl ShutdownCoordinator { false } - /// Checks whether the SIGUSR1 signal has been set, which is used to trigger - /// a graceful shutdown for an update. [Watchtower](https://containrrr.dev/watchtower/) is configured on the main + /// Checks whether a shutdown (SIGUSR1 by default) signal has been set, + /// which is used to trigger a graceful shutdown for an update. [Watchtower](https://containrrr.dev/watchtower/) is configured on the main /// Veloren server to send SIGUSR1 instead of SIGTERM which allows us to /// react specifically to shutdowns that are for an update. /// NOTE: SIGUSR1 is not supported on Windows - fn check_sigusr1_signal(&mut self, server: &mut Server, settings: &Settings) { - if self.sigusr1_signal.load(Ordering::Relaxed) && self.shutdown_initiated_at.is_none() { - info!("Received SIGUSR1 signal, initiating graceful shutdown"); + fn check_shutdown_signal(&mut self, server: &mut Server, settings: &Settings) { + if self.shutdown_signal.load(Ordering::Relaxed) && self.shutdown_initiated_at.is_none() { + info!("Received shutdown signal, initiating graceful shutdown"); let grace_period = Duration::from_secs(u64::from(settings.update_shutdown_grace_period_secs)); let shutdown_message = settings.update_shutdown_message.to_owned(); @@ -124,7 +124,7 @@ impl ShutdownCoordinator { // Reset the SIGUSR1 signal indicator in case shutdown is aborted and we need to // trigger shutdown again - self.sigusr1_signal.store(false, Ordering::Relaxed); + self.shutdown_signal.store(false, Ordering::Relaxed); } }