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