• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
spawn( &mut self, default: Stdio, needs_stdin: bool, ) -> io::Result<(Process, StdioPipes)>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 
output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)>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 
exec(&mut self, default: Stdio) -> io::Error43     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 
do_exec( &mut self, stdio: ChildPipes, maybe_envp: Option<&CStringArray>, ) -> io::Result<zx_handle_t>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 {
id(&self) -> u32154     pub fn id(&self) -> u32 {
155         self.handle.raw() as u32
156     }
157 
kill(&mut self) -> io::Result<()>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 
wait(&mut self) -> io::Result<ExitStatus>168     pub fn wait(&mut self) -> io::Result<ExitStatus> {
169         use crate::sys::process::zircon::*;
170 
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;
174 
175         unsafe {
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             ))?;
190         }
191         if actual != 1 {
192             return Err(io::const_io_error!(
193                 io::ErrorKind::InvalidData,
194                 "Failed to get exit status of process",
195             ));
196         }
197         Ok(ExitStatus(proc_info.return_code))
198     }
199 
try_wait(&mut self) -> io::Result<Option<ExitStatus>>200     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
201         use crate::sys::process::zircon::*;
202 
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;
206 
207         unsafe {
208             let status =
209                 zx_object_wait_one(self.handle.raw(), ZX_TASK_TERMINATED, 0, ptr::null_mut());
210             match status {
211                 0 => {} // Success
212                 x if x == ERR_TIMED_OUT => {
213                     return Ok(None);
214                 }
215                 _ => {
216                     panic!("Failed to wait on process handle: {status}");
217                 }
218             }
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             ))?;
227         }
228         if actual != 1 {
229             return Err(io::const_io_error!(
230                 io::ErrorKind::InvalidData,
231                 "Failed to get exit status of process",
232             ));
233         }
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 {
exit_ok(&self) -> Result<(), ExitStatusError>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         }
247     }
248 
code(&self) -> Option<i32>249     pub fn code(&self) -> Option<i32> {
250         // FIXME: support extracting return code as an i64
251         self.0.try_into().ok()
252     }
253 
signal(&self) -> Option<i32>254     pub fn signal(&self) -> Option<i32> {
255         None
256     }
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
263     // other things from std::os::unix) properly. This veneer is always going to be a bodge. So
264     // while I don't know if these implementations are actually correct, I think they will do for
265     // now at least.
core_dumped(&self) -> bool266     pub fn core_dumped(&self) -> bool {
267         false
268     }
stopped_signal(&self) -> Option<i32>269     pub fn stopped_signal(&self) -> Option<i32> {
270         None
271     }
continued(&self) -> bool272     pub fn continued(&self) -> bool {
273         false
274     }
275 
into_raw(&self) -> c_int276     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
278         // have the conventional Unix representation. Despite the fact that this is not
279         // standardised in SuS or POSIX, all Unix systems encode the signal and exit status the
280         // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
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`
288         // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
289         // not possible here because we must return a c_int because that's what Unix (including
290         // SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't
291         // necessarily fit.
292         //
293         // It seems to me that the right answer would be to provide std::os::fuchsia with its
294         // own ExitStatusExt, rather that trying to provide a not very convincing imitation of
295         // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
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     }
301 }
302 
303 /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
304 impl From<c_int> for ExitStatus {
from(a: c_int) -> ExitStatus305     fn from(a: c_int) -> ExitStatus {
306         ExitStatus(a as i64)
307     }
308 }
309 
310 impl fmt::Display for ExitStatus {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result311     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312         write!(f, "exit code: {}", self.0)
313     }
314 }
315 
316 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
317 pub struct ExitStatusError(NonZeroI64);
318 
319 impl Into<ExitStatus> for ExitStatusError {
into(self) -> ExitStatus320     fn into(self) -> ExitStatus {
321         ExitStatus(self.0.into())
322     }
323 }
324 
325 impl ExitStatusError {
code(self) -> Option<NonZeroI32>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 }
331