]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | use prelude::v1::*; | |
c34b1796 | 12 | use os::unix::prelude::*; |
85aaf69f SL |
13 | |
14 | use collections::HashMap; | |
15 | use env; | |
9346a6ac | 16 | use ffi::{OsString, OsStr, CString, CStr}; |
85aaf69f | 17 | use fmt; |
85aaf69f SL |
18 | use io::{self, Error, ErrorKind}; |
19 | use libc::{self, pid_t, c_void, c_int, gid_t, uid_t}; | |
62682a34 | 20 | use mem; |
85aaf69f | 21 | use ptr; |
62682a34 SL |
22 | use sys::fd::FileDesc; |
23 | use sys::fs::{File, OpenOptions}; | |
d9579d0f | 24 | use sys::pipe::AnonPipe; |
9346a6ac | 25 | use sys::{self, c, cvt, cvt_r}; |
85aaf69f SL |
26 | |
27 | //////////////////////////////////////////////////////////////////////////////// | |
28 | // Command | |
29 | //////////////////////////////////////////////////////////////////////////////// | |
30 | ||
31 | #[derive(Clone)] | |
32 | pub struct Command { | |
33 | pub program: CString, | |
34 | pub args: Vec<CString>, | |
35 | pub env: Option<HashMap<OsString, OsString>>, | |
36 | pub cwd: Option<CString>, | |
37 | pub uid: Option<uid_t>, | |
38 | pub gid: Option<gid_t>, | |
39 | pub detach: bool, // not currently exposed in std::process | |
40 | } | |
41 | ||
42 | impl Command { | |
43 | pub fn new(program: &OsStr) -> Command { | |
44 | Command { | |
45 | program: program.to_cstring().unwrap(), | |
46 | args: Vec::new(), | |
47 | env: None, | |
48 | cwd: None, | |
49 | uid: None, | |
50 | gid: None, | |
51 | detach: false, | |
52 | } | |
53 | } | |
54 | ||
55 | pub fn arg(&mut self, arg: &OsStr) { | |
56 | self.args.push(arg.to_cstring().unwrap()) | |
57 | } | |
58 | pub fn args<'a, I: Iterator<Item = &'a OsStr>>(&mut self, args: I) { | |
c34b1796 | 59 | self.args.extend(args.map(|s| s.to_cstring().unwrap())) |
85aaf69f SL |
60 | } |
61 | fn init_env_map(&mut self) { | |
62 | if self.env.is_none() { | |
63 | self.env = Some(env::vars_os().collect()); | |
64 | } | |
65 | } | |
66 | pub fn env(&mut self, key: &OsStr, val: &OsStr) { | |
67 | self.init_env_map(); | |
68 | self.env.as_mut().unwrap().insert(key.to_os_string(), val.to_os_string()); | |
69 | } | |
70 | pub fn env_remove(&mut self, key: &OsStr) { | |
71 | self.init_env_map(); | |
72 | self.env.as_mut().unwrap().remove(&key.to_os_string()); | |
73 | } | |
74 | pub fn env_clear(&mut self) { | |
75 | self.env = Some(HashMap::new()) | |
76 | } | |
77 | pub fn cwd(&mut self, dir: &OsStr) { | |
78 | self.cwd = Some(dir.to_cstring().unwrap()) | |
79 | } | |
80 | } | |
81 | ||
82 | //////////////////////////////////////////////////////////////////////////////// | |
83 | // Processes | |
84 | //////////////////////////////////////////////////////////////////////////////// | |
85 | ||
86 | /// Unix exit statuses | |
87 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
88 | pub enum ExitStatus { | |
89 | /// Normal termination with an exit code. | |
90 | Code(i32), | |
91 | ||
92 | /// Termination by signal, with the signal number. | |
93 | /// | |
94 | /// Never generated on Windows. | |
95 | Signal(i32), | |
96 | } | |
97 | ||
98 | impl ExitStatus { | |
99 | pub fn success(&self) -> bool { | |
100 | *self == ExitStatus::Code(0) | |
101 | } | |
102 | pub fn code(&self) -> Option<i32> { | |
103 | match *self { | |
104 | ExitStatus::Code(c) => Some(c), | |
105 | _ => None | |
106 | } | |
107 | } | |
108 | } | |
109 | ||
110 | impl fmt::Display for ExitStatus { | |
111 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
112 | match *self { | |
113 | ExitStatus::Code(code) => write!(f, "exit code: {}", code), | |
114 | ExitStatus::Signal(code) => write!(f, "signal: {}", code), | |
115 | } | |
116 | } | |
117 | } | |
118 | ||
119 | /// The unique id of the process (this should never be negative). | |
120 | pub struct Process { | |
121 | pid: pid_t | |
122 | } | |
123 | ||
9346a6ac AL |
124 | pub enum Stdio { |
125 | Inherit, | |
9346a6ac | 126 | None, |
62682a34 | 127 | Raw(c_int), |
9346a6ac AL |
128 | } |
129 | ||
62682a34 SL |
130 | pub type RawStdio = FileDesc; |
131 | ||
85aaf69f SL |
132 | const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; |
133 | ||
134 | impl Process { | |
85aaf69f SL |
135 | pub unsafe fn kill(&self) -> io::Result<()> { |
136 | try!(cvt(libc::funcs::posix88::signal::kill(self.pid, libc::SIGKILL))); | |
137 | Ok(()) | |
138 | } | |
139 | ||
140 | pub fn spawn(cfg: &Command, | |
9346a6ac AL |
141 | in_fd: Stdio, |
142 | out_fd: Stdio, | |
143 | err_fd: Stdio) -> io::Result<Process> { | |
144 | let dirp = cfg.cwd.as_ref().map(|c| c.as_ptr()).unwrap_or(ptr::null()); | |
145 | ||
146 | let (envp, _a, _b) = make_envp(cfg.env.as_ref()); | |
147 | let (argv, _a) = make_argv(&cfg.program, &cfg.args); | |
d9579d0f | 148 | let (input, output) = try!(sys::pipe::anon_pipe()); |
9346a6ac AL |
149 | |
150 | let pid = unsafe { | |
151 | match libc::fork() { | |
152 | 0 => { | |
153 | drop(input); | |
154 | Process::child_after_fork(cfg, output, argv, envp, dirp, | |
155 | in_fd, out_fd, err_fd) | |
156 | } | |
157 | n if n < 0 => return Err(Error::last_os_error()), | |
158 | n => n, | |
159 | } | |
160 | }; | |
161 | ||
162 | let p = Process{ pid: pid }; | |
163 | drop(output); | |
164 | let mut bytes = [0; 8]; | |
165 | ||
166 | // loop to handle EINTR | |
167 | loop { | |
168 | match input.read(&mut bytes) { | |
169 | Ok(0) => return Ok(p), | |
170 | Ok(8) => { | |
171 | assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), | |
172 | "Validation on the CLOEXEC pipe failed: {:?}", bytes); | |
173 | let errno = combine(&bytes[0.. 4]); | |
174 | assert!(p.wait().is_ok(), | |
175 | "wait() should either return Ok or panic"); | |
176 | return Err(Error::from_raw_os_error(errno)) | |
177 | } | |
178 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} | |
179 | Err(e) => { | |
180 | assert!(p.wait().is_ok(), | |
181 | "wait() should either return Ok or panic"); | |
182 | panic!("the CLOEXEC pipe failed: {:?}", e) | |
183 | }, | |
184 | Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic | |
185 | assert!(p.wait().is_ok(), | |
186 | "wait() should either return Ok or panic"); | |
187 | panic!("short read on the CLOEXEC pipe") | |
188 | } | |
85aaf69f SL |
189 | } |
190 | } | |
191 | ||
9346a6ac AL |
192 | fn combine(arr: &[u8]) -> i32 { |
193 | let a = arr[0] as u32; | |
194 | let b = arr[1] as u32; | |
195 | let c = arr[2] as u32; | |
196 | let d = arr[3] as u32; | |
85aaf69f | 197 | |
9346a6ac | 198 | ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 |
c34b1796 | 199 | } |
9346a6ac | 200 | } |
c34b1796 | 201 | |
9346a6ac AL |
202 | // And at this point we've reached a special time in the life of the |
203 | // child. The child must now be considered hamstrung and unable to | |
204 | // do anything other than syscalls really. Consider the following | |
205 | // scenario: | |
206 | // | |
207 | // 1. Thread A of process 1 grabs the malloc() mutex | |
208 | // 2. Thread B of process 1 forks(), creating thread C | |
209 | // 3. Thread C of process 2 then attempts to malloc() | |
210 | // 4. The memory of process 2 is the same as the memory of | |
211 | // process 1, so the mutex is locked. | |
212 | // | |
213 | // This situation looks a lot like deadlock, right? It turns out | |
214 | // that this is what pthread_atfork() takes care of, which is | |
215 | // presumably implemented across platforms. The first thing that | |
216 | // threads to *before* forking is to do things like grab the malloc | |
217 | // mutex, and then after the fork they unlock it. | |
218 | // | |
219 | // Despite this information, libnative's spawn has been witnessed to | |
220 | // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but | |
221 | // all collected backtraces point at malloc/free traffic in the | |
222 | // child spawned process. | |
223 | // | |
224 | // For this reason, the block of code below should contain 0 | |
225 | // invocations of either malloc of free (or their related friends). | |
226 | // | |
227 | // As an example of not having malloc/free traffic, we don't close | |
228 | // this file descriptor by dropping the FileDesc (which contains an | |
229 | // allocation). Instead we just close it manually. This will never | |
230 | // have the drop glue anyway because this code never returns (the | |
231 | // child will either exec() or invoke libc::exit) | |
232 | unsafe fn child_after_fork(cfg: &Command, | |
233 | mut output: AnonPipe, | |
234 | argv: *const *const libc::c_char, | |
235 | envp: *const libc::c_void, | |
236 | dirp: *const libc::c_char, | |
237 | in_fd: Stdio, | |
238 | out_fd: Stdio, | |
239 | err_fd: Stdio) -> ! { | |
240 | fn fail(output: &mut AnonPipe) -> ! { | |
241 | let errno = sys::os::errno() as u32; | |
242 | let bytes = [ | |
243 | (errno >> 24) as u8, | |
244 | (errno >> 16) as u8, | |
245 | (errno >> 8) as u8, | |
246 | (errno >> 0) as u8, | |
247 | CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], | |
248 | CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] | |
249 | ]; | |
250 | // pipe I/O up to PIPE_BUF bytes should be atomic, and then we want | |
251 | // to be sure we *don't* run at_exit destructors as we're being torn | |
252 | // down regardless | |
253 | assert!(output.write(&bytes).is_ok()); | |
254 | unsafe { libc::_exit(1) } | |
c34b1796 AL |
255 | } |
256 | ||
9346a6ac | 257 | let setup = |src: Stdio, dst: c_int| { |
62682a34 SL |
258 | match src { |
259 | Stdio::Inherit => true, | |
260 | Stdio::Raw(fd) => cvt_r(|| libc::dup2(fd, dst)).is_ok(), | |
9346a6ac AL |
261 | |
262 | // If a stdio file descriptor is set to be ignored, we open up | |
263 | // /dev/null into that file descriptor. Otherwise, the first | |
264 | // file descriptor opened up in the child would be numbered as | |
265 | // one of the stdio file descriptors, which is likely to wreak | |
266 | // havoc. | |
267 | Stdio::None => { | |
268 | let mut opts = OpenOptions::new(); | |
269 | opts.read(dst == libc::STDIN_FILENO); | |
270 | opts.write(dst != libc::STDIN_FILENO); | |
271 | let devnull = CStr::from_ptr(b"/dev/null\0".as_ptr() | |
272 | as *const _); | |
273 | if let Ok(f) = File::open_c(devnull, &opts) { | |
62682a34 | 274 | cvt_r(|| libc::dup2(f.fd().raw(), dst)).is_ok() |
9346a6ac | 275 | } else { |
62682a34 | 276 | false |
85aaf69f SL |
277 | } |
278 | } | |
62682a34 | 279 | } |
9346a6ac | 280 | }; |
85aaf69f | 281 | |
9346a6ac AL |
282 | if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) } |
283 | if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) } | |
284 | if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) } | |
85aaf69f | 285 | |
9346a6ac AL |
286 | if let Some(u) = cfg.gid { |
287 | if libc::setgid(u as libc::gid_t) != 0 { | |
85aaf69f | 288 | fail(&mut output); |
9346a6ac AL |
289 | } |
290 | } | |
291 | if let Some(u) = cfg.uid { | |
292 | // When dropping privileges from root, the `setgroups` call | |
293 | // will remove any extraneous groups. If we don't call this, | |
294 | // then even though our uid has dropped, we may still have | |
295 | // groups that enable us to do super-user things. This will | |
296 | // fail if we aren't root, so don't bother checking the | |
297 | // return value, this is just done as an optimistic | |
298 | // privilege dropping function. | |
299 | let _ = c::setgroups(0, ptr::null()); | |
300 | ||
301 | if libc::setuid(u as libc::uid_t) != 0 { | |
302 | fail(&mut output); | |
303 | } | |
304 | } | |
305 | if cfg.detach { | |
306 | // Don't check the error of setsid because it fails if we're the | |
307 | // process leader already. We just forked so it shouldn't return | |
308 | // error, but ignore it anyway. | |
309 | let _ = libc::setsid(); | |
310 | } | |
311 | if !dirp.is_null() && libc::chdir(dirp) == -1 { | |
312 | fail(&mut output); | |
313 | } | |
314 | if !envp.is_null() { | |
315 | *sys::os::environ() = envp as *const _; | |
316 | } | |
62682a34 SL |
317 | |
318 | // Reset signal handling so the child process starts in a | |
319 | // standardized state. libstd ignores SIGPIPE, and signal-handling | |
320 | // libraries often set a mask. Child processes inherit ignored | |
321 | // signals and the signal mask from their parent, but most | |
322 | // UNIX programs do not reset these things on their own, so we | |
323 | // need to clean things up now to avoid confusing the program | |
324 | // we're about to run. | |
325 | let mut set: c::sigset_t = mem::uninitialized(); | |
326 | if c::sigemptyset(&mut set) != 0 || | |
327 | c::pthread_sigmask(c::SIG_SETMASK, &set, ptr::null_mut()) != 0 || | |
328 | libc::funcs::posix01::signal::signal( | |
329 | libc::SIGPIPE, mem::transmute(c::SIG_DFL) | |
330 | ) == mem::transmute(c::SIG_ERR) { | |
331 | fail(&mut output); | |
332 | } | |
333 | ||
334 | let _ = libc::execvp(*argv, argv); | |
9346a6ac | 335 | fail(&mut output) |
85aaf69f SL |
336 | } |
337 | ||
62682a34 SL |
338 | pub fn id(&self) -> u32 { |
339 | self.pid as u32 | |
340 | } | |
341 | ||
85aaf69f SL |
342 | pub fn wait(&self) -> io::Result<ExitStatus> { |
343 | let mut status = 0 as c_int; | |
9346a6ac | 344 | try!(cvt_r(|| unsafe { c::waitpid(self.pid, &mut status, 0) })); |
85aaf69f SL |
345 | Ok(translate_status(status)) |
346 | } | |
347 | ||
348 | pub fn try_wait(&self) -> Option<ExitStatus> { | |
349 | let mut status = 0 as c_int; | |
9346a6ac | 350 | match cvt_r(|| unsafe { |
85aaf69f SL |
351 | c::waitpid(self.pid, &mut status, c::WNOHANG) |
352 | }) { | |
9346a6ac AL |
353 | Ok(0) => None, |
354 | Ok(n) if n == self.pid => Some(translate_status(status)), | |
d9579d0f | 355 | Ok(n) => panic!("unknown pid: {}", n), |
9346a6ac | 356 | Err(e) => panic!("unknown waitpid error: {}", e), |
85aaf69f SL |
357 | } |
358 | } | |
359 | } | |
360 | ||
9346a6ac AL |
361 | fn make_argv(prog: &CString, args: &[CString]) |
362 | -> (*const *const libc::c_char, Vec<*const libc::c_char>) | |
85aaf69f SL |
363 | { |
364 | let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1); | |
365 | ||
366 | // Convert the CStrings into an array of pointers. Note: the | |
367 | // lifetime of the various CStrings involved is guaranteed to be | |
368 | // larger than the lifetime of our invocation of cb, but this is | |
369 | // technically unsafe as the callback could leak these pointers | |
370 | // out of our scope. | |
371 | ptrs.push(prog.as_ptr()); | |
372 | ptrs.extend(args.iter().map(|tmp| tmp.as_ptr())); | |
373 | ||
374 | // Add a terminating null pointer (required by libc). | |
375 | ptrs.push(ptr::null()); | |
376 | ||
9346a6ac | 377 | (ptrs.as_ptr(), ptrs) |
85aaf69f SL |
378 | } |
379 | ||
9346a6ac AL |
380 | fn make_envp(env: Option<&HashMap<OsString, OsString>>) |
381 | -> (*const c_void, Vec<Vec<u8>>, Vec<*const libc::c_char>) | |
85aaf69f SL |
382 | { |
383 | // On posixy systems we can pass a char** for envp, which is a | |
384 | // null-terminated array of "k=v\0" strings. Since we must create | |
385 | // these strings locally, yet expose a raw pointer to them, we | |
386 | // create a temporary vector to own the CStrings that outlives the | |
387 | // call to cb. | |
9346a6ac AL |
388 | if let Some(env) = env { |
389 | let mut tmps = Vec::with_capacity(env.len()); | |
390 | ||
391 | for pair in env { | |
392 | let mut kv = Vec::new(); | |
393 | kv.push_all(pair.0.as_bytes()); | |
394 | kv.push('=' as u8); | |
395 | kv.push_all(pair.1.as_bytes()); | |
396 | kv.push(0); // terminating null | |
397 | tmps.push(kv); | |
398 | } | |
85aaf69f | 399 | |
9346a6ac AL |
400 | let mut ptrs: Vec<*const libc::c_char> = |
401 | tmps.iter() | |
402 | .map(|tmp| tmp.as_ptr() as *const libc::c_char) | |
403 | .collect(); | |
404 | ptrs.push(ptr::null()); | |
85aaf69f | 405 | |
9346a6ac AL |
406 | (ptrs.as_ptr() as *const _, tmps, ptrs) |
407 | } else { | |
408 | (0 as *const _, Vec::new(), Vec::new()) | |
85aaf69f SL |
409 | } |
410 | } | |
411 | ||
412 | fn translate_status(status: c_int) -> ExitStatus { | |
413 | #![allow(non_snake_case)] | |
414 | #[cfg(any(target_os = "linux", target_os = "android"))] | |
415 | mod imp { | |
416 | pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 } | |
417 | pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff } | |
418 | pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f } | |
419 | } | |
420 | ||
421 | #[cfg(any(target_os = "macos", | |
422 | target_os = "ios", | |
423 | target_os = "freebsd", | |
424 | target_os = "dragonfly", | |
c34b1796 | 425 | target_os = "bitrig", |
c1a9b12d | 426 | target_os = "netbsd", |
85aaf69f SL |
427 | target_os = "openbsd"))] |
428 | mod imp { | |
429 | pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 } | |
430 | pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 } | |
431 | pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 } | |
432 | } | |
433 | ||
434 | if imp::WIFEXITED(status) { | |
435 | ExitStatus::Code(imp::WEXITSTATUS(status)) | |
436 | } else { | |
437 | ExitStatus::Signal(imp::WTERMSIG(status)) | |
438 | } | |
439 | } | |
62682a34 SL |
440 | |
441 | #[cfg(test)] | |
442 | mod tests { | |
443 | use super::*; | |
444 | use prelude::v1::*; | |
445 | ||
446 | use ffi::OsStr; | |
447 | use mem; | |
448 | use ptr; | |
449 | use libc; | |
450 | use slice; | |
451 | use sys::{self, c, cvt, pipe}; | |
452 | ||
453 | #[cfg(not(target_os = "android"))] | |
454 | extern { | |
455 | fn sigaddset(set: *mut c::sigset_t, signum: libc::c_int) -> libc::c_int; | |
456 | } | |
457 | ||
458 | #[cfg(target_os = "android")] | |
459 | unsafe fn sigaddset(set: *mut c::sigset_t, signum: libc::c_int) -> libc::c_int { | |
460 | let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::<c::sigset_t>()); | |
461 | let bit = (signum - 1) as usize; | |
462 | raw[bit / 8] |= 1 << (bit % 8); | |
463 | return 0; | |
464 | } | |
465 | ||
466 | #[test] | |
467 | fn test_process_mask() { | |
468 | unsafe { | |
469 | // Test to make sure that a signal mask does not get inherited. | |
470 | let cmd = Command::new(OsStr::new("cat")); | |
471 | let (stdin_read, stdin_write) = sys::pipe::anon_pipe().unwrap(); | |
472 | let (stdout_read, stdout_write) = sys::pipe::anon_pipe().unwrap(); | |
473 | ||
474 | let mut set: c::sigset_t = mem::uninitialized(); | |
475 | let mut old_set: c::sigset_t = mem::uninitialized(); | |
476 | cvt(c::sigemptyset(&mut set)).unwrap(); | |
477 | cvt(sigaddset(&mut set, libc::SIGINT)).unwrap(); | |
478 | cvt(c::pthread_sigmask(c::SIG_SETMASK, &set, &mut old_set)).unwrap(); | |
479 | ||
480 | let cat = Process::spawn(&cmd, Stdio::Raw(stdin_read.raw()), | |
481 | Stdio::Raw(stdout_write.raw()), | |
482 | Stdio::None).unwrap(); | |
483 | drop(stdin_read); | |
484 | drop(stdout_write); | |
485 | ||
486 | cvt(c::pthread_sigmask(c::SIG_SETMASK, &old_set, ptr::null_mut())).unwrap(); | |
487 | ||
488 | cvt(libc::funcs::posix88::signal::kill(cat.id() as libc::pid_t, libc::SIGINT)).unwrap(); | |
489 | // We need to wait until SIGINT is definitely delivered. The | |
490 | // easiest way is to write something to cat, and try to read it | |
491 | // back: if SIGINT is unmasked, it'll get delivered when cat is | |
492 | // next scheduled. | |
493 | let _ = stdin_write.write(b"Hello"); | |
494 | drop(stdin_write); | |
495 | ||
496 | // Either EOF or failure (EPIPE) is okay. | |
497 | let mut buf = [0; 5]; | |
498 | if let Ok(ret) = stdout_read.read(&mut buf) { | |
499 | assert!(ret == 0); | |
500 | } | |
501 | ||
502 | cat.wait().unwrap(); | |
503 | } | |
504 | } | |
505 | } |