]>
Commit | Line | Data |
---|---|---|
29967ef6 | 1 | use crate::fmt; |
416331ca | 2 | use crate::io::{self, Error, ErrorKind}; |
416331ca XL |
3 | use crate::sys; |
4 | use crate::sys::cvt; | |
416331ca | 5 | use crate::sys::process::process_common::*; |
e1599b0c | 6 | use crate::sys_common::thread; |
dfeec247 XL |
7 | use libc::RTP_ID; |
8 | use libc::{self, c_char, c_int}; | |
416331ca XL |
9 | |
10 | //////////////////////////////////////////////////////////////////////////////// | |
11 | // Command | |
12 | //////////////////////////////////////////////////////////////////////////////// | |
13 | ||
14 | impl Command { | |
dfeec247 XL |
15 | pub fn spawn( |
16 | &mut self, | |
17 | default: Stdio, | |
18 | needs_stdin: bool, | |
19 | ) -> io::Result<(Process, StdioPipes)> { | |
20 | use crate::sys::cvt_r; | |
416331ca | 21 | const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; |
60c5eb7d | 22 | let envp = self.capture_env(); |
416331ca XL |
23 | |
24 | if self.saw_nul() { | |
dfeec247 | 25 | return Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data")); |
416331ca XL |
26 | } |
27 | let (ours, theirs) = self.setup_io(default, needs_stdin)?; | |
28 | let mut p = Process { pid: 0, status: None }; | |
29 | ||
30 | unsafe { | |
31 | macro_rules! t { | |
dfeec247 XL |
32 | ($e:expr) => { |
33 | match $e { | |
34 | Ok(e) => e, | |
35 | Err(e) => return Err(e.into()), | |
36 | } | |
37 | }; | |
416331ca XL |
38 | } |
39 | ||
40 | let mut orig_stdin = libc::STDIN_FILENO; | |
41 | let mut orig_stdout = libc::STDOUT_FILENO; | |
42 | let mut orig_stderr = libc::STDERR_FILENO; | |
43 | ||
44 | if let Some(fd) = theirs.stdin.fd() { | |
45 | orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO))); | |
46 | t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); | |
47 | } | |
48 | if let Some(fd) = theirs.stdout.fd() { | |
49 | orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO))); | |
50 | t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); | |
51 | } | |
52 | if let Some(fd) = theirs.stderr.fd() { | |
53 | orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO))); | |
54 | t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); | |
55 | } | |
56 | ||
57 | if let Some(ref cwd) = *self.get_cwd() { | |
58 | t!(cvt(libc::chdir(cwd.as_ptr()))); | |
59 | } | |
60 | ||
dfeec247 XL |
61 | let c_envp = envp |
62 | .as_ref() | |
63 | .map(|c| c.as_ptr()) | |
60c5eb7d XL |
64 | .unwrap_or_else(|| *sys::os::environ() as *const _); |
65 | let stack_size = thread::min_stack(); | |
66 | ||
67 | // ensure that access to the environment is synchronized | |
68 | let _lock = sys::os::env_lock(); | |
69 | ||
e1599b0c | 70 | let ret = libc::rtpSpawn( |
29967ef6 | 71 | self.get_program_cstr().as_ptr(), |
e1599b0c | 72 | self.get_argv().as_ptr() as *mut *const c_char, // argv |
60c5eb7d | 73 | c_envp as *mut *const c_char, |
dfeec247 XL |
74 | 100 as c_int, // initial priority |
75 | stack_size, // initial stack size. | |
76 | 0, // options | |
77 | 0, // task options | |
416331ca XL |
78 | ); |
79 | ||
80 | // Because FileDesc was not used, each duplicated file descriptor | |
81 | // needs to be closed manually | |
82 | if orig_stdin != libc::STDIN_FILENO { | |
83 | t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO))); | |
84 | libc::close(orig_stdin); | |
85 | } | |
86 | if orig_stdout != libc::STDOUT_FILENO { | |
87 | t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO))); | |
88 | libc::close(orig_stdout); | |
89 | } | |
90 | if orig_stderr != libc::STDERR_FILENO { | |
91 | t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO))); | |
92 | libc::close(orig_stderr); | |
93 | } | |
94 | ||
e1599b0c | 95 | if ret != libc::RTP_ID_ERROR { |
416331ca XL |
96 | p.pid = ret; |
97 | Ok((p, ours)) | |
98 | } else { | |
99 | Err(io::Error::last_os_error()) | |
100 | } | |
101 | } | |
102 | } | |
103 | ||
104 | pub fn exec(&mut self, default: Stdio) -> io::Error { | |
105 | let ret = Command::spawn(self, default, false); | |
106 | match ret { | |
107 | Ok(t) => unsafe { | |
108 | let mut status = 0 as c_int; | |
109 | libc::waitpid(t.0.pid, &mut status, 0); | |
110 | libc::exit(0); | |
111 | }, | |
112 | Err(e) => e, | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | //////////////////////////////////////////////////////////////////////////////// | |
118 | // Processes | |
119 | //////////////////////////////////////////////////////////////////////////////// | |
120 | ||
121 | /// The unique id of the process (this should never be negative). | |
122 | pub struct Process { | |
123 | pid: RTP_ID, | |
124 | status: Option<ExitStatus>, | |
125 | } | |
126 | ||
127 | impl Process { | |
128 | pub fn id(&self) -> u32 { | |
129 | self.pid as u32 | |
130 | } | |
131 | ||
132 | pub fn kill(&mut self) -> io::Result<()> { | |
133 | // If we've already waited on this process then the pid can be recycled | |
134 | // and used for another process, and we probably shouldn't be killing | |
135 | // random processes, so just return an error. | |
136 | if self.status.is_some() { | |
dfeec247 XL |
137 | Err(Error::new( |
138 | ErrorKind::InvalidInput, | |
139 | "invalid argument: can't kill an exited process", | |
140 | )) | |
416331ca | 141 | } else { |
dfeec247 | 142 | cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) |
416331ca XL |
143 | } |
144 | } | |
145 | ||
146 | pub fn wait(&mut self) -> io::Result<ExitStatus> { | |
147 | use crate::sys::cvt_r; | |
148 | if let Some(status) = self.status { | |
dfeec247 | 149 | return Ok(status); |
416331ca XL |
150 | } |
151 | let mut status = 0 as c_int; | |
152 | cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; | |
153 | self.status = Some(ExitStatus::new(status)); | |
154 | Ok(ExitStatus::new(status)) | |
155 | } | |
156 | ||
157 | pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { | |
158 | if let Some(status) = self.status { | |
dfeec247 | 159 | return Ok(Some(status)); |
416331ca XL |
160 | } |
161 | let mut status = 0 as c_int; | |
dfeec247 | 162 | let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; |
416331ca XL |
163 | if pid == 0 { |
164 | Ok(None) | |
165 | } else { | |
166 | self.status = Some(ExitStatus::new(status)); | |
167 | Ok(Some(ExitStatus::new(status))) | |
168 | } | |
169 | } | |
170 | } | |
29967ef6 XL |
171 | |
172 | /// Unix exit statuses | |
173 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
174 | pub struct ExitStatus(c_int); | |
175 | ||
176 | impl ExitStatus { | |
177 | pub fn new(status: c_int) -> ExitStatus { | |
178 | ExitStatus(status) | |
179 | } | |
180 | ||
181 | fn exited(&self) -> bool { | |
182 | libc::WIFEXITED(self.0) | |
183 | } | |
184 | ||
185 | pub fn success(&self) -> bool { | |
186 | self.code() == Some(0) | |
187 | } | |
188 | ||
189 | pub fn code(&self) -> Option<i32> { | |
190 | if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } | |
191 | } | |
192 | ||
193 | pub fn signal(&self) -> Option<i32> { | |
194 | if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } | |
195 | } | |
196 | } | |
197 | ||
198 | /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. | |
199 | impl From<c_int> for ExitStatus { | |
200 | fn from(a: c_int) -> ExitStatus { | |
201 | ExitStatus(a) | |
202 | } | |
203 | } | |
204 | ||
205 | impl fmt::Display for ExitStatus { | |
206 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
207 | if let Some(code) = self.code() { | |
208 | write!(f, "exit code: {}", code) | |
209 | } else { | |
210 | let signal = self.signal().unwrap(); | |
211 | write!(f, "signal: {}", signal) | |
212 | } | |
213 | } | |
214 | } |