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