Skip to main content

yoctui_utils/
process.rs

1use std::io;
2
3/// Return whether a failed spawn can succeed once the executable stops being
4/// rewritten by another process.
5#[cfg(unix)]
6pub fn is_transient_spawn_error(error: &io::Error) -> bool {
7    error.raw_os_error() == Some(libc::ETXTBSY)
8}
9
10#[cfg(not(unix))]
11pub fn is_transient_spawn_error(_error: &io::Error) -> bool {
12    false
13}
14
15/// Best-effort Unix process-priority reduction for background discovery work.
16/// A larger nice value gives interactive work precedence. Callers choose
17/// whether inability to lower priority should fail the operation.
18#[cfg(unix)]
19pub fn lower_process_priority(pid: u32, nice: i32) -> io::Result<()> {
20    if !(0..=19).contains(&nice) {
21        return Err(io::Error::new(
22            io::ErrorKind::InvalidInput,
23            "nice value must be between 0 and 19",
24        ));
25    }
26    // SAFETY: setpriority receives a process id returned by spawn and a
27    // validated value. It neither borrows memory nor retains pointers.
28    if unsafe { libc::setpriority(libc::PRIO_PROCESS, pid, nice) } == 0 {
29        Ok(())
30    } else {
31        Err(io::Error::last_os_error())
32    }
33}
34
35#[cfg(not(unix))]
36pub fn lower_process_priority(_pid: u32, nice: i32) -> io::Result<()> {
37    if !(0..=19).contains(&nice) {
38        return Err(io::Error::new(
39            io::ErrorKind::InvalidInput,
40            "nice value must be between 0 and 19",
41        ));
42    }
43    Ok(())
44}
45
46/// Kill an owned child process group when an asynchronous operation is dropped.
47/// Callers must start the child in a new group whose ID equals its PID, and
48/// disarm after normal completion. The child handle remains responsible for reaping.
49pub struct ProcessGroupGuard {
50    pid: Option<i32>,
51}
52
53impl ProcessGroupGuard {
54    pub fn new(pid: u32) -> Self {
55        Self {
56            pid: i32::try_from(pid).ok().filter(|pid| *pid > 0),
57        }
58    }
59
60    pub fn disarm(&mut self) {
61        self.pid = None;
62    }
63}
64
65impl Drop for ProcessGroupGuard {
66    fn drop(&mut self) {
67        #[cfg(unix)]
68        if let Some(pid) = self.pid {
69            // SAFETY: callers supply the PID of their own newly isolated group.
70            unsafe {
71                libc::kill(-pid, libc::SIGKILL);
72            }
73        }
74    }
75}
76
77#[cfg(test)]
78#[path = "tests/process/mod.rs"]
79mod tests;