proses 0.1.1

Proses – Professional Secure Execution System
use crate::process::Process;
use anyhow::{Context, Result};
use std::fs::OpenOptions;
use std::process::Command;

/// Spawn `sh -c <process.command>` in `process.cwd`.
///
/// stdout is redirected to `process.log_out` (append mode).
/// stderr is redirected to `process.log_err` (append mode).
/// stdin is connected to `/dev/null`.
///
/// Returns the OS PID of the spawned child.
pub fn spawn(process: &Process) -> Result<u32> {
    // Ensure parent log directory exists.
    if let Some(parent) = process.log_out.parent() {
        std::fs::create_dir_all(parent)
            .with_context(|| format!("Failed to create log directory {:?}", parent))?;
    }

    let log_out = OpenOptions::new()
        .create(true)
        .append(true)
        .open(&process.log_out)
        .with_context(|| format!("Failed to open stdout log {:?}", process.log_out))?;

    let log_err = OpenOptions::new()
        .create(true)
        .append(true)
        .open(&process.log_err)
        .with_context(|| format!("Failed to open stderr log {:?}", process.log_err))?;

    let child = Command::new("sh")
        .arg("-c")
        .arg(&process.command)
        .current_dir(&process.cwd)
        .envs(&process.env)
        .stdout(log_out)
        .stderr(log_err)
        .stdin(std::process::Stdio::null())
        .spawn()
        .with_context(|| {
            format!(
                "Failed to spawn process '{}' with command: {}",
                process.name, process.command
            )
        })?;

    Ok(child.id())
}

/// Send `SIGTERM` to the process identified by `pid`.
///
/// Returns `Ok(())` even if the process no longer exists (`ESRCH`), because
/// the goal — stopping the process — has already been achieved in that case.
pub fn stop(pid: u32) -> Result<()> {
    if pid == 0 {
        return Ok(());
    }

    let ret = unsafe { libc::kill(pid as libc::pid_t, libc::SIGTERM) };

    if ret != 0 {
        let err = std::io::Error::last_os_error();
        // ESRCH: no such process — it is already gone, which is fine.
        if err.raw_os_error() == Some(libc::ESRCH) {
            return Ok(());
        }
        return Err(anyhow::anyhow!("kill({}, SIGTERM) failed: {}", pid, err));
    }

    Ok(())
}

/// Returns `true` if a process with the given `pid` exists in the OS process
/// table (`kill(pid, 0)` succeeds).
///
/// Always returns `false` for `pid == 0`.
pub fn is_running(pid: u32) -> bool {
    if pid == 0 {
        return false;
    }
    // kill(pid, 0) returns 0 when the process exists (regardless of whether
    // we can actually send it a signal), and -1 otherwise.
    unsafe { libc::kill(pid as libc::pid_t, 0) == 0 }
}