diff --git a/rust/gui-client/src-tauri/src/client.rs b/rust/gui-client/src-tauri/src/client.rs index f39f1aabc..8518ea347 100644 --- a/rust/gui-client/src-tauri/src/client.rs +++ b/rust/gui-client/src-tauri/src/client.rs @@ -158,6 +158,7 @@ fn fix_log_filter(settings: &mut AdvancedSettings) -> Result<()> { fn start_logging(directives: &str) -> Result { let logging_handles = logging::setup(directives)?; tracing::info!( + arch = std::env::consts::ARCH, ?directives, ?GIT_VERSION, system_uptime_seconds = firezone_headless_client::uptime::get().map(|dur| dur.as_secs()), diff --git a/rust/headless-client/src/ipc_service.rs b/rust/headless-client/src/ipc_service.rs index 76a5da9b6..63e7342fa 100644 --- a/rust/headless-client/src/ipc_service.rs +++ b/rust/headless-client/src/ipc_service.rs @@ -91,26 +91,40 @@ pub fn run_only_ipc_service() -> Result<()> { fn run_debug_ipc_service() -> Result<()> { crate::setup_stdout_logging()?; + tracing::info!( + arch = std::env::consts::ARCH, + git_version = crate::GIT_VERSION, + system_uptime_seconds = crate::uptime::get().map(|dur| dur.as_secs()), + ); let rt = tokio::runtime::Runtime::new()?; let _guard = rt.enter(); let mut signals = Signals::new()?; - // Couldn't get the loop to work here yet, so SIGHUP is not implemented - rt.block_on(async { - let ipc_service = pin!(ipc_listen()); + rt.block_on(ipc_listen_with_signals(&mut signals)) +} - match future::select(pin!(signals.recv()), ipc_service).await { - future::Either::Left((SignalKind::Hangup, _)) => { - bail!("Exiting, SIGHUP not implemented for the IPC service"); - } - future::Either::Left((SignalKind::Interrupt, _)) => { - tracing::info!("Caught Interrupt signal"); - Ok(()) - } - future::Either::Right((Ok(impossible), _)) => match impossible {}, - future::Either::Right((Err(error), _)) => Err(error).context("ipc_listen failed"), +/// Run the IPC service, and exit if we catch any signals +/// +/// Shared between the Linux systemd service and the debug subcommand +/// TODO: Better name +async fn ipc_listen_with_signals(signals: &mut Signals) -> Result<()> { + let ipc_service = pin!(ipc_listen()); + + match future::select(pin!(signals.recv()), ipc_service).await { + future::Either::Left((SignalKind::Hangup, _)) => { + bail!("Exiting, SIGHUP not implemented for the IPC service"); } - }) + future::Either::Left((SignalKind::Interrupt, _)) => { + tracing::info!("Caught SIGINT"); + Ok(()) + } + future::Either::Left((SignalKind::Terminate, _)) => { + tracing::info!("Caught SIGTERM"); + Ok(()) + } + future::Either::Right((Ok(impossible), _)) => match impossible {}, + future::Either::Right((Err(error), _)) => Err(error).context("ipc_listen failed"), + } } #[cfg(not(debug_assertions))] @@ -332,6 +346,7 @@ fn setup_logging(log_dir: Option) -> Result Result<()> { anyhow::bail!("This is the IPC service binary, it's not meant to run interactively."); } let rt = tokio::runtime::Runtime::new()?; - if let Err(error) = rt.block_on(super::ipc_listen()) { - tracing::error!(?error, "`ipc_listen` failed"); - } - Ok(()) + let _guard = rt.enter(); + let mut signals = Signals::new()?; + + rt.block_on(super::ipc_listen_with_signals(&mut signals)) } pub(crate) fn install_ipc_service() -> Result<()> { diff --git a/rust/headless-client/src/lib.rs b/rust/headless-client/src/lib.rs index c8205bdb9..8e1514987 100644 --- a/rust/headless-client/src/lib.rs +++ b/rust/headless-client/src/lib.rs @@ -164,6 +164,10 @@ enum SignalKind { Hangup, /// SIGINT Interrupt, + /// SIGTERM + /// + /// Not caught on Windows + Terminate, } /// Sets up logging for stdout only, with INFO level by default diff --git a/rust/headless-client/src/linux.rs b/rust/headless-client/src/linux.rs index 96e282c51..7e2556908 100644 --- a/rust/headless-client/src/linux.rs +++ b/rust/headless-client/src/linux.rs @@ -2,7 +2,7 @@ use super::{SignalKind, TOKEN_ENV_KEY}; use anyhow::{bail, Result}; -use futures::future::{select, Either}; +use futures::future::FutureExt as _; use std::{ path::{Path, PathBuf}, pin::pin, @@ -15,22 +15,32 @@ const ROOT_GROUP: u32 = 0; const ROOT_USER: u32 = 0; pub(crate) struct Signals { + /// For reloading settings in the standalone Client sighup: Signal, + /// For Ctrl+C from a terminal sigint: Signal, + /// For systemd service stopping + sigterm: Signal, } impl Signals { pub(crate) fn new() -> Result { let sighup = signal(TokioSignalKind::hangup())?; let sigint = signal(TokioSignalKind::interrupt())?; + let sigterm = signal(TokioSignalKind::terminate())?; - Ok(Self { sighup, sigint }) + Ok(Self { + sighup, + sigint, + sigterm, + }) } pub(crate) async fn recv(&mut self) -> SignalKind { - match select(pin!(self.sighup.recv()), pin!(self.sigint.recv())).await { - Either::Left((_, _)) => SignalKind::Hangup, - Either::Right((_, _)) => SignalKind::Interrupt, + futures::select! { + _ = pin!(self.sighup.recv().fuse()) => SignalKind::Hangup, + _ = pin!(self.sigint.recv().fuse()) => SignalKind::Interrupt, + _ = pin!(self.sigterm.recv().fuse()) => SignalKind::Terminate, } } } diff --git a/rust/headless-client/src/standalone.rs b/rust/headless-client/src/standalone.rs index 991e54dc9..84082a880 100644 --- a/rust/headless-client/src/standalone.rs +++ b/rust/headless-client/src/standalone.rs @@ -111,7 +111,10 @@ pub fn run_only_headless_client() -> Result<()> { .unzip(); setup_global_subscriber(layer); - tracing::info!(git_version = crate::GIT_VERSION); + tracing::info!( + arch = std::env::consts::ARCH, + git_version = crate::GIT_VERSION + ); let rt = tokio::runtime::Builder::new_current_thread() .enable_all() @@ -174,11 +177,15 @@ pub fn run_only_headless_client() -> Result<()> { loop { match future::select(pin!(signals.recv()), pin!(cb_rx.recv())).await { future::Either::Left((SignalKind::Hangup, _)) => { - tracing::info!("Caught Hangup signal"); + tracing::info!("Caught SIGHUP"); session.reconnect(); } future::Either::Left((SignalKind::Interrupt, _)) => { - tracing::info!("Caught Interrupt signal"); + tracing::info!("Caught SIGINT"); + return Ok(()); + } + future::Either::Left((SignalKind::Terminate, _)) => { + tracing::info!("Caught SIGTERM"); return Ok(()); } future::Either::Right((None, _)) => {