use libsystemd::daemon;
use std::time::Duration;
use tokio::time::interval;
#[derive(Clone)]
pub struct Watchdog {
interval: Duration,
enabled: bool,
}
impl Watchdog {
pub fn new(interval_secs: Option<u64>) -> Self {
let enabled = interval_secs.is_some_and(|s| s > 0);
let interval = Duration::from_secs(interval_secs.unwrap_or(30));
Self { interval, enabled }
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn interval(&self) -> Duration {
self.interval
}
pub fn start(self) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
if !self.enabled {
return;
}
let watchdog_timeout = daemon::watchdog_enabled(false);
if watchdog_timeout.is_none() {
eprintln!("[WARNING] systemd watchdog not enabled in service unit");
return;
}
let mut ticker = interval(self.interval);
ticker.tick().await;
loop {
ticker.tick().await;
if let Err(e) = daemon::notify(false, &[daemon::NotifyState::Watchdog]) {
eprintln!("[ERROR] systemd watchdog notify failed: {}", e);
}
}
})
}
}
pub(crate) fn sd_notify(state: daemon::NotifyState) -> Result<(), String> {
daemon::notify(false, &[state])
.map(|_| ())
.map_err(|e| e.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_watchdog_disabled_none() {
let wd = Watchdog::new(None);
assert!(!wd.is_enabled());
assert_eq!(wd.interval(), Duration::from_secs(30)); }
#[test]
fn test_watchdog_disabled_zero() {
let wd = Watchdog::new(Some(0));
assert!(!wd.is_enabled());
}
#[test]
fn test_watchdog_enabled() {
let wd = Watchdog::new(Some(15));
assert!(wd.is_enabled());
assert_eq!(wd.interval(), Duration::from_secs(15));
}
#[test]
fn test_watchdog_clone() {
let wd = Watchdog::new(Some(20));
let wd2 = wd.clone();
assert_eq!(wd.is_enabled(), wd2.is_enabled());
assert_eq!(wd.interval(), wd2.interval());
}
#[test]
fn test_libsystemd_notify_ready() {
let result = sd_notify(daemon::NotifyState::Ready);
let _ = result; }
#[test]
fn test_libsystemd_notify_watchdog() {
let result = sd_notify(daemon::NotifyState::Watchdog);
let _ = result;
}
#[test]
fn test_libsystemd_notify_status() {
let result = sd_notify(daemon::NotifyState::Status("fsmon test".to_string()));
let _ = result;
}
#[test]
fn test_libsystemd_watchdog_enabled() {
let _ = daemon::watchdog_enabled(false);
}
}