use std::{fmt::Display, process::Command, time::Duration}; use serde::Deserialize; use tokio::sync::watch::Sender; use crate::{Error, Status}; use super::ServiceSpawner; #[derive(Debug, Clone, Deserialize)] pub struct Systemd { pub service: String, } impl Display for Systemd { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}.service", self.service.trim_end_matches(".service")) } } impl ServiceSpawner for Systemd { async fn spawn(self, tx: Sender) -> Result<(), Error> { let mut command = Command::new("systemctl"); command.arg("is-active").arg(&self.service); let mut interval = tokio::time::interval(Duration::from_secs(5)); loop { interval.tick().await; let status = match command.output() { Ok(output) if output.status.success() => Status::Pass, Ok(output) => { let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string(); Status::Fail(Some(format!("Service state: {}", stdout))) } Err(err) => { tracing::error!("Failed to spawn process: {err}"); Status::Unknown } }; tx.send_if_modified(|s| s.update(status)); } } } impl Systemd { pub async fn check(&self) -> Result { let output = Command::new("systemctl") .arg("is-active") .arg(&self.service) .output()?; let status = match output.status.success() { true => Status::Pass, false => { let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string(); Status::Fail(Some(format!("Service state: {}", stdout))) } }; Ok(status) } }