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