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