• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::fmt;
2 use crate::io::{self, Error, ErrorKind};
3 use crate::num::NonZeroI32;
4 use crate::sys;
5 use crate::sys::cvt;
6 use crate::sys::process::process_common::*;
7 use crate::sys_common::thread;
8 use core::ffi::NonZero_c_int;
9 use libc::RTP_ID;
10 use libc::{self, c_char, c_int};
11 
12 ////////////////////////////////////////////////////////////////////////////////
13 // Command
14 ////////////////////////////////////////////////////////////////////////////////
15 
16 impl Command {
spawn( &mut self, default: Stdio, needs_stdin: bool, ) -> io::Result<(Process, StdioPipes)>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;
23         let envp = self.capture_env();
24 
25         if self.saw_nul() {
26             return Err(io::const_io_error!(
27                 ErrorKind::InvalidInput,
28                 "nul byte found in provided data",
29             ));
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 {
36                 ($e:expr) => {
37                     match $e {
38                         Ok(e) => e,
39                         Err(e) => return Err(e.into()),
40                     }
41                 };
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 
65             // pre_exec closures are ignored on VxWorks
66             let _ = self.get_closures();
67 
68             let c_envp = envp
69                 .as_ref()
70                 .map(|c| c.as_ptr())
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
75             let _lock = sys::os::env_read_lock();
76 
77             let ret = libc::rtpSpawn(
78                 self.get_program_cstr().as_ptr(),
79                 self.get_argv().as_ptr() as *mut *const c_char, // argv
80                 c_envp as *mut *const c_char,
81                 100 as c_int, // initial priority
82                 stack_size,   // initial stack size.
83                 0,            // options
84                 0,            // task options
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 
102             if ret != libc::RTP_ID_ERROR {
103                 p.pid = ret;
104                 Ok((p, ours))
105             } else {
106                 Err(io::Error::last_os_error())
107             }
108         }
109     }
110 
output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)>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 
exec(&mut self, default: Stdio) -> io::Error116     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 {
id(&self) -> u32140     pub fn id(&self) -> u32 {
141         self.pid as u32
142     }
143 
kill(&mut self) -> io::Result<()>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
147         // random processes, so return Ok because the process has exited already.
148         if self.status.is_some() {
149             Ok(())
150         } else {
151             cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
152         }
153     }
154 
wait(&mut self) -> io::Result<ExitStatus>155     pub fn wait(&mut self) -> io::Result<ExitStatus> {
156         use crate::sys::cvt_r;
157         if let Some(status) = self.status {
158             return Ok(status);
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 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>166     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
167         if let Some(status) = self.status {
168             return Ok(Some(status));
169         }
170         let mut status = 0 as c_int;
171         let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
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 }
180 
181 /// Unix exit statuses
182 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
183 pub struct ExitStatus(c_int);
184 
185 impl ExitStatus {
new(status: c_int) -> ExitStatus186     pub fn new(status: c_int) -> ExitStatus {
187         ExitStatus(status)
188     }
189 
exited(&self) -> bool190     fn exited(&self) -> bool {
191         libc::WIFEXITED(self.0)
192     }
193 
exit_ok(&self) -> Result<(), ExitStatusError>194     pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
195         // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
196         // true on all actual versions of Unix, is widely assumed, and is specified in SuS
197         // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
198         // true for a platform pretending to be Unix, the tests (our doctests, and also
199         // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
200         match NonZero_c_int::try_from(self.0) {
201             Ok(failure) => Err(ExitStatusError(failure)),
202             Err(_) => Ok(()),
203         }
204     }
205 
code(&self) -> Option<i32>206     pub fn code(&self) -> Option<i32> {
207         if self.exited() { Some(libc::WEXITSTATUS(self.0)) } else { None }
208     }
209 
signal(&self) -> Option<i32>210     pub fn signal(&self) -> Option<i32> {
211         if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
212     }
213 
core_dumped(&self) -> bool214     pub fn core_dumped(&self) -> bool {
215         // This method is not yet properly implemented on VxWorks
216         false
217     }
218 
stopped_signal(&self) -> Option<i32>219     pub fn stopped_signal(&self) -> Option<i32> {
220         if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None }
221     }
222 
continued(&self) -> bool223     pub fn continued(&self) -> bool {
224         // This method is not yet properly implemented on VxWorks
225         false
226     }
227 
into_raw(&self) -> c_int228     pub fn into_raw(&self) -> c_int {
229         self.0
230     }
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 {
from(a: c_int) -> ExitStatus235     fn from(a: c_int) -> ExitStatus {
236         ExitStatus(a)
237     }
238 }
239 
240 impl fmt::Display for ExitStatus {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result241     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242         if let Some(code) = self.code() {
243             write!(f, "exit code: {code}")
244         } else {
245             let signal = self.signal().unwrap();
246             write!(f, "signal: {signal}")
247         }
248     }
249 }
250 
251 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
252 pub struct ExitStatusError(NonZero_c_int);
253 
254 impl Into<ExitStatus> for ExitStatusError {
into(self) -> ExitStatus255     fn into(self) -> ExitStatus {
256         ExitStatus(self.0.into())
257     }
258 }
259 
260 impl ExitStatusError {
code(self) -> Option<NonZeroI32>261     pub fn code(self) -> Option<NonZeroI32> {
262         ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
263     }
264 }
265