]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/process/process_vxworks.rs
Rollup merge of #105824 - zacchiro:patch-1, r=JohnTitor
[rust.git] / library / std / src / sys / unix / process / process_vxworks.rs
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 {
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
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
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
147         // random processes, so just return an error.
148         if self.status.is_some() {
149             Err(io::const_io_error!(
150                 ErrorKind::InvalidInput,
151                 "invalid argument: can't kill an exited process",
152             ))
153         } else {
154             cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
155         }
156     }
157
158     pub fn wait(&mut self) -> io::Result<ExitStatus> {
159         use crate::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 { libc::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 { libc::waitpid(self.pid, &mut status, libc::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)]
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         libc::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         // procsss_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() { Some(libc::WEXITSTATUS(self.0)) } else { None }
211     }
212
213     pub fn signal(&self) -> Option<i32> {
214         if !self.exited() { Some(libc::WTERMSIG(self.0)) } else { None }
215     }
216
217     pub fn core_dumped(&self) -> bool {
218         // This method is not yet properly implemented on VxWorks
219         false
220     }
221
222     pub fn stopped_signal(&self) -> Option<i32> {
223         if libc::WIFSTOPPED(self.0) { Some(libc::WSTOPSIG(self.0)) } else { None }
224     }
225
226     pub fn continued(&self) -> bool {
227         // This method is not yet properly implemented on VxWorks
228         false
229     }
230
231     pub fn into_raw(&self) -> c_int {
232         self.0
233     }
234 }
235
236 /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
237 impl From<c_int> for ExitStatus {
238     fn from(a: c_int) -> ExitStatus {
239         ExitStatus(a)
240     }
241 }
242
243 impl fmt::Display for ExitStatus {
244     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245         if let Some(code) = self.code() {
246             write!(f, "exit code: {code}")
247         } else {
248             let signal = self.signal().unwrap();
249             write!(f, "signal: {signal}")
250         }
251     }
252 }
253
254 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
255 pub struct ExitStatusError(NonZero_c_int);
256
257 impl Into<ExitStatus> for ExitStatusError {
258     fn into(self) -> ExitStatus {
259         ExitStatus(self.0.into())
260     }
261 }
262
263 impl ExitStatusError {
264     pub fn code(self) -> Option<NonZeroI32> {
265         ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
266     }
267 }