]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/process/process_fuchsia.rs
Merge from rustc
[rust.git] / library / std / src / sys / unix / process / process_fuchsia.rs
1 use crate::fmt;
2 use crate::io;
3 use crate::mem;
4 use crate::num::{NonZeroI32, NonZeroI64};
5 use crate::ptr;
6
7 use crate::sys::process::process_common::*;
8 use crate::sys::process::zircon::{zx_handle_t, Handle};
9
10 use libc::{c_int, size_t};
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         let envp = self.capture_env();
23
24         if self.saw_nul() {
25             return Err(io::const_io_error!(
26                 io::ErrorKind::InvalidInput,
27                 "nul byte found in provided data",
28             ));
29         }
30
31         let (ours, theirs) = self.setup_io(default, needs_stdin)?;
32
33         let process_handle = unsafe { self.do_exec(theirs, envp.as_ref())? };
34
35         Ok((Process { handle: Handle::new(process_handle) }, ours))
36     }
37
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
43     pub fn exec(&mut self, default: Stdio) -> io::Error {
44         if self.saw_nul() {
45             return io::const_io_error!(
46                 io::ErrorKind::InvalidInput,
47                 "nul byte found in provided data",
48             );
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!();
55             }
56             Err(e) => e,
57         }
58     }
59
60     unsafe fn do_exec(
61         &mut self,
62         stdio: ChildPipes,
63         maybe_envp: Option<&CStringArray>,
64     ) -> io::Result<zx_handle_t> {
65         use crate::sys::process::zircon::*;
66
67         let envp = match maybe_envp {
68             // None means to clone the current environment, which is done in the
69             // flags below.
70             None => ptr::null(),
71             Some(envp) => envp.as_ptr(),
72         };
73
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                 })
106             }
107         };
108
109         // Clone stdin, stdout, and stderr
110         let action1 = make_action(&stdio.stdin, 0)?;
111         let action2 = make_action(&stdio.stdout, 1)?;
112         let action3 = make_action(&stdio.stderr, 2)?;
113         let actions = [action1, action2, action3];
114
115         // We don't want FileDesc::drop to be called on any stdio. fdio_spawn_etc
116         // always consumes transferred file descriptors.
117         mem::forget(stdio);
118
119         for callback in self.get_closures().iter_mut() {
120             callback()?;
121         }
122
123         let mut process_handle: zx_handle_t = 0;
124         zx_cvt(fdio_spawn_etc(
125             ZX_HANDLE_INVALID,
126             FDIO_SPAWN_CLONE_JOB
127                 | FDIO_SPAWN_CLONE_LDSVC
128                 | FDIO_SPAWN_CLONE_NAMESPACE
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(),
132             self.get_argv().as_ptr(),
133             envp,
134             actions.len() as size_t,
135             actions.as_ptr(),
136             &mut process_handle,
137             ptr::null_mut(),
138         ))?;
139         // FIXME: See if we want to do something with that err_msg
140
141         Ok(process_handle)
142     }
143 }
144
145 ////////////////////////////////////////////////////////////////////////////////
146 // Processes
147 ////////////////////////////////////////////////////////////////////////////////
148
149 pub struct Process {
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<()> {
159         use crate::sys::process::zircon::*;
160
161         unsafe {
162             zx_cvt(zx_task_kill(self.handle.raw()))?;
163         }
164
165         Ok(())
166     }
167
168     pub fn wait(&mut self) -> io::Result<ExitStatus> {
169         use crate::default::Default;
170         use crate::sys::process::zircon::*;
171
172         let mut proc_info: zx_info_process_t = Default::default();
173         let mut actual: size_t = 0;
174         let mut avail: size_t = 0;
175
176         unsafe {
177             zx_cvt(zx_object_wait_one(
178                 self.handle.raw(),
179                 ZX_TASK_TERMINATED,
180                 ZX_TIME_INFINITE,
181                 ptr::null_mut(),
182             ))?;
183             zx_cvt(zx_object_get_info(
184                 self.handle.raw(),
185                 ZX_INFO_PROCESS,
186                 &mut proc_info as *mut _ as *mut libc::c_void,
187                 mem::size_of::<zx_info_process_t>(),
188                 &mut actual,
189                 &mut avail,
190             ))?;
191         }
192         if actual != 1 {
193             return Err(io::const_io_error!(
194                 io::ErrorKind::InvalidData,
195                 "Failed to get exit status of process",
196             ));
197         }
198         Ok(ExitStatus(proc_info.return_code))
199     }
200
201     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
202         use crate::default::Default;
203         use crate::sys::process::zircon::*;
204
205         let mut proc_info: zx_info_process_t = Default::default();
206         let mut actual: size_t = 0;
207         let mut avail: size_t = 0;
208
209         unsafe {
210             let status =
211                 zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, 0, ptr::null_mut());
212             match status {
213                 0 => {} // Success
214                 x if x == ERR_TIMED_OUT => {
215                     return Ok(None);
216                 }
217                 _ => {
218                     panic!("Failed to wait on process handle: {status}");
219                 }
220             }
221             zx_cvt(zx_object_get_info(
222                 self.handle.raw(),
223                 ZX_INFO_PROCESS,
224                 &mut proc_info as *mut _ as *mut libc::c_void,
225                 mem::size_of::<zx_info_process_t>(),
226                 &mut actual,
227                 &mut avail,
228             ))?;
229         }
230         if actual != 1 {
231             return Err(io::const_io_error!(
232                 io::ErrorKind::InvalidData,
233                 "Failed to get exit status of process",
234             ));
235         }
236         Ok(Some(ExitStatus(proc_info.return_code)))
237     }
238 }
239
240 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
241 pub struct ExitStatus(i64);
242
243 impl ExitStatus {
244     pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
245         match NonZeroI64::try_from(self.0) {
246             /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
247             /* was zero, couldn't convert */ Err(_) => Ok(()),
248         }
249     }
250
251     pub fn code(&self) -> Option<i32> {
252         // FIXME: support extracting return code as an i64
253         self.0.try_into().ok()
254     }
255
256     pub fn signal(&self) -> Option<i32> {
257         None
258     }
259
260     // FIXME: The actually-Unix implementation in process_unix.rs uses WSTOPSIG, WCOREDUMP et al.
261     // I infer from the implementation of `success`, `code` and `signal` above that these are not
262     // available on Fuchsia.
263     //
264     // It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many
265     // other things from std::os::unix) properly.  This veneer is always going to be a bodge.  So
266     // while I don't know if these implementations are actually correct, I think they will do for
267     // now at least.
268     pub fn core_dumped(&self) -> bool {
269         false
270     }
271     pub fn stopped_signal(&self) -> Option<i32> {
272         None
273     }
274     pub fn continued(&self) -> bool {
275         false
276     }
277
278     pub fn into_raw(&self) -> c_int {
279         // We don't know what someone who calls into_raw() will do with this value, but it should
280         // have the conventional Unix representation.  Despite the fact that this is not
281         // standardised in SuS or POSIX, all Unix systems encode the signal and exit status the
282         // same way.  (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
283         // Unix.)
284         //
285         // The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may
286         // do their own shifting and masking, or even pass the status to another computer running a
287         // different Unix variant.
288         //
289         // The other view would be to say that the caller on Fuchsia ought to know that `into_raw`
290         // will give a raw Fuchsia status (whatever that is - I don't know, personally).  That is
291         // not possible here because we must return a c_int because that's what Unix (including
292         // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't
293         // necessarily fit.
294         //
295         // It seems to me that the right answer would be to provide std::os::fuchsia with its
296         // own ExitStatusExt, rather that trying to provide a not very convincing imitation of
297         // Unix.  Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia.  But
298         // fixing this up that is beyond the scope of my efforts now.
299         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.");
300         let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8;
301         wait_status_as_if_unix
302     }
303 }
304
305 /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
306 impl From<c_int> for ExitStatus {
307     fn from(a: c_int) -> ExitStatus {
308         ExitStatus(a as i64)
309     }
310 }
311
312 impl fmt::Display for ExitStatus {
313     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314         write!(f, "exit code: {}", self.0)
315     }
316 }
317
318 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
319 pub struct ExitStatusError(NonZeroI64);
320
321 impl Into<ExitStatus> for ExitStatusError {
322     fn into(self) -> ExitStatus {
323         ExitStatus(self.0.into())
324     }
325 }
326
327 impl ExitStatusError {
328     pub fn code(self) -> Option<NonZeroI32> {
329         // fixme: affected by the same bug as ExitStatus::code()
330         ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
331     }
332 }