Skip to main content

syd/unshare/
wait.rs

1use std::io;
2
3use nix::{
4    errno::{Errno, Errno::EINTR},
5    libc::pid_t,
6    sys::{
7        signal::{kill, Signal, SIGKILL},
8        wait::waitpid,
9    },
10    unistd::Pid,
11};
12
13use crate::unshare::{Child, ExitStatus};
14
15impl Child {
16    /// Returns pid of the process (a mirror of std method)
17    #[expect(clippy::cast_sign_loss)]
18    pub fn id(&self) -> u32 {
19        self.pid as u32
20    }
21
22    /// Returns pid of process with correct `pid_t` type
23    pub fn pid(&self) -> pid_t {
24        self.pid
25    }
26
27    /// Synchronously wait for child to complete and return exit status
28    pub fn wait(&mut self) -> Result<ExitStatus, io::Error> {
29        if let Some(x) = self.status {
30            return Ok(x);
31        }
32        let status = self._wait()?;
33        self.status = Some(status);
34        Ok(status)
35    }
36
37    fn _wait(&mut self) -> Result<ExitStatus, io::Error> {
38        use nix::sys::wait::WaitStatus::*;
39        loop {
40            match waitpid(Some(Pid::from_raw(self.pid)), None) {
41                Ok(PtraceEvent(..)) => {}
42                Ok(PtraceSyscall(..)) => {}
43                Ok(Exited(x, status)) => {
44                    assert!(i32::from(x) == self.pid);
45                    #[expect(clippy::cast_possible_truncation)]
46                    return Ok(ExitStatus::Exited(status as i8));
47                }
48                Ok(Signaled(x, sig, core)) => {
49                    assert!(i32::from(x) == self.pid);
50                    return Ok(ExitStatus::Signaled(sig, core));
51                }
52                Ok(Stopped(_, _)) => unreachable!(),
53                Ok(Continued(_)) => unreachable!(),
54                Ok(StillAlive) => unreachable!(),
55                Err(EINTR) => continue,
56                Err(errno) => return Err(io::Error::from_raw_os_error(errno as i32)),
57            }
58        }
59    }
60
61    /// Send arbitrary unix signal to the process
62    pub fn signal(&self, signal: Signal) -> Result<(), Errno> {
63        // This prevents (somewhat not-reliable) killing some other process
64        // with same pid.
65        if self.status.is_none() {
66            kill(Pid::from_raw(self.pid), signal)
67        } else {
68            // Invalid argument: Can't kill an exited process.
69            Err(Errno::ESRCH)
70        }
71    }
72
73    /// Kill process with SIGKILL signal
74    pub fn kill(&self) -> Result<(), Errno> {
75        self.signal(SIGKILL)
76    }
77}