server: hide systemd stuff behind compiletime cond
All checks were successful
Build and test / check (push) Successful in 1m49s
Build and test / check-license (push) Successful in 1m49s
Build and test / build (push) Successful in 2m36s
Build and test / test (push) Successful in 3m25s
Build and test / docs (push) Successful in 6m35s

This commit is contained in:
2025-12-15 17:02:53 +09:00
parent 73f5cd9fd4
commit 912f0e8971
3 changed files with 60 additions and 15 deletions

View File

@@ -34,7 +34,6 @@ nix = { version = "0.30.1", features = ["fs", "process", "socket", "user"] }
num_cpus = "1.17.0" num_cpus = "1.17.0"
prettytable = "0.10.0" prettytable = "0.10.0"
rand = "0.9.2" rand = "0.9.2"
sd-notify = "0.4.5"
serde = "1.0.228" serde = "1.0.228"
serde_json = { version = "1.0.145", features = ["preserve_order"] } serde_json = { version = "1.0.145", features = ["preserve_order"] }
sqlx = { version = "0.8.6", features = ["runtime-tokio", "mysql", "tls-rustls"] } sqlx = { version = "0.8.6", features = ["runtime-tokio", "mysql", "tls-rustls"] }
@@ -45,12 +44,13 @@ tokio-stream = "0.1.17"
tokio-util = { version = "0.7.17", features = ["codec", "rt"] } tokio-util = { version = "0.7.17", features = ["codec", "rt"] }
toml = "0.9.8" toml = "0.9.8"
tracing = { version = "0.1.43", features = ["log"] } tracing = { version = "0.1.43", features = ["log"] }
tracing-journald = "0.3.2"
tracing-subscriber = "0.3.22" tracing-subscriber = "0.3.22"
uuid = { version = "1.19.0", features = ["v4"] } uuid = { version = "1.19.0", features = ["v4"] }
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
landlock = "0.4.4" landlock = "0.4.4"
sd-notify = "0.4.5"
tracing-journald = "0.3.2"
[build-dependencies] [build-dependencies]
anyhow = "1.0.100" anyhow = "1.0.100"

View File

