feature/ci-debian
rs 85 lines 2.62 KB
Raw
1 use crate::process::Process;
2 use anyhow::{Context, Result};
3 use std::fs::OpenOptions;
4 use std::process::Command;
5
6 /// Spawn `sh -c <process.command>` in `process.cwd`.
7 ///
8 /// stdout is redirected to `process.log_out` (append mode).
9 /// stderr is redirected to `process.log_err` (append mode).
10 /// stdin is connected to `/dev/null`.
11 ///
12 /// Returns the OS PID of the spawned child.
13 pub fn spawn(process: &Process) -> Result<u32> {
14 // Ensure parent log directory exists.
15 if let Some(parent) = process.log_out.parent() {
16 std::fs::create_dir_all(parent)
17 .with_context(|| format!("Failed to create log directory {:?}", parent))?;
18 }
19
20 let log_out = OpenOptions::new()
21 .create(true)
22 .append(true)
23 .open(&process.log_out)
24 .with_context(|| format!("Failed to open stdout log {:?}", process.log_out))?;
25
26 let log_err = OpenOptions::new()
27 .create(true)
28 .append(true)
29 .open(&process.log_err)
30 .with_context(|| format!("Failed to open stderr log {:?}", process.log_err))?;
31
32 let child = Command::new("sh")
33 .arg("-c")
34 .arg(&process.command)
35 .current_dir(&process.cwd)
36 .envs(&process.env)
37 .stdout(log_out)
38 .stderr(log_err)
39 .stdin(std::process::Stdio::null())
40 .spawn()
41 .with_context(|| {
42 format!(
43 "Failed to spawn process '{}' with command: {}",
44 process.name, process.command
45 )
46 })?;
47
48 Ok(child.id())
49 }
50
51 /// Send `SIGTERM` to the process identified by `pid`.
52 ///
53 /// Returns `Ok(())` even if the process no longer exists (`ESRCH`), because
54 /// the goal — stopping the process — has already been achieved in that case.
55 pub fn stop(pid: u32) -> Result<()> {
56 if pid == 0 {
57 return Ok(());
58 }
59
60 let ret = unsafe { libc::kill(pid as libc::pid_t, libc::SIGTERM) };
61
62 if ret != 0 {
63 let err = std::io::Error::last_os_error();
64 // ESRCH: no such process — it is already gone, which is fine.
65 if err.raw_os_error() == Some(libc::ESRCH) {
66 return Ok(());
67 }
68 return Err(anyhow::anyhow!("kill({}, SIGTERM) failed: {}", pid, err));
69 }
70
71 Ok(())
72 }
73
74 /// Returns `true` if a process with the given `pid` exists in the OS process
75 /// table (`kill(pid, 0)` succeeds).
76 ///
77 /// Always returns `false` for `pid == 0`.
78 pub fn is_running(pid: u32) -> bool {
79 if pid == 0 {
80 return false;
81 }
82 // kill(pid, 0) returns 0 when the process exists (regardless of whether
83 // we can actually send it a signal), and -1 otherwise.
84 unsafe { libc::kill(pid as libc::pid_t, 0) == 0 }
85 }