1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use std::io::{Error, ErrorKind, Result};
use std::os::unix::process::ExitStatusExt;
use std::process::ExitStatus;
use libc::pid_t;
use tokio::signal::unix::{signal, SignalKind};
#[derive(Debug)]
pub struct Child {
pid: pid_t,
status: Option<ExitStatus>,
}
#[derive(Debug)]
pub enum Fork {
Parent(Child),
Child,
}
macro_rules! cvt {
($e:expr) => {
match $e {
-1 => Err(Error::last_os_error()),
x => Ok(x),
}
};
}
pub unsafe fn fork() -> Result<Fork> {
match cvt!(libc::fork())? {
0 => Ok(Fork::Child),
pid => Ok(Fork::Parent(Child { pid, status: None })),
}
}
impl Child {
pub fn pid(&self) -> pid_t {
self.pid
}
pub fn kill(&mut self) -> Result<()> {
if self.status.is_some() {
Err(Error::new(
ErrorKind::InvalidInput,
"invalid argument: can't kill an exited process",
))
} else {
cvt!(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
}
}
pub async fn wait(&mut self) -> Result<ExitStatus> {
if let Some(status) = self.try_wait()? {
return Ok(status);
}
let mut sigchld = signal(SignalKind::child())?;
loop {
sigchld.recv().await;
if let Some(x) = self.try_wait()? {
break Ok(x);
}
}
}
pub fn block(&mut self) -> Result<ExitStatus> {
if let Some(status) = self.status {
return Ok(status);
}
let mut status = 0;
loop {
match cvt!(unsafe { libc::waitpid(self.pid, &mut status, 0) }) {
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
x => break x,
}
}?;
self.status = Some(ExitStatus::from_raw(status));
Ok(ExitStatus::from_raw(status))
}
pub fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
if let Some(status) = self.status {
return Ok(Some(status));
}
let mut status = 0;
let pid = cvt!(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
if pid == 0 {
Ok(None)
} else {
self.status = Some(ExitStatus::from_raw(status));
Ok(Some(ExitStatus::from_raw(status)))
}
}
}