1 use crate::std::fmt; 2 use crate::std::io::{self, Error, ErrorKind}; 3 use crate::std::num::NonZeroI32; 4 use crate::std::sys; 5 use crate::std::sys::cvt; 6 use crate::std::sys::process::process_common::*; 7 use crate::std::sys_common::thread; 8 use core::ffi::NonZero_c_int; 9 use dlibc::RTP_ID; 10 use dlibc::{self, c_char, c_int}; 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 use crate::std::sys::cvt_r; 23 let envp = self.capture_env(); 24 25 if self.saw_nul() { 26 return Err(io::const_io_error!( 27 ErrorKind::InvalidInput, 28 "nul byte found in provided data", 29 )); 30 } 31 let (ours, theirs) = self.setup_io(default, needs_stdin)?; 32 let mut p = Process { 33 pid: 0, 34 status: None, 35 }; 36 37 unsafe { 38 macro_rules! t { 39 ($e:expr) => { 40 match $e { 41 Ok(e) => e, 42 Err(e) => return Err(e.into()), 43 } 44 }; 45 } 46 47 let mut orig_stdin = dlibc::STDIN_FILENO; 48 let mut orig_stdout = dlibc::STDOUT_FILENO; 49 let mut orig_stderr = dlibc::STDERR_FILENO; 50 51 if let Some(fd) = theirs.stdin.fd() { 52 orig_stdin = t!(cvt_r(|| dlibc::dup(dlibc::STDIN_FILENO))); 53 t!(cvt_r(|| dlibc::dup2(fd, dlibc::STDIN_FILENO))); 54 } 55 if let Some(fd) = theirs.stdout.fd() { 56 orig_stdout = t!(cvt_r(|| dlibc::dup(dlibc::STDOUT_FILENO))); 57 t!(cvt_r(|| dlibc::dup2(fd, dlibc::STDOUT_FILENO))); 58 } 59 if let Some(fd) = theirs.stderr.fd() { 60 orig_stderr = t!(cvt_r(|| dlibc::dup(dlibc::STDERR_FILENO))); 61 t!(cvt_r(|| dlibc::dup2(fd, dlibc::STDERR_FILENO))); 62 } 63 64 if let Some(ref cwd) = *self.get_cwd() { 65 t!(cvt(dlibc::chdir(cwd.as_ptr()))); 66 } 67 68 // pre_exec closures are ignored on VxWorks 69 let _ = self.get_closures(); 70 71 let c_envp = envp 72 .as_ref() 73 .map(|c| c.as_ptr()) 74 .unwrap_or_else(|| *sys::os::environ() as *const _); 75 let stack_size = thread::min_stack(); 76 77 // ensure that access to the environment is synchronized 78 let _lock = sys::os::env_read_lock(); 79 80 let ret = dlibc::rtpSpawn( 81 self.get_program_cstr().as_ptr(), 82 self.get_argv().as_ptr() as *mut *const c_char, // argv 83 c_envp as *mut *const c_char, 84 100 as c_int, // initial priority 85 stack_size, // initial stack size. 86 0, // options 87 0, // task options 88 ); 89 90 // Because FileDesc was not used, each duplicated file descriptor 91 // needs to be closed manually 92 if orig_stdin != dlibc::STDIN_FILENO { 93 t!(cvt_r(|| dlibc::dup2(orig_stdin, dlibc::STDIN_FILENO))); 94 dlibc::close(orig_stdin); 95 } 96 if orig_stdout != dlibc::STDOUT_FILENO { 97 t!(cvt_r(|| dlibc::dup2(orig_stdout, dlibc::STDOUT_FILENO))); 98 dlibc::close(orig_stdout); 99 } 100 if orig_stderr != dlibc::STDERR_FILENO { 101 t!(cvt_r(|| dlibc::dup2(orig_stderr, dlibc::STDERR_FILENO))); 102 dlibc::close(orig_stderr); 103 } 104 105 if ret != dlibc::RTP_ID_ERROR { 106 p.pid = ret; 107 Ok((p, ours)) 108 } else { 109 Err(io::Error::last_os_error()) 110 } 111 } 112 } 113 114 pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> { 115 let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?; 116 crate::std::sys_common::process::wait_with_output(proc, pipes) 117 } 118 119 pub fn exec(&mut self, default: Stdio) -> io::Error { 120 let ret = Command::spawn(self, default, false); 121 match ret { 122 Ok(t) => unsafe { 123 let mut status = 0 as c_int; 124 dlibc::waitpid(t.0.pid, &mut status, 0); 125 dlibc::exit(0); 126 }, 127 Err(e) => e, 128 } 129 } 130 } 131 132 //////////////////////////////////////////////////////////////////////////////// 133 // Processes 134 //////////////////////////////////////////////////////////////////////////////// 135 136 /// The unique id of the process (this should never be negative). 137 pub struct Process { 138 pid: RTP_ID, 139 status: Option<ExitStatus>, 140 } 141 142 impl Process { 143 pub fn id(&self) -> u32 { 144 self.pid as u32 145 } 146 147 pub fn kill(&mut self) -> io::Result<()> { 148 // If we've already waited on this process then the pid can be recycled 149 // and used for another process, and we probably shouldn't be killing 150 // random processes, so return Ok because the process has exited already. 151 if self.status.is_some() { 152 Ok(()) 153 } else { 154 cvt(unsafe { dlibc::kill(self.pid, dlibc::SIGKILL) }).map(drop) 155 } 156 } 157 158 pub fn wait(&mut self) -> io::Result<ExitStatus> { 159 use crate::std::sys::cvt_r; 160 if let Some(status) = self.status { 161 return Ok(status); 162 } 163 let mut status = 0 as c_int; 164 cvt_r(|| unsafe { dlibc::waitpid(self.pid, &mut status, 0) })?; 165 self.status = Some(ExitStatus::new(status)); 166 Ok(ExitStatus::new(status)) 167 } 168 169 pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { 170 if let Some(status) = self.status { 171 return Ok(Some(status)); 172 } 173 let mut status = 0 as c_int; 174 let pid = cvt(unsafe { dlibc::waitpid(self.pid, &mut status, dlibc::WNOHANG) })?; 175 if pid == 0 { 176 Ok(None) 177 } else { 178 self.status = Some(ExitStatus::new(status)); 179 Ok(Some(ExitStatus::new(status))) 180 } 181 } 182 } 183 184 /// Unix exit statuses 185 #[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] 186 pub struct ExitStatus(c_int); 187 188 impl ExitStatus { 189 pub fn new(status: c_int) -> ExitStatus { 190 ExitStatus(status) 191 } 192 193 fn exited(&self) -> bool { 194 dlibc::WIFEXITED(self.0) 195 } 196 197 pub fn exit_ok(&self) -> Result<(), ExitStatusError> { 198 // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is 199 // true on all actual versions of Unix, is widely assumed, and is specified in SuS 200 // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not 201 // true for a platform pretending to be Unix, the tests (our doctests, and also 202 // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too. 203 match NonZero_c_int::try_from(self.0) { 204 Ok(failure) => Err(ExitStatusError(failure)), 205 Err(_) => Ok(()), 206 } 207 } 208 209 pub fn code(&self) -> Option<i32> { 210 if self.exited() { 211 Some(dlibc::WEXITSTATUS(self.0)) 212 } else { 213 None 214 } 215 } 216 217 pub fn signal(&self) -> Option<i32> { 218 if !self.exited() { 219 Some(dlibc::WTERMSIG(self.0)) 220 } else { 221 None 222 } 223 } 224 225 pub fn core_dumped(&self) -> bool { 226 // This method is not yet properly implemented on VxWorks 227 false 228 } 229 230 pub fn stopped_signal(&self) -> Option<i32> { 231 if dlibc::WIFSTOPPED(self.0) { 232 Some(dlibc::WSTOPSIG(self.0)) 233 } else { 234 None 235 } 236 } 237 238 pub fn continued(&self) -> bool { 239 // This method is not yet properly implemented on VxWorks 240 false 241 } 242 243 pub fn into_raw(&self) -> c_int { 244 self.0 245 } 246 } 247 248 /// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying. 249 impl From<c_int> for ExitStatus { 250 fn from(a: c_int) -> ExitStatus { 251 ExitStatus(a) 252 } 253 } 254 255 impl fmt::Display for ExitStatus { 256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 257 if let Some(code) = self.code() { 258 write!(f, "exit code: {code}") 259 } else { 260 let signal = self.signal().unwrap(); 261 write!(f, "signal: {signal}") 262 } 263 } 264 } 265 266 #[derive(PartialEq, Eq, Clone, Copy, Debug)] 267 pub struct ExitStatusError(NonZero_c_int); 268 269 impl Into<ExitStatus> for ExitStatusError { 270 fn into(self) -> ExitStatus { 271 ExitStatus(self.0.into()) 272 } 273 } 274 275 impl ExitStatusError { 276 pub fn code(self) -> Option<NonZeroI32> { 277 ExitStatus(self.0.into()) 278 .code() 279 .map(|st| st.try_into().unwrap()) 280 } 281 } 282