@@ -16,6 +16,7 @@ pub struct ServerArgs {
pub subcmd: ServerCommand, pub subcmd: ServerCommand,
/// Enable systemd mode /// Enable systemd mode
#[cfg(target_os = "linux")]
#[arg(long)] #[arg(long)]
pub systemd: bool, pub systemd: bool,
@@ -58,6 +59,8 @@ pub async fn handle_command(
args: ServerArgs, args: ServerArgs,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let mut auto_detected_systemd_mode = false; let mut auto_detected_systemd_mode = false;
#[cfg(target_os = "linux")]
let systemd_mode = args.systemd || { let systemd_mode = args.systemd || {
if let Ok(true) = sd_notify::booted() { if let Ok(true) = sd_notify::booted() {
auto_detected_systemd_mode = true; auto_detected_systemd_mode = true;
@@ -67,7 +70,12 @@ pub async fn handle_command(
} }
}; };
#[cfg(not(target_os = "linux"))]
let systemd_mode = false;
if systemd_mode { if systemd_mode {
#[cfg(target_os = "linux")]
{
let subscriber = tracing_subscriber::Registry::default() let subscriber = tracing_subscriber::Registry::default()
.with(verbosity.tracing_level_filter()) .with(verbosity.tracing_level_filter())
.with(tracing_journald::layer()?); .with(tracing_journald::layer()?);
@@ -86,6 +94,7 @@ pub async fn handle_command(
} else { } else {
tracing::debug!("Running in systemd mode"); tracing::debug!("Running in systemd mode");
} }
}
} else { } else {
let subscriber = tracing_subscriber::Registry::default() let subscriber = tracing_subscriber::Registry::default()
.with(verbosity.tracing_level_filter()) .with(verbosity.tracing_level_filter())

View File

@@ -68,6 +68,7 @@ impl Supervisor {
let mut watchdog_duration = None; let mut watchdog_duration = None;
let mut watchdog_micro_seconds = 0; let mut watchdog_micro_seconds = 0;
#[cfg(target_os = "linux")]
let watchdog_task = let watchdog_task =
if systemd_mode && sd_notify::watchdog_enabled(true, &mut watchdog_micro_seconds) { if systemd_mode && sd_notify::watchdog_enabled(true, &mut watchdog_micro_seconds) {
watchdog_duration = Some(Duration::from_micros(watchdog_micro_seconds)); watchdog_duration = Some(Duration::from_micros(watchdog_micro_seconds));
@@ -80,6 +81,8 @@ impl Supervisor {
tracing::debug!("Systemd watchdog not enabled, skipping watchdog thread"); tracing::debug!("Systemd watchdog not enabled, skipping watchdog thread");
None None
}; };
#[cfg(not(target_os = "linux"))]
let watchdog_task = None;
let db_connection_pool = let db_connection_pool =
Arc::new(RwLock::new(create_db_connection_pool(&config.mysql).await?)); Arc::new(RwLock::new(create_db_connection_pool(&config.mysql).await?));
@@ -102,19 +105,34 @@ impl Supervisor {
let task_tracker = TaskTracker::new(); let task_tracker = TaskTracker::new();
#[cfg(target_os = "linux")]
let status_notifier_task = if systemd_mode { let status_notifier_task = if systemd_mode {
Some(spawn_status_notifier_task(task_tracker.clone())) Some(spawn_status_notifier_task(task_tracker.clone()))
} else { } else {
None None
}; };
#[cfg(not(target_os = "linux"))]
let status_notifier_task = None;
let (tx, rx) = broadcast::channel(1); let (tx, rx) = broadcast::channel(1);
// TODO: try to detech systemd socket before using the provided socket path // TODO: try to detech systemd socket before using the provided socket path
#[cfg(target_os = "linux")]
let listener = Arc::new(RwLock::new(match config.socket_path { let listener = Arc::new(RwLock::new(match config.socket_path {
Some(ref path) => create_unix_listener_with_socket_path(path.clone()).await?, Some(ref path) => create_unix_listener_with_socket_path(path.clone()).await?,
None => create_unix_listener_with_systemd_socket().await?, None => create_unix_listener_with_systemd_socket().await?,
})); }));
#[cfg(not(target_os = "linux"))]
let listener = Arc::new(RwLock::new(
create_unix_listener_with_socket_path(
config
.socket_path
.as_ref()
.ok_or(anyhow!("Socket path must be set"))?
.clone(),
)
.await?,
));
let (reload_tx, reload_rx) = broadcast::channel(1); let (reload_tx, reload_rx) = broadcast::channel(1);
let shutdown_cancel_token = CancellationToken::new(); let shutdown_cancel_token = CancellationToken::new();
@@ -211,16 +229,28 @@ impl Supervisor {
// first. Make sure to handle that appropriately to avoid a deadlock. // first. Make sure to handle that appropriately to avoid a deadlock.
async fn reload_listener(&self) -> anyhow::Result<()> { async fn reload_listener(&self) -> anyhow::Result<()> {
let config = self.config.lock().await; let config = self.config.lock().await;
#[cfg(target_os = "linux")]
let new_listener = match config.socket_path { let new_listener = match config.socket_path {
Some(ref path) => create_unix_listener_with_socket_path(path.clone()).await?, Some(ref path) => create_unix_listener_with_socket_path(path.clone()).await?,
None => create_unix_listener_with_systemd_socket().await?, None => create_unix_listener_with_systemd_socket().await?,
}; };
#[cfg(not(target_os = "linux"))]
let new_listener = create_unix_listener_with_socket_path(
config
.socket_path
.as_ref()
.ok_or(anyhow!("Socket path must be set"))?
.clone(),
)
.await?;
let mut listener = self.listener.write().await; let mut listener = self.listener.write().await;
*listener = new_listener; *listener = new_listener;
Ok(()) Ok(())
} }
pub async fn reload(&self) -> anyhow::Result<()> { pub async fn reload(&self) -> anyhow::Result<()> {
#[cfg(target_os = "linux")]
sd_notify::notify(false, &[sd_notify::NotifyState::Reloading])?; sd_notify::notify(false, &[sd_notify::NotifyState::Reloading])?;
let previous_config = self.config.lock().await.clone(); let previous_config = self.config.lock().await.clone();
@@ -257,12 +287,14 @@ impl Supervisor {
self.resume_receiving_new_connections()?; self.resume_receiving_new_connections()?;
} }
#[cfg(target_os = "linux")]
sd_notify::notify(false, &[sd_notify::NotifyState::Ready])?; sd_notify::notify(false, &[sd_notify::NotifyState::Ready])?;
Ok(()) Ok(())
} }
pub async fn shutdown(&self) -> anyhow::Result<()> { pub async fn shutdown(&self) -> anyhow::Result<()> {
#[cfg(target_os = "linux")]
sd_notify::notify(false, &[sd_notify::NotifyState::Stopping])?; sd_notify::notify(false, &[sd_notify::NotifyState::Stopping])?;
tracing::debug!("Stop accepting new connections"); tracing::debug!("Stop accepting new connections");
@@ -323,6 +355,7 @@ impl Supervisor {
} }
} }
#[cfg(target_os = "linux")]
fn spawn_watchdog_task(duration: Duration) -> JoinHandle<()> { fn spawn_watchdog_task(duration: Duration) -> JoinHandle<()> {
tokio::spawn(async move { tokio::spawn(async move {
let mut interval = interval(duration.div_f32(2.0)); let mut interval = interval(duration.div_f32(2.0));
@@ -339,6 +372,7 @@ fn spawn_watchdog_task(duration: Duration) -> JoinHandle<()> {
}) })
} }
#[cfg(target_os = "linux")]
fn spawn_status_notifier_task(task_tracker: TaskTracker) -> JoinHandle<()> { fn spawn_status_notifier_task(task_tracker: TaskTracker) -> JoinHandle<()> {
const STATUS_UPDATE_INTERVAL_SECS: Duration = Duration::from_secs(1); const STATUS_UPDATE_INTERVAL_SECS: Duration = Duration::from_secs(1);
@@ -385,6 +419,7 @@ async fn create_unix_listener_with_socket_path(
Ok(listener) Ok(listener)
} }
#[cfg(target_os = "linux")]
async fn create_unix_listener_with_systemd_socket() -> anyhow::Result<TokioUnixListener> { async fn create_unix_listener_with_systemd_socket() -> anyhow::Result<TokioUnixListener> {
let fd = sd_notify::listen_fds() let fd = sd_notify::listen_fds()
.context("Failed to get file descriptors from systemd")? .context("Failed to get file descriptors from systemd")?
@@ -468,6 +503,7 @@ async fn listener_task(
mut supervisor_message_receiver: broadcast::Receiver<SupervisorMessage>, mut supervisor_message_receiver: broadcast::Receiver<SupervisorMessage>,
db_is_mariadb: Arc<RwLock<bool>>, db_is_mariadb: Arc<RwLock<bool>>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
#[cfg(target_os = "linux")]
sd_notify::notify(false, &[sd_notify::NotifyState::Ready])?; sd_notify::notify(false, &[sd_notify::NotifyState::Ready])?;
loop { loop {