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