Skip to content

Commit 0b7042b

Browse files
committed
Send notifications about the state of the service to systemd
1 parent c86746f commit 0b7042b

File tree

3 files changed

+47
-3
lines changed

3 files changed

+47
-3
lines changed

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ rand.workspace = true
3333
rand_chacha = "0.3.1"
3434
reqwest.workspace = true
3535
rustls.workspace = true
36+
sd-notify = "0.4.5"
3637
serde_json.workspace = true
3738
serde_yaml = "0.9.34"
3839
sqlx.workspace = true

crates/cli/src/shutdown.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
// Copyright 2024 New Vector Ltd.
1+
// Copyright 2024, 2025 New Vector Ltd.
22
//
33
// SPDX-License-Identifier: AGPL-3.0-only
44
// Please see LICENSE in the repository root for full details.
55

6-
use std::{future::Future, pin::Pin, process::ExitCode, time::Duration};
6+
use std::{future::Future, process::ExitCode, time::Duration};
77

88
use futures_util::future::BoxFuture;
99
use mas_handlers::ActivityTracker;
@@ -36,6 +36,7 @@ pub struct ShutdownManager {
3636
reload_handlers: Vec<Box<dyn Fn() -> BoxFuture<'static, ()>>>,
3737
}
3838

39+
/// Represents a thing that can be reloaded with a SIGHUP
3940
pub trait Reloadable: Clone + Send {
4041
fn reload(&self) -> impl Future<Output = ()> + Send;
4142
}
@@ -57,6 +58,16 @@ impl Reloadable for Templates {
5758
}
5859
}
5960

61+
/// A wrapper around [`sd_notify::notify`] that logs any errors
62+
fn notify(states: &[sd_notify::NotifyState]) {
63+
if let Err(e) = sd_notify::notify(false, states) {
64+
tracing::error!(
65+
error = &e as &dyn std::error::Error,
66+
"Failed to notify service manager"
67+
);
68+
}
69+
}
70+
6071
impl ShutdownManager {
6172
/// Create a new shutdown manager, installing the signal handlers
6273
///
@@ -113,7 +124,13 @@ impl ShutdownManager {
113124

114125
/// Run until we finish completely shutting down.
115126
pub async fn run(mut self) -> ExitCode {
116-
// Wait for a first signal and trigger the soft shutdown
127+
notify(&[sd_notify::NotifyState::Ready]);
128+
129+
let mut watchdog_usec = 0;
130+
let watchdog_enabled = sd_notify::watchdog_enabled(false, &mut watchdog_usec);
131+
let mut watchdog_interval = tokio::time::interval(Duration::from_micros(watchdog_usec / 2));
132+
133+
// Wait for a first shutdown signal and trigger the soft shutdown
117134
let likely_crashed = loop {
118135
tokio::select! {
119136
() = self.soft_shutdown_token.cancelled() => {
@@ -131,21 +148,37 @@ impl ShutdownManager {
131148
break false;
132149
},
133150

151+
_ = watchdog_interval.tick(), if watchdog_enabled => {
152+
notify(&[
153+
sd_notify::NotifyState::Watchdog,
154+
]);
155+
},
156+
134157
_ = self.sighup.recv() => {
135158
tracing::info!("Reload signal received (SIGHUP), reloading");
136159

160+
notify(&[
161+
sd_notify::NotifyState::Reloading,
162+
sd_notify::NotifyState::monotonic_usec_now()
163+
.expect("Failed to read monotonic clock")
164+
]);
165+
137166
// XXX: if the handler takes a long time, it will block the
138167
// rest of the shutdown process, which is not ideal. We
139168
// should probably have a timeout here
140169
for handler in &self.reload_handlers {
141170
handler().await;
142171
}
143172

173+
notify(&[sd_notify::NotifyState::Ready]);
174+
144175
tracing::info!("Reloading done");
145176
},
146177
}
147178
};
148179

180+
notify(&[sd_notify::NotifyState::Stopping]);
181+
149182
self.soft_shutdown_token.cancel();
150183
self.task_tracker.close();
151184

0 commit comments

Comments
 (0)