]>
Commit | Line | Data |
---|---|---|
e74abb32 | 1 | use crate::fmt; |
60c5eb7d | 2 | use crate::io; |
532ac7d7 | 3 | use crate::mem; |
17df50a5 | 4 | use crate::num::{NonZeroI32, NonZeroI64}; |
532ac7d7 | 5 | use crate::ptr; |
476ff2be | 6 | |
532ac7d7 | 7 | use crate::sys::process::process_common::*; |
60c5eb7d | 8 | use crate::sys::process::zircon::{zx_handle_t, Handle}; |
532ac7d7 | 9 | |
e74abb32 | 10 | use libc::{c_int, size_t}; |
476ff2be SL |
11 | |
12 | //////////////////////////////////////////////////////////////////////////////// | |
13 | // Command | |
14 | //////////////////////////////////////////////////////////////////////////////// | |
15 | ||
16 | impl Command { | |
60c5eb7d XL |
17 | pub fn spawn( |
18 | &mut self, | |
19 | default: Stdio, | |
20 | needs_stdin: bool, | |
21 | ) -> io::Result<(Process, StdioPipes)> { | |
ff7c6d11 XL |
22 | let envp = self.capture_env(); |
23 | ||
476ff2be | 24 | if self.saw_nul() { |
5099ac24 | 25 | return Err(io::const_io_error!( |
60c5eb7d | 26 | io::ErrorKind::InvalidInput, |
5099ac24 | 27 | "nul byte found in provided data", |
60c5eb7d | 28 | )); |
476ff2be SL |
29 | } |
30 | ||
31 | let (ours, theirs) = self.setup_io(default, needs_stdin)?; | |
32 | ||
ff7c6d11 | 33 | let process_handle = unsafe { self.do_exec(theirs, envp.as_ref())? }; |
476ff2be | 34 | |
8bb4bdeb | 35 | Ok((Process { handle: Handle::new(process_handle) }, ours)) |
476ff2be SL |
36 | } |
37 | ||
9c376795 FG |
38 | pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> { |
39 | let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; | |
40 | crate::sys_common::process::wait_with_output(proc, pipes) | |
41 | } | |
42 | ||
476ff2be SL |
43 | pub fn exec(&mut self, default: Stdio) -> io::Error { |
44 | if self.saw_nul() { | |
5099ac24 | 45 | return io::const_io_error!( |
cdc7bbd5 | 46 | io::ErrorKind::InvalidInput, |
5099ac24 | 47 | "nul byte found in provided data", |
cdc7bbd5 | 48 | ); |
476ff2be SL |
49 | } |
50 | ||
51 | match self.setup_io(default, true) { | |
52 | Ok((_, _)) => { | |
53 | // FIXME: This is tough because we don't support the exec syscalls | |
54 | unimplemented!(); | |
60c5eb7d | 55 | } |
476ff2be SL |
56 | Err(e) => e, |
57 | } | |
58 | } | |
59 | ||
60c5eb7d XL |
60 | unsafe fn do_exec( |
61 | &mut self, | |
62 | stdio: ChildPipes, | |
63 | maybe_envp: Option<&CStringArray>, | |
64 | ) -> io::Result<zx_handle_t> { | |
532ac7d7 | 65 | use crate::sys::process::zircon::*; |
476ff2be | 66 | |
ff7c6d11 | 67 | let envp = match maybe_envp { |
e1599b0c XL |
68 | // None means to clone the current environment, which is done in the |
69 | // flags below. | |
476ff2be | 70 | None => ptr::null(), |
e1599b0c | 71 | Some(envp) => envp.as_ptr(), |
476ff2be SL |
72 | }; |
73 | ||
e1599b0c XL |
74 | let make_action = |local_io: &ChildStdio, target_fd| -> io::Result<fdio_spawn_action_t> { |
75 | if let Some(local_fd) = local_io.fd() { | |
76 | Ok(fdio_spawn_action_t { | |
77 | action: FDIO_SPAWN_ACTION_TRANSFER_FD, | |
78 | local_fd, | |
79 | target_fd, | |
80 | ..Default::default() | |
81 | }) | |
82 | } else { | |
83 | if let ChildStdio::Null = local_io { | |
84 | // acts as no-op | |
85 | return Ok(Default::default()); | |
86 | } | |
87 | ||
88 | let mut handle = ZX_HANDLE_INVALID; | |
89 | let status = fdio_fd_clone(target_fd, &mut handle); | |
90 | if status == ERR_INVALID_ARGS || status == ERR_NOT_SUPPORTED { | |
91 | // This descriptor is closed; skip it rather than generating an | |
92 | // error. | |
93 | return Ok(Default::default()); | |
94 | } | |
95 | zx_cvt(status)?; | |
96 | ||
97 | let mut cloned_fd = 0; | |
98 | zx_cvt(fdio_fd_create(handle, &mut cloned_fd))?; | |
99 | ||
100 | Ok(fdio_spawn_action_t { | |
101 | action: FDIO_SPAWN_ACTION_TRANSFER_FD, | |
102 | local_fd: cloned_fd as i32, | |
103 | target_fd, | |
104 | ..Default::default() | |
105 | }) | |
94b46f34 XL |
106 | } |
107 | }; | |
476ff2be SL |
108 | |
109 | // Clone stdin, stdout, and stderr | |
e1599b0c XL |
110 | let action1 = make_action(&stdio.stdin, 0)?; |
111 | let action2 = make_action(&stdio.stdout, 1)?; | |
112 | let action3 = make_action(&stdio.stderr, 2)?; | |
94b46f34 | 113 | let actions = [action1, action2, action3]; |
476ff2be | 114 | |
94b46f34 XL |
115 | // We don't want FileDesc::drop to be called on any stdio. fdio_spawn_etc |
116 | // always consumes transferred file descriptors. | |
476ff2be SL |
117 | mem::forget(stdio); |
118 | ||
119 | for callback in self.get_closures().iter_mut() { | |
120 | callback()?; | |
121 | } | |
122 | ||
ea8adc8c | 123 | let mut process_handle: zx_handle_t = 0; |
94b46f34 | 124 | zx_cvt(fdio_spawn_etc( |
e1599b0c | 125 | ZX_HANDLE_INVALID, |
60c5eb7d XL |
126 | FDIO_SPAWN_CLONE_JOB |
127 | | FDIO_SPAWN_CLONE_LDSVC | |
128 | | FDIO_SPAWN_CLONE_NAMESPACE | |
1b1a35ee XL |
129 | | FDIO_SPAWN_CLONE_ENVIRON // this is ignored when envp is non-null |
130 | | FDIO_SPAWN_CLONE_UTC_CLOCK, | |
131 | self.get_program_cstr().as_ptr(), | |
60c5eb7d XL |
132 | self.get_argv().as_ptr(), |
133 | envp, | |
134 | actions.len() as size_t, | |
135 | actions.as_ptr(), | |
94b46f34 XL |
136 | &mut process_handle, |
137 | ptr::null_mut(), | |
138 | ))?; | |
8bb4bdeb XL |
139 | // FIXME: See if we want to do something with that err_msg |
140 | ||
141 | Ok(process_handle) | |
476ff2be SL |
142 | } |
143 | } | |
144 | ||
145 | //////////////////////////////////////////////////////////////////////////////// | |
146 | // Processes | |
147 | //////////////////////////////////////////////////////////////////////////////// | |
148 | ||
149 | pub struct Process { | |
476ff2be SL |
150 | handle: Handle, |
151 | } | |
152 | ||
153 | impl Process { | |
154 | pub fn id(&self) -> u32 { | |
155 | self.handle.raw() as u32 | |
156 | } | |
157 | ||
158 | pub fn kill(&mut self) -> io::Result<()> { | |
532ac7d7 | 159 | use crate::sys::process::zircon::*; |
476ff2be | 160 | |
60c5eb7d XL |
161 | unsafe { |
162 | zx_cvt(zx_task_kill(self.handle.raw()))?; | |
163 | } | |
476ff2be SL |
164 | |
165 | Ok(()) | |
166 | } | |
167 | ||
168 | pub fn wait(&mut self) -> io::Result<ExitStatus> { | |
532ac7d7 | 169 | use crate::sys::process::zircon::*; |
476ff2be | 170 | |
ea8adc8c XL |
171 | let mut proc_info: zx_info_process_t = Default::default(); |
172 | let mut actual: size_t = 0; | |
173 | let mut avail: size_t = 0; | |
476ff2be SL |
174 | |
175 | unsafe { | |
60c5eb7d XL |
176 | zx_cvt(zx_object_wait_one( |
177 | self.handle.raw(), | |
178 | ZX_TASK_TERMINATED, | |
179 | ZX_TIME_INFINITE, | |
180 | ptr::null_mut(), | |
181 | ))?; | |
182 | zx_cvt(zx_object_get_info( | |
183 | self.handle.raw(), | |
184 | ZX_INFO_PROCESS, | |
185 | &mut proc_info as *mut _ as *mut libc::c_void, | |
186 | mem::size_of::<zx_info_process_t>(), | |
187 | &mut actual, | |
188 | &mut avail, | |
189 | ))?; | |
476ff2be SL |
190 | } |
191 | if actual != 1 { | |
5099ac24 | 192 | return Err(io::const_io_error!( |
60c5eb7d | 193 | io::ErrorKind::InvalidData, |
5099ac24 | 194 | "Failed to get exit status of process", |
60c5eb7d | 195 | )); |
476ff2be | 196 | } |
e74abb32 | 197 | Ok(ExitStatus(proc_info.return_code)) |
476ff2be | 198 | } |
32a655c1 | 199 | |
8bb4bdeb | 200 | pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { |
532ac7d7 | 201 | use crate::sys::process::zircon::*; |
32a655c1 | 202 | |
ea8adc8c XL |
203 | let mut proc_info: zx_info_process_t = Default::default(); |
204 | let mut actual: size_t = 0; | |
205 | let mut avail: size_t = 0; | |
32a655c1 SL |
206 | |
207 | unsafe { | |
60c5eb7d XL |
208 | let status = |
209 | zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, 0, ptr::null_mut()); | |
32a655c1 | 210 | match status { |
60c5eb7d | 211 | 0 => {} // Success |
32a655c1 | 212 | x if x == ERR_TIMED_OUT => { |
8bb4bdeb | 213 | return Ok(None); |
60c5eb7d XL |
214 | } |
215 | _ => { | |
5e7ed085 | 216 | panic!("Failed to wait on process handle: {status}"); |
60c5eb7d | 217 | } |
32a655c1 | 218 | } |
60c5eb7d XL |
219 | zx_cvt(zx_object_get_info( |
220 | self.handle.raw(), | |
221 | ZX_INFO_PROCESS, | |
222 | &mut proc_info as *mut _ as *mut libc::c_void, | |
223 | mem::size_of::<zx_info_process_t>(), | |
224 | &mut actual, | |
225 | &mut avail, | |
226 | ))?; | |
32a655c1 SL |
227 | } |
228 | if actual != 1 { | |
5099ac24 | 229 | return Err(io::const_io_error!( |
60c5eb7d | 230 | io::ErrorKind::InvalidData, |
5099ac24 | 231 | "Failed to get exit status of process", |
60c5eb7d | 232 | )); |
32a655c1 | 233 | } |
e74abb32 XL |
234 | Ok(Some(ExitStatus(proc_info.return_code))) |
235 | } | |
236 | } | |
237 | ||
238 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
239 | pub struct ExitStatus(i64); | |
240 | ||
241 | impl ExitStatus { | |
17df50a5 XL |
242 | pub fn exit_ok(&self) -> Result<(), ExitStatusError> { |
243 | match NonZeroI64::try_from(self.0) { | |
244 | /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)), | |
245 | /* was zero, couldn't convert */ Err(_) => Ok(()), | |
246 | } | |
e74abb32 XL |
247 | } |
248 | ||
249 | pub fn code(&self) -> Option<i32> { | |
250 | // FIXME: support extracting return code as an i64 | |
251 | self.0.try_into().ok() | |
252 | } | |
253 | ||
254 | pub fn signal(&self) -> Option<i32> { | |
255 | None | |
256 | } | |
5869c6ff XL |
257 | |
258 | // FIXME: The actually-Unix implementation in process_unix.rs uses WSTOPSIG, WCOREDUMP et al. | |
259 | // I infer from the implementation of `success`, `code` and `signal` above that these are not | |
260 | // available on Fuchsia. | |
261 | // | |
262 | // It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many | |
9c376795 | 263 | // other things from std::os::unix) properly. This veneer is always going to be a bodge. So |
5869c6ff XL |
264 | // while I don't know if these implementations are actually correct, I think they will do for |
265 | // now at least. | |
266 | pub fn core_dumped(&self) -> bool { | |
267 | false | |
268 | } | |
269 | pub fn stopped_signal(&self) -> Option<i32> { | |
270 | None | |
271 | } | |
272 | pub fn continued(&self) -> bool { | |
273 | false | |
274 | } | |
275 | ||
276 | pub fn into_raw(&self) -> c_int { | |
277 | // We don't know what someone who calls into_raw() will do with this value, but it should | |
9c376795 | 278 | // have the conventional Unix representation. Despite the fact that this is not |
5869c6ff | 279 | // standardised in SuS or POSIX, all Unix systems encode the signal and exit status the |
9c376795 | 280 | // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every |
5869c6ff XL |
281 | // Unix.) |
282 | // | |
283 | // The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may | |
284 | // do their own shifting and masking, or even pass the status to another computer running a | |
285 | // different Unix variant. | |
286 | // | |
287 | // The other view would be to say that the caller on Fuchsia ought to know that `into_raw` | |
9c376795 | 288 | // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is |
a2a8927a | 289 | // not possible here because we must return a c_int because that's what Unix (including |
5869c6ff XL |
290 | // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't |
291 | // necessarily fit. | |
292 | // | |
2b03887a | 293 | // It seems to me that the right answer would be to provide std::os::fuchsia with its |
5869c6ff | 294 | // own ExitStatusExt, rather that trying to provide a not very convincing imitation of |
9c376795 | 295 | // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But |
5869c6ff XL |
296 | // fixing this up that is beyond the scope of my efforts now. |
297 | let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255."); | |
298 | let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8; | |
299 | wait_status_as_if_unix | |
300 | } | |
e74abb32 XL |
301 | } |
302 | ||
3dfed10e | 303 | /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. |
e74abb32 XL |
304 | impl From<c_int> for ExitStatus { |
305 | fn from(a: c_int) -> ExitStatus { | |
306 | ExitStatus(a as i64) | |
307 | } | |
308 | } | |
309 | ||
310 | impl fmt::Display for ExitStatus { | |
311 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
312 | write!(f, "exit code: {}", self.0) | |
476ff2be SL |
313 | } |
314 | } | |
17df50a5 XL |
315 | |
316 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
317 | pub struct ExitStatusError(NonZeroI64); | |
318 | ||
319 | impl Into<ExitStatus> for ExitStatusError { | |
320 | fn into(self) -> ExitStatus { | |
321 | ExitStatus(self.0.into()) | |
322 | } | |
323 | } | |
324 | ||
325 | impl ExitStatusError { | |
326 | pub fn code(self) -> Option<NonZeroI32> { | |
327 | // fixme: affected by the same bug as ExitStatus::code() | |
328 | ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap()) | |
329 | } | |
330 | } |