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