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