]>
Commit | Line | Data |
---|---|---|
29967ef6 | 1 | use crate::fmt; |
416331ca | 2 | use crate::io::{self, Error, ErrorKind}; |
17df50a5 | 3 | use crate::num::NonZeroI32; |
416331ca XL |
4 | use crate::sys; |
5 | use crate::sys::cvt; | |
416331ca | 6 | use crate::sys::process::process_common::*; |
e1599b0c | 7 | use crate::sys_common::thread; |
5e7ed085 | 8 | use core::ffi::NonZero_c_int; |
dfeec247 XL |
9 | use libc::RTP_ID; |
10 | use libc::{self, c_char, c_int}; | |
416331ca XL |
11 | |
12 | //////////////////////////////////////////////////////////////////////////////// | |
13 | // Command | |
14 | //////////////////////////////////////////////////////////////////////////////// | |
15 | ||
16 | impl Command { | |
dfeec247 XL |
17 | pub fn spawn( |
18 | &mut self, | |
19 | default: Stdio, | |
20 | needs_stdin: bool, | |
21 | ) -> io::Result<(Process, StdioPipes)> { | |
22 | use crate::sys::cvt_r; | |
60c5eb7d | 23 | let envp = self.capture_env(); |
416331ca XL |
24 | |
25 | if self.saw_nul() { | |
5099ac24 | 26 | return Err(io::const_io_error!( |
cdc7bbd5 | 27 | ErrorKind::InvalidInput, |
5099ac24 | 28 | "nul byte found in provided data", |
cdc7bbd5 | 29 | )); |
416331ca XL |
30 | } |
31 | let (ours, theirs) = self.setup_io(default, needs_stdin)?; | |
32 | let mut p = Process { pid: 0, status: None }; | |
33 | ||
34 | unsafe { | |
35 | macro_rules! t { | |
dfeec247 XL |
36 | ($e:expr) => { |
37 | match $e { | |
38 | Ok(e) => e, | |
39 | Err(e) => return Err(e.into()), | |
40 | } | |
41 | }; | |
416331ca XL |
42 | } |
43 | ||
44 | let mut orig_stdin = libc::STDIN_FILENO; | |
45 | let mut orig_stdout = libc::STDOUT_FILENO; | |
46 | let mut orig_stderr = libc::STDERR_FILENO; | |
47 | ||
48 | if let Some(fd) = theirs.stdin.fd() { | |
49 | orig_stdin = t!(cvt_r(|| libc::dup(libc::STDIN_FILENO))); | |
50 | t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); | |
51 | } | |
52 | if let Some(fd) = theirs.stdout.fd() { | |
53 | orig_stdout = t!(cvt_r(|| libc::dup(libc::STDOUT_FILENO))); | |
54 | t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); | |
55 | } | |
56 | if let Some(fd) = theirs.stderr.fd() { | |
57 | orig_stderr = t!(cvt_r(|| libc::dup(libc::STDERR_FILENO))); | |
58 | t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); | |
59 | } | |
60 | ||
61 | if let Some(ref cwd) = *self.get_cwd() { | |
62 | t!(cvt(libc::chdir(cwd.as_ptr()))); | |
63 | } | |
64 | ||
cdc7bbd5 XL |
65 | // pre_exec closures are ignored on VxWorks |
66 | let _ = self.get_closures(); | |
67 | ||
dfeec247 XL |
68 | let c_envp = envp |
69 | .as_ref() | |
70 | .map(|c| c.as_ptr()) | |
60c5eb7d XL |
71 | .unwrap_or_else(|| *sys::os::environ() as *const _); |
72 | let stack_size = thread::min_stack(); | |
73 | ||
74 | // ensure that access to the environment is synchronized | |
cdc7bbd5 | 75 | let _lock = sys::os::env_read_lock(); |
60c5eb7d | 76 | |
e1599b0c | 77 | let ret = libc::rtpSpawn( |
29967ef6 | 78 | self.get_program_cstr().as_ptr(), |
e1599b0c | 79 | self.get_argv().as_ptr() as *mut *const c_char, // argv |
60c5eb7d | 80 | c_envp as *mut *const c_char, |
dfeec247 XL |
81 | 100 as c_int, // initial priority |
82 | stack_size, // initial stack size. | |
83 | 0, // options | |
84 | 0, // task options | |
416331ca XL |
85 | ); |
86 | ||
87 | // Because FileDesc was not used, each duplicated file descriptor | |
88 | // needs to be closed manually | |
89 | if orig_stdin != libc::STDIN_FILENO { | |
90 | t!(cvt_r(|| libc::dup2(orig_stdin, libc::STDIN_FILENO))); | |
91 | libc::close(orig_stdin); | |
92 | } | |
93 | if orig_stdout != libc::STDOUT_FILENO { | |
94 | t!(cvt_r(|| libc::dup2(orig_stdout, libc::STDOUT_FILENO))); | |
95 | libc::close(orig_stdout); | |
96 | } | |
97 | if orig_stderr != libc::STDERR_FILENO { | |
98 | t!(cvt_r(|| libc::dup2(orig_stderr, libc::STDERR_FILENO))); | |
99 | libc::close(orig_stderr); | |
100 | } | |
101 | ||
e1599b0c | 102 | if ret != libc::RTP_ID_ERROR { |
416331ca XL |
103 | p.pid = ret; |
104 | Ok((p, ours)) | |
105 | } else { | |
106 | Err(io::Error::last_os_error()) | |
107 | } | |
108 | } | |
109 | } | |
110 | ||
9c376795 FG |
111 | pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> { |
112 | let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; | |
113 | crate::sys_common::process::wait_with_output(proc, pipes) | |
114 | } | |
115 | ||
416331ca XL |
116 | pub fn exec(&mut self, default: Stdio) -> io::Error { |
117 | let ret = Command::spawn(self, default, false); | |
118 | match ret { | |
119 | Ok(t) => unsafe { | |
120 | let mut status = 0 as c_int; | |
121 | libc::waitpid(t.0.pid, &mut status, 0); | |
122 | libc::exit(0); | |
123 | }, | |
124 | Err(e) => e, | |
125 | } | |
126 | } | |
127 | } | |
128 | ||
129 | //////////////////////////////////////////////////////////////////////////////// | |
130 | // Processes | |
131 | //////////////////////////////////////////////////////////////////////////////// | |
132 | ||
133 | /// The unique id of the process (this should never be negative). | |
134 | pub struct Process { | |
135 | pid: RTP_ID, | |
136 | status: Option<ExitStatus>, | |
137 | } | |
138 | ||
139 | impl Process { | |
140 | pub fn id(&self) -> u32 { | |
141 | self.pid as u32 | |
142 | } | |
143 | ||
144 | pub fn kill(&mut self) -> io::Result<()> { | |
145 | // If we've already waited on this process then the pid can be recycled | |
146 | // and used for another process, and we probably shouldn't be killing | |
fe692bf9 | 147 | // random processes, so return Ok because the process has exited already. |
416331ca | 148 | if self.status.is_some() { |
fe692bf9 | 149 | Ok(()) |
416331ca | 150 | } else { |
dfeec247 | 151 | cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop) |
416331ca XL |
152 | } |
153 | } | |
154 | ||
155 | pub fn wait(&mut self) -> io::Result<ExitStatus> { | |
156 | use crate::sys::cvt_r; | |
157 | if let Some(status) = self.status { | |
dfeec247 | 158 | return Ok(status); |
416331ca XL |
159 | } |
160 | let mut status = 0 as c_int; | |
161 | cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; | |
162 | self.status = Some(ExitStatus::new(status)); | |
163 | Ok(ExitStatus::new(status)) | |
164 | } | |
165 | ||
166 | pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { | |
167 | if let Some(status) = self.status { | |
dfeec247 | 168 | return Ok(Some(status)); |
416331ca XL |
169 | } |
170 | let mut status = 0 as c_int; | |
dfeec247 | 171 | let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?; |
416331ca XL |
172 | if pid == 0 { |
173 | Ok(None) | |
174 | } else { | |
175 | self.status = Some(ExitStatus::new(status)); | |
176 | Ok(Some(ExitStatus::new(status))) | |
177 | } | |
178 | } | |
179 | } | |
29967ef6 XL |
180 | |
181 | /// Unix exit statuses | |
182 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
183 | pub struct ExitStatus(c_int); | |
184 | ||
185 | impl ExitStatus { | |
186 | pub fn new(status: c_int) -> ExitStatus { | |
187 | ExitStatus(status) | |
188 | } | |
189 | ||
190 | fn exited(&self) -> bool { | |
191 | libc::WIFEXITED(self.0) | |
192 | } | |
193 | ||
17df50a5 | 194 | pub fn exit_ok(&self) -> Result<(), ExitStatusError> { |
9c376795 | 195 | // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is |
17df50a5 | 196 | // true on all actual versions of Unix, is widely assumed, and is specified in SuS |
9c376795 | 197 | // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not |
17df50a5 | 198 | // true for a platform pretending to be Unix, the tests (our doctests, and also |
49aad941 | 199 | // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. |
17df50a5 XL |
200 | match NonZero_c_int::try_from(self.0) { |
201 | Ok(failure) => Err(ExitStatusError(failure)), | |
202 | Err(_) => Ok(()), | |
203 | } | |
29967ef6 XL |
204 | } |
205 | ||
206 | pub fn code(&self) -> Option<i32> { | |
207 | if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None } | |
208 | } | |
209 | ||
210 | pub fn signal(&self) -> Option<i32> { | |
211 | if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None } | |
212 | } | |
cdc7bbd5 XL |
213 | |
214 | pub fn core_dumped(&self) -> bool { | |
215 | // This method is not yet properly implemented on VxWorks | |
216 | false | |
217 | } | |
218 | ||
219 | pub fn stopped_signal(&self) -> Option<i32> { | |
220 | if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None } | |
221 | } | |
222 | ||
223 | pub fn continued(&self) -> bool { | |
224 | // This method is not yet properly implemented on VxWorks | |
225 | false | |
226 | } | |
227 | ||
228 | pub fn into_raw(&self) -> c_int { | |
229 | self.0 | |
230 | } | |
29967ef6 XL |
231 | } |
232 | ||
233 | /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. | |
234 | impl From<c_int> for ExitStatus { | |
235 | fn from(a: c_int) -> ExitStatus { | |
236 | ExitStatus(a) | |
237 | } | |
238 | } | |
239 | ||
240 | impl fmt::Display for ExitStatus { | |
241 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
242 | if let Some(code) = self.code() { | |
5e7ed085 | 243 | write!(f, "exit code: {code}") |
29967ef6 XL |
244 | } else { |
245 | let signal = self.signal().unwrap(); | |
5e7ed085 | 246 | write!(f, "signal: {signal}") |
29967ef6 XL |
247 | } |
248 | } | |
249 | } | |
17df50a5 XL |
250 | |
251 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
252 | pub struct ExitStatusError(NonZero_c_int); | |
253 | ||
254 | impl Into<ExitStatus> for ExitStatusError { | |
255 | fn into(self) -> ExitStatus { | |
256 | ExitStatus(self.0.into()) | |
257 | } | |
258 | } | |
259 | ||
260 | impl ExitStatusError { | |
261 | pub fn code(self) -> Option<NonZeroI32> { | |
262 | ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) | |
263 | } | |
264 | } |