syspulse-core 0.1.2

Core library for syspulse daemon manager
Documentation
use async_trait::async_trait;
use std::time::Duration;
use tokio::time::timeout;

use super::HealthChecker;
use crate::daemon::{HealthCheckSpec, HealthStatus};
use crate::error::{Result, SyspulseError};

pub struct CommandHealthChecker {
    spec: HealthCheckSpec,
}

impl CommandHealthChecker {
    pub fn new(spec: HealthCheckSpec) -> Self {
        Self { spec }
    }
}

#[async_trait]
impl HealthChecker for CommandHealthChecker {
    async fn check(&self) -> Result<HealthStatus> {
        let dur = Duration::from_secs(self.spec.timeout_secs);

        #[cfg(unix)]
        let mut cmd = {
            let mut c = tokio::process::Command::new("sh");
            c.arg("-c").arg(&self.spec.target);
            c
        };

        #[cfg(windows)]
        let mut cmd = {
            let mut c = tokio::process::Command::new("cmd");
            c.arg("/C").arg(&self.spec.target);
            c
        };

        cmd.stdout(std::process::Stdio::null())
            .stderr(std::process::Stdio::null());

        match timeout(dur, cmd.status()).await {
            Ok(Ok(status)) => {
                if status.success() {
                    Ok(HealthStatus::Healthy)
                } else {
                    tracing::debug!(
                        command = %self.spec.target,
                        exit_code = ?status.code(),
                        "Command health check returned non-zero"
                    );
                    Ok(HealthStatus::Unhealthy)
                }
            }
            Ok(Err(e)) => {
                tracing::debug!(
                    command = %self.spec.target,
                    error = %e,
                    "Command health check failed to execute"
                );
                Ok(HealthStatus::Unhealthy)
            }
            Err(_) => {
                tracing::debug!(
                    command = %self.spec.target,
                    "Command health check timed out"
                );
                Err(SyspulseError::Timeout(dur))
            }
        }
    }

    fn spec(&self) -> &HealthCheckSpec {
        &self.spec
    }
}