2015-02-06 09:42:57 -08:00
|
|
|
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
|
|
use prelude::v1::*;
|
std: Stabilize portions of `std::os::$platform`
This commit starts to organize the `std::os::$platform` modules and in the
process stabilizes some of the functionality contained within. The organization
of these modules will reflect the organization of the standard library itself
with extension traits for primitives in the same corresponding module.
The OS-specific modules will grow more functionality over time including
concrete types that are not extending functionality of other structures, and
these will either go into the closest module in `std::os::$platform` or they
will grow a new module in the hierarchy.
The following items are now stable:
* `os::{unix, windows}`
* `unix::ffi`
* `unix::ffi::OsStrExt`
* `unix::ffi::OsStrExt::{from_bytes, as_bytes, to_cstring}`
* `unix::ffi::OsString`
* `unix::ffi::OsStringExt::{from_vec, into_vec}`
* `unix::process`
* `unix::process::CommandExt`
* `unix::process::CommandExt::{uid, gid}`
* `unix::process::ExitStatusExt`
* `unix::process::ExitStatusExt::signal`
* `unix::prelude`
* `windows::ffi`
* `windows::ffi::OsStringExt`
* `windows::ffi::OsStringExt::from_wide`
* `windows::ffi::OsStrExt`
* `windows::ffi::OsStrExt::encode_wide`
* `windows::prelude`
The following items remain unstable:
* `unix::io`
* `unix::io::{Fd, AsRawFd}`
* `unix::fs::{PermissionsExt, OpenOptionsExt}`
* `windows::io`
* `windows::io::{Handle, AsRawHandle}`
* `windows::io::{Socket, AsRawSocket}`
* `windows::fs`
* `windows::fs::OpenOptionsExt`
Due to the reorgnization of the platform extension modules, this commit is a
breaking change. Most imports can be fixed by adding the relevant libstd module
in the `use` path (such as `ffi` or `fs`).
[breaking-change]
2015-03-13 17:12:38 -07:00
|
|
|
use os::unix::prelude::*;
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-10-31 11:09:43 -07:00
|
|
|
use collections::hash_map::{HashMap, Entry};
|
2015-02-06 09:42:57 -08:00
|
|
|
use env;
|
2015-04-03 15:34:15 -07:00
|
|
|
use ffi::{OsString, OsStr, CString, CStr};
|
2015-02-06 09:42:57 -08:00
|
|
|
use fmt;
|
|
|
|
|
use io::{self, Error, ErrorKind};
|
2015-10-31 11:09:43 -07:00
|
|
|
use libc::{self, pid_t, c_int, gid_t, uid_t, c_char};
|
|
|
|
|
use mem;
|
2015-02-06 09:42:57 -08:00
|
|
|
use ptr;
|
2015-06-09 16:41:14 -07:00
|
|
|
use sys::fd::FileDesc;
|
|
|
|
|
use sys::fs::{File, OpenOptions};
|
2016-02-04 11:10:37 -08:00
|
|
|
use sys::pipe::{self, AnonPipe};
|
2015-11-02 16:23:22 -08:00
|
|
|
use sys::{self, cvt, cvt_r};
|
2015-02-06 09:42:57 -08:00
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Command
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
pub struct Command {
|
2015-10-31 11:09:43 -07:00
|
|
|
// Currently we try hard to ensure that the call to `.exec()` doesn't
|
|
|
|
|
// actually allocate any memory. While many platforms try to ensure that
|
|
|
|
|
// memory allocation works after a fork in a multithreaded process, it's
|
|
|
|
|
// been observed to be buggy and somewhat unreliable, so we do our best to
|
|
|
|
|
// just not do it at all!
|
|
|
|
|
//
|
|
|
|
|
// Along those lines, the `argv` and `envp` raw pointers here are exactly
|
|
|
|
|
// what's gonna get passed to `execvp`. The `argv` array starts with the
|
|
|
|
|
// `program` and ends with a NULL, and the `envp` pointer, if present, is
|
|
|
|
|
// also null-terminated.
|
|
|
|
|
//
|
|
|
|
|
// Right now we don't support removing arguments, so there's no much fancy
|
|
|
|
|
// support there, but we support adding and removing environment variables,
|
|
|
|
|
// so a side table is used to track where in the `envp` array each key is
|
|
|
|
|
// located. Whenever we add a key we update it in place if it's already
|
|
|
|
|
// present, and whenever we remove a key we update the locations of all
|
|
|
|
|
// other keys.
|
2016-01-15 15:29:45 -05:00
|
|
|
program: CString,
|
|
|
|
|
args: Vec<CString>,
|
2015-10-31 11:09:43 -07:00
|
|
|
env: Option<HashMap<OsString, (usize, CString)>>,
|
|
|
|
|
argv: Vec<*const c_char>,
|
|
|
|
|
envp: Option<Vec<*const c_char>>,
|
|
|
|
|
|
2016-01-15 15:29:45 -05:00
|
|
|
cwd: Option<CString>,
|
|
|
|
|
uid: Option<uid_t>,
|
|
|
|
|
gid: Option<gid_t>,
|
|
|
|
|
session_leader: bool,
|
|
|
|
|
saw_nul: bool,
|
2016-02-03 16:55:59 -08:00
|
|
|
closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>,
|
2016-02-04 11:10:37 -08:00
|
|
|
stdin: Option<Stdio>,
|
|
|
|
|
stdout: Option<Stdio>,
|
|
|
|
|
stderr: Option<Stdio>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// passed back to std::process with the pipes connected to the child, if any
|
|
|
|
|
// were requested
|
|
|
|
|
pub struct StdioPipes {
|
|
|
|
|
pub stdin: Option<AnonPipe>,
|
|
|
|
|
pub stdout: Option<AnonPipe>,
|
|
|
|
|
pub stderr: Option<AnonPipe>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// passed to do_exec() with configuration of what the child stdio should look
|
|
|
|
|
// like
|
|
|
|
|
struct ChildPipes {
|
|
|
|
|
stdin: ChildStdio,
|
|
|
|
|
stdout: ChildStdio,
|
|
|
|
|
stderr: ChildStdio,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum ChildStdio {
|
|
|
|
|
Inherit,
|
|
|
|
|
Explicit(c_int),
|
|
|
|
|
Owned(FileDesc),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub enum Stdio {
|
|
|
|
|
Inherit,
|
|
|
|
|
Null,
|
|
|
|
|
MakePipe,
|
|
|
|
|
Fd(FileDesc),
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Command {
|
|
|
|
|
pub fn new(program: &OsStr) -> Command {
|
2016-01-15 15:29:45 -05:00
|
|
|
let mut saw_nul = false;
|
2015-10-31 11:09:43 -07:00
|
|
|
let program = os2c(program, &mut saw_nul);
|
2015-02-06 09:42:57 -08:00
|
|
|
Command {
|
2015-10-31 11:09:43 -07:00
|
|
|
argv: vec![program.as_ptr(), 0 as *const _],
|
|
|
|
|
program: program,
|
2015-02-06 09:42:57 -08:00
|
|
|
args: Vec::new(),
|
|
|
|
|
env: None,
|
2015-10-31 11:09:43 -07:00
|
|
|
envp: None,
|
2015-02-06 09:42:57 -08:00
|
|
|
cwd: None,
|
|
|
|
|
uid: None,
|
|
|
|
|
gid: None,
|
2015-06-21 16:43:19 +02:00
|
|
|
session_leader: false,
|
2016-01-15 15:29:45 -05:00
|
|
|
saw_nul: saw_nul,
|
2016-02-03 16:55:59 -08:00
|
|
|
closures: Vec::new(),
|
2016-02-04 11:10:37 -08:00
|
|
|
stdin: None,
|
|
|
|
|
stdout: None,
|
|
|
|
|
stderr: None,
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn arg(&mut self, arg: &OsStr) {
|
2015-10-31 11:09:43 -07:00
|
|
|
// Overwrite the trailing NULL pointer in `argv` and then add a new null
|
|
|
|
|
// pointer.
|
|
|
|
|
let arg = os2c(arg, &mut self.saw_nul);
|
|
|
|
|
self.argv[self.args.len() + 1] = arg.as_ptr();
|
|
|
|
|
self.argv.push(0 as *const _);
|
|
|
|
|
|
|
|
|
|
// Also make sure we keep track of the owned value to schedule a
|
|
|
|
|
// destructor for this memory.
|
|
|
|
|
self.args.push(arg);
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-10-31 11:09:43 -07:00
|
|
|
|
|
|
|
|
fn init_env_map(&mut self) -> (&mut HashMap<OsString, (usize, CString)>,
|
|
|
|
|
&mut Vec<*const c_char>) {
|
2015-02-06 09:42:57 -08:00
|
|
|
if self.env.is_none() {
|
2015-10-31 11:09:43 -07:00
|
|
|
let mut map = HashMap::new();
|
|
|
|
|
let mut envp = Vec::new();
|
|
|
|
|
for (k, v) in env::vars_os() {
|
|
|
|
|
let s = pair_to_key(&k, &v, &mut self.saw_nul);
|
|
|
|
|
envp.push(s.as_ptr());
|
|
|
|
|
map.insert(k, (envp.len() - 1, s));
|
|
|
|
|
}
|
|
|
|
|
envp.push(0 as *const _);
|
|
|
|
|
self.env = Some(map);
|
|
|
|
|
self.envp = Some(envp);
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-10-31 11:09:43 -07:00
|
|
|
(self.env.as_mut().unwrap(), self.envp.as_mut().unwrap())
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2016-01-15 15:29:45 -05:00
|
|
|
|
2015-10-31 11:09:43 -07:00
|
|
|
pub fn env(&mut self, key: &OsStr, val: &OsStr) {
|
|
|
|
|
let new_key = pair_to_key(key, val, &mut self.saw_nul);
|
|
|
|
|
let (map, envp) = self.init_env_map();
|
|
|
|
|
|
|
|
|
|
// If `key` is already present then we we just update `envp` in place
|
|
|
|
|
// (and store the owned value), but if it's not there we override the
|
|
|
|
|
// trailing NULL pointer, add a new NULL pointer, and store where we
|
|
|
|
|
// were located.
|
|
|
|
|
match map.entry(key.to_owned()) {
|
|
|
|
|
Entry::Occupied(mut e) => {
|
|
|
|
|
let (i, ref mut s) = *e.get_mut();
|
|
|
|
|
envp[i] = new_key.as_ptr();
|
|
|
|
|
*s = new_key;
|
|
|
|
|
}
|
|
|
|
|
Entry::Vacant(e) => {
|
|
|
|
|
let len = envp.len();
|
|
|
|
|
envp[len - 1] = new_key.as_ptr();
|
|
|
|
|
envp.push(0 as *const _);
|
|
|
|
|
e.insert((len - 1, new_key));
|
|
|
|
|
}
|
2016-01-15 15:29:45 -05:00
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-10-31 11:09:43 -07:00
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
pub fn env_remove(&mut self, key: &OsStr) {
|
2015-10-31 11:09:43 -07:00
|
|
|
let (map, envp) = self.init_env_map();
|
|
|
|
|
|
|
|
|
|
// If we actually ended up removing a key, then we need to update the
|
|
|
|
|
// position of all keys that come after us in `envp` because they're all
|
|
|
|
|
// one element sooner now.
|
|
|
|
|
if let Some((i, _)) = map.remove(key) {
|
|
|
|
|
envp.remove(i);
|
|
|
|
|
|
|
|
|
|
for (_, &mut (ref mut j, _)) in map.iter_mut() {
|
|
|
|
|
if *j >= i {
|
|
|
|
|
*j -= 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-10-31 11:09:43 -07:00
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
pub fn env_clear(&mut self) {
|
2015-10-31 11:09:43 -07:00
|
|
|
self.env = Some(HashMap::new());
|
|
|
|
|
self.envp = Some(vec![0 as *const _]);
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2015-10-31 11:09:43 -07:00
|
|
|
|
2015-02-06 09:42:57 -08:00
|
|
|
pub fn cwd(&mut self, dir: &OsStr) {
|
2016-01-15 15:29:45 -05:00
|
|
|
self.cwd = Some(os2c(dir, &mut self.saw_nul));
|
|
|
|
|
}
|
|
|
|
|
pub fn uid(&mut self, id: uid_t) {
|
|
|
|
|
self.uid = Some(id);
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2016-01-15 15:29:45 -05:00
|
|
|
pub fn gid(&mut self, id: gid_t) {
|
|
|
|
|
self.gid = Some(id);
|
|
|
|
|
}
|
|
|
|
|
pub fn session_leader(&mut self, session_leader: bool) {
|
|
|
|
|
self.session_leader = session_leader;
|
|
|
|
|
}
|
2016-02-03 16:55:59 -08:00
|
|
|
|
|
|
|
|
pub fn before_exec(&mut self,
|
|
|
|
|
f: Box<FnMut() -> io::Result<()> + Send + Sync>) {
|
|
|
|
|
self.closures.push(f);
|
|
|
|
|
}
|
2015-10-29 13:45:56 -07:00
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
pub fn stdin(&mut self, stdin: Stdio) {
|
|
|
|
|
self.stdin = Some(stdin);
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
pub fn stdout(&mut self, stdout: Stdio) {
|
|
|
|
|
self.stdout = Some(stdout);
|
2015-10-29 13:45:56 -07:00
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
pub fn stderr(&mut self, stderr: Stdio) {
|
|
|
|
|
self.stderr = Some(stderr);
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
pub fn spawn(&mut self, default: Stdio)
|
|
|
|
|
-> io::Result<(Process, StdioPipes)> {
|
|
|
|
|
if self.saw_nul {
|
|
|
|
|
return Err(io::Error::new(ErrorKind::InvalidInput,
|
|
|
|
|
"nul byte found in provided data"));
|
2016-01-15 15:29:45 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
let (ours, theirs) = try!(self.setup_io(default));
|
2015-05-05 16:35:15 -07:00
|
|
|
let (input, output) = try!(sys::pipe::anon_pipe());
|
2015-04-03 15:34:15 -07:00
|
|
|
|
|
|
|
|
let pid = unsafe {
|
2015-10-31 11:09:43 -07:00
|
|
|
match try!(cvt(libc::fork())) {
|
2015-04-03 15:34:15 -07:00
|
|
|
0 => {
|
|
|
|
|
drop(input);
|
2016-02-04 11:10:37 -08:00
|
|
|
let err = self.exec(theirs);
|
2015-10-31 11:09:43 -07:00
|
|
|
let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
|
|
|
|
|
let bytes = [
|
|
|
|
|
(errno >> 24) as u8,
|
|
|
|
|
(errno >> 16) as u8,
|
|
|
|
|
(errno >> 8) as u8,
|
|
|
|
|
(errno >> 0) as u8,
|
|
|
|
|
CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
|
|
|
|
|
CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
|
|
|
|
|
];
|
|
|
|
|
// pipe I/O up to PIPE_BUF bytes should be atomic, and then
|
|
|
|
|
// we want to be sure we *don't* run at_exit destructors as
|
|
|
|
|
// we're being torn down regardless
|
|
|
|
|
assert!(output.write(&bytes).is_ok());
|
|
|
|
|
libc::_exit(1)
|
2015-04-03 15:34:15 -07:00
|
|
|
}
|
|
|
|
|
n => n,
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2016-02-03 18:09:35 -08:00
|
|
|
let mut p = Process { pid: pid, status: None };
|
2015-04-03 15:34:15 -07:00
|
|
|
drop(output);
|
|
|
|
|
let mut bytes = [0; 8];
|
|
|
|
|
|
|
|
|
|
// loop to handle EINTR
|
|
|
|
|
loop {
|
|
|
|
|
match input.read(&mut bytes) {
|
2016-02-04 11:10:37 -08:00
|
|
|
Ok(0) => return Ok((p, ours)),
|
2015-04-03 15:34:15 -07:00
|
|
|
Ok(8) => {
|
|
|
|
|
assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
|
|
|
|
|
"Validation on the CLOEXEC pipe failed: {:?}", bytes);
|
|
|
|
|
let errno = combine(&bytes[0.. 4]);
|
|
|
|
|
assert!(p.wait().is_ok(),
|
|
|
|
|
"wait() should either return Ok or panic");
|
|
|
|
|
return Err(Error::from_raw_os_error(errno))
|
|
|
|
|
}
|
|
|
|
|
Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
|
|
|
|
|
Err(e) => {
|
|
|
|
|
assert!(p.wait().is_ok(),
|
|
|
|
|
"wait() should either return Ok or panic");
|
|
|
|
|
panic!("the CLOEXEC pipe failed: {:?}", e)
|
|
|
|
|
},
|
|
|
|
|
Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
|
|
|
|
|
assert!(p.wait().is_ok(),
|
|
|
|
|
"wait() should either return Ok or panic");
|
|
|
|
|
panic!("short read on the CLOEXEC pipe")
|
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-03 15:34:15 -07:00
|
|
|
fn combine(arr: &[u8]) -> i32 {
|
|
|
|
|
let a = arr[0] as u32;
|
|
|
|
|
let b = arr[1] as u32;
|
|
|
|
|
let c = arr[2] as u32;
|
|
|
|
|
let d = arr[3] as u32;
|
2015-02-06 09:42:57 -08:00
|
|
|
|
2015-04-03 15:34:15 -07:00
|
|
|
((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
|
2015-02-24 18:15:03 +09:00
|
|
|
}
|
2015-04-03 15:34:15 -07:00
|
|
|
}
|
2015-02-24 18:15:03 +09:00
|
|
|
|
2015-04-03 15:34:15 -07:00
|
|
|
// And at this point we've reached a special time in the life of the
|
|
|
|
|
// child. The child must now be considered hamstrung and unable to
|
|
|
|
|
// do anything other than syscalls really. Consider the following
|
|
|
|
|
// scenario:
|
|
|
|
|
//
|
|
|
|
|
// 1. Thread A of process 1 grabs the malloc() mutex
|
|
|
|
|
// 2. Thread B of process 1 forks(), creating thread C
|
|
|
|
|
// 3. Thread C of process 2 then attempts to malloc()
|
|
|
|
|
// 4. The memory of process 2 is the same as the memory of
|
|
|
|
|
// process 1, so the mutex is locked.
|
|
|
|
|
//
|
|
|
|
|
// This situation looks a lot like deadlock, right? It turns out
|
|
|
|
|
// that this is what pthread_atfork() takes care of, which is
|
|
|
|
|
// presumably implemented across platforms. The first thing that
|
|
|
|
|
// threads to *before* forking is to do things like grab the malloc
|
|
|
|
|
// mutex, and then after the fork they unlock it.
|
|
|
|
|
//
|
|
|
|
|
// Despite this information, libnative's spawn has been witnessed to
|
|
|
|
|
// deadlock on both OSX and FreeBSD. I'm not entirely sure why, but
|
|
|
|
|
// all collected backtraces point at malloc/free traffic in the
|
|
|
|
|
// child spawned process.
|
|
|
|
|
//
|
|
|
|
|
// For this reason, the block of code below should contain 0
|
|
|
|
|
// invocations of either malloc of free (or their related friends).
|
|
|
|
|
//
|
|
|
|
|
// As an example of not having malloc/free traffic, we don't close
|
|
|
|
|
// this file descriptor by dropping the FileDesc (which contains an
|
|
|
|
|
// allocation). Instead we just close it manually. This will never
|
|
|
|
|
// have the drop glue anyway because this code never returns (the
|
|
|
|
|
// child will either exec() or invoke libc::exit)
|
2016-02-04 11:10:37 -08:00
|
|
|
unsafe fn exec(&mut self, stdio: ChildPipes) -> io::Error {
|
2015-10-31 11:09:43 -07:00
|
|
|
macro_rules! try {
|
|
|
|
|
($e:expr) => (match $e {
|
|
|
|
|
Ok(e) => e,
|
|
|
|
|
Err(e) => return e,
|
|
|
|
|
})
|
2015-02-24 18:15:03 +09:00
|
|
|
}
|
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
if let Some(fd) = stdio.stdin.fd() {
|
|
|
|
|
try!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
|
|
|
|
|
}
|
|
|
|
|
if let Some(fd) = stdio.stdout.fd() {
|
|
|
|
|
try!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
|
|
|
|
|
}
|
|
|
|
|
if let Some(fd) = stdio.stderr.fd() {
|
|
|
|
|
try!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
|
|
|
|
|
}
|
2015-04-03 15:34:15 -07:00
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
if let Some(u) = self.gid {
|
2015-10-31 11:09:43 -07:00
|
|
|
try!(cvt(libc::setgid(u as gid_t)));
|
2015-04-03 15:34:15 -07:00
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
if let Some(u) = self.uid {
|
2015-04-03 15:34:15 -07:00
|
|
|
// When dropping privileges from root, the `setgroups` call
|
|
|
|
|
// will remove any extraneous groups. If we don't call this,
|
|
|
|
|
// then even though our uid has dropped, we may still have
|
|
|
|
|
// groups that enable us to do super-user things. This will
|
|
|
|
|
// fail if we aren't root, so don't bother checking the
|
|
|
|
|
// return value, this is just done as an optimistic
|
|
|
|
|
// privilege dropping function.
|
2015-11-02 16:23:22 -08:00
|
|
|
let _ = libc::setgroups(0, ptr::null());
|
2015-04-03 15:34:15 -07:00
|
|
|
|
2015-10-31 11:09:43 -07:00
|
|
|
try!(cvt(libc::setuid(u as uid_t)));
|
2015-04-03 15:34:15 -07:00
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
if self.session_leader {
|
2015-04-03 15:34:15 -07:00
|
|
|
// Don't check the error of setsid because it fails if we're the
|
|
|
|
|
// process leader already. We just forked so it shouldn't return
|
|
|
|
|
// error, but ignore it anyway.
|
|
|
|
|
let _ = libc::setsid();
|
|
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
if let Some(ref cwd) = self.cwd {
|
2015-10-31 11:09:43 -07:00
|
|
|
try!(cvt(libc::chdir(cwd.as_ptr())));
|
2015-04-03 15:34:15 -07:00
|
|
|
}
|
2016-02-04 11:10:37 -08:00
|
|
|
if let Some(ref envp) = self.envp {
|
2015-10-31 11:09:43 -07:00
|
|
|
*sys::os::environ() = envp.as_ptr();
|
2015-04-03 15:34:15 -07:00
|
|
|
}
|
2015-05-23 22:25:49 -04:00
|
|
|
|
2015-10-31 11:09:43 -07:00
|
|
|
// NaCl has no signal support.
|
|
|
|
|
if cfg!(not(target_os = "nacl")) {
|
2015-10-24 20:51:34 -05:00
|
|
|
// Reset signal handling so the child process starts in a
|
|
|
|
|
// standardized state. libstd ignores SIGPIPE, and signal-handling
|
|
|
|
|
// libraries often set a mask. Child processes inherit ignored
|
|
|
|
|
// signals and the signal mask from their parent, but most
|
|
|
|
|
// UNIX programs do not reset these things on their own, so we
|
|
|
|
|
// need to clean things up now to avoid confusing the program
|
|
|
|
|
// we're about to run.
|
2015-11-02 16:23:22 -08:00
|
|
|
let mut set: libc::sigset_t = mem::uninitialized();
|
2015-10-31 11:09:43 -07:00
|
|
|
try!(cvt(libc::sigemptyset(&mut set)));
|
|
|
|
|
try!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
|
|
|
|
|
ptr::null_mut())));
|
|
|
|
|
let ret = libc::signal(libc::SIGPIPE, libc::SIG_DFL);
|
|
|
|
|
if ret == libc::SIG_ERR {
|
|
|
|
|
return io::Error::last_os_error()
|
2015-10-24 20:51:34 -05:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-23 22:25:49 -04:00
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
for callback in self.closures.iter_mut() {
|
2016-02-03 16:55:59 -08:00
|
|
|
try!(callback());
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
libc::execvp(self.argv[0], self.argv.as_ptr());
|
2015-10-31 11:09:43 -07:00
|
|
|
io::Error::last_os_error()
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
|
|
|
|
|
fn setup_io(&self, default: Stdio) -> io::Result<(StdioPipes, ChildPipes)> {
|
|
|
|
|
let stdin = self.stdin.as_ref().unwrap_or(&default);
|
|
|
|
|
let stdout = self.stdout.as_ref().unwrap_or(&default);
|
|
|
|
|
let stderr = self.stderr.as_ref().unwrap_or(&default);
|
|
|
|
|
let (their_stdin, our_stdin) = try!(stdin.to_child_stdio(true));
|
|
|
|
|
let (their_stdout, our_stdout) = try!(stdout.to_child_stdio(false));
|
|
|
|
|
let (their_stderr, our_stderr) = try!(stderr.to_child_stdio(false));
|
|
|
|
|
let ours = StdioPipes {
|
|
|
|
|
stdin: our_stdin,
|
|
|
|
|
stdout: our_stdout,
|
|
|
|
|
stderr: our_stderr,
|
|
|
|
|
};
|
|
|
|
|
let theirs = ChildPipes {
|
|
|
|
|
stdin: their_stdin,
|
|
|
|
|
stdout: their_stdout,
|
|
|
|
|
stderr: their_stderr,
|
|
|
|
|
};
|
|
|
|
|
Ok((ours, theirs))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString {
|
|
|
|
|
CString::new(s.as_bytes()).unwrap_or_else(|_e| {
|
|
|
|
|
*saw_nul = true;
|
|
|
|
|
CString::new("<string-with-nul>").unwrap()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Stdio {
|
|
|
|
|
fn to_child_stdio(&self, readable: bool)
|
|
|
|
|
-> io::Result<(ChildStdio, Option<AnonPipe>)> {
|
|
|
|
|
match *self {
|
|
|
|
|
Stdio::Inherit => Ok((ChildStdio::Inherit, None)),
|
|
|
|
|
|
|
|
|
|
// Make sure that the source descriptors are not an stdio
|
|
|
|
|
// descriptor, otherwise the order which we set the child's
|
|
|
|
|
// descriptors may blow away a descriptor which we are hoping to
|
|
|
|
|
// save. For example, suppose we want the child's stderr to be the
|
|
|
|
|
// parent's stdout, and the child's stdout to be the parent's
|
|
|
|
|
// stderr. No matter which we dup first, the second will get
|
|
|
|
|
// overwritten prematurely.
|
|
|
|
|
Stdio::Fd(ref fd) => {
|
|
|
|
|
if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO {
|
|
|
|
|
Ok((ChildStdio::Owned(try!(fd.duplicate())), None))
|
|
|
|
|
} else {
|
|
|
|
|
Ok((ChildStdio::Explicit(fd.raw()), None))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stdio::MakePipe => {
|
|
|
|
|
let (reader, writer) = try!(pipe::anon_pipe());
|
|
|
|
|
let (ours, theirs) = if readable {
|
|
|
|
|
(writer, reader)
|
|
|
|
|
} else {
|
|
|
|
|
(reader, writer)
|
|
|
|
|
};
|
|
|
|
|
Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Stdio::Null => {
|
|
|
|
|
let mut opts = OpenOptions::new();
|
|
|
|
|
opts.read(readable);
|
|
|
|
|
opts.write(!readable);
|
|
|
|
|
let path = unsafe {
|
|
|
|
|
CStr::from_ptr("/dev/null\0".as_ptr() as *const _)
|
|
|
|
|
};
|
|
|
|
|
let fd = try!(File::open_c(&path, &opts));
|
|
|
|
|
Ok((ChildStdio::Owned(fd.into_fd()), None))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ChildStdio {
|
|
|
|
|
fn fd(&self) -> Option<c_int> {
|
|
|
|
|
match *self {
|
|
|
|
|
ChildStdio::Inherit => None,
|
|
|
|
|
ChildStdio::Explicit(fd) => Some(fd),
|
|
|
|
|
ChildStdio::Owned(ref fd) => Some(fd.raw()),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pair_to_key(key: &OsStr, value: &OsStr, saw_nul: &mut bool) -> CString {
|
|
|
|
|
let (key, value) = (key.as_bytes(), value.as_bytes());
|
|
|
|
|
let mut v = Vec::with_capacity(key.len() + value.len() + 1);
|
|
|
|
|
v.extend(key);
|
|
|
|
|
v.push(b'=');
|
|
|
|
|
v.extend(value);
|
|
|
|
|
CString::new(v).unwrap_or_else(|_e| {
|
|
|
|
|
*saw_nul = true;
|
|
|
|
|
CString::new("foo=bar").unwrap()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Debug for Command {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
try!(write!(f, "{:?}", self.program));
|
|
|
|
|
for arg in &self.args {
|
|
|
|
|
try!(write!(f, " {:?}", arg));
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Processes
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
/// Unix exit statuses
|
|
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
|
|
|
|
pub struct ExitStatus(c_int);
|
|
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "linux", target_os = "android",
|
|
|
|
|
target_os = "nacl"))]
|
|
|
|
|
mod status_imp {
|
|
|
|
|
pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
|
|
|
|
|
pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
|
|
|
|
|
pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(any(target_os = "macos",
|
|
|
|
|
target_os = "ios",
|
|
|
|
|
target_os = "freebsd",
|
|
|
|
|
target_os = "dragonfly",
|
|
|
|
|
target_os = "bitrig",
|
|
|
|
|
target_os = "netbsd",
|
|
|
|
|
target_os = "openbsd"))]
|
|
|
|
|
mod status_imp {
|
|
|
|
|
pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
|
|
|
|
|
pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
|
|
|
|
|
pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ExitStatus {
|
|
|
|
|
fn exited(&self) -> bool {
|
|
|
|
|
status_imp::WIFEXITED(self.0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn success(&self) -> bool {
|
|
|
|
|
self.code() == Some(0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn code(&self) -> Option<i32> {
|
|
|
|
|
if self.exited() {
|
|
|
|
|
Some(status_imp::WEXITSTATUS(self.0))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn signal(&self) -> Option<i32> {
|
|
|
|
|
if !self.exited() {
|
|
|
|
|
Some(status_imp::WTERMSIG(self.0))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for ExitStatus {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
if let Some(code) = self.code() {
|
|
|
|
|
write!(f, "exit code: {}", code)
|
|
|
|
|
} else {
|
|
|
|
|
let signal = self.signal().unwrap();
|
|
|
|
|
write!(f, "signal: {}", signal)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The unique id of the process (this should never be negative).
|
|
|
|
|
pub struct Process {
|
|
|
|
|
pid: pid_t,
|
|
|
|
|
status: Option<ExitStatus>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
|
|
|
|
|
|
|
|
|
|
impl Process {
|
2015-04-16 09:44:05 -07:00
|
|
|
pub fn id(&self) -> u32 {
|
|
|
|
|
self.pid as u32
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-03 18:09:35 -08:00
|
|
|
pub fn kill(&mut self) -> io::Result<()> {
|
|
|
|
|
// If we've already waited on this process then the pid can be recycled
|
|
|
|
|
// and used for another process, and we probably shouldn't be killing
|
|
|
|
|
// random processes, so just return an error.
|
|
|
|
|
if self.status.is_some() {
|
|
|
|
|
Err(Error::new(ErrorKind::InvalidInput,
|
|
|
|
|
"invalid argument: can't kill an exited process"))
|
|
|
|
|
} else {
|
|
|
|
|
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
|
|
|
|
|
}
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
|
2016-02-03 18:09:35 -08:00
|
|
|
pub fn wait(&mut self) -> io::Result<ExitStatus> {
|
|
|
|
|
if let Some(status) = self.status {
|
|
|
|
|
return Ok(status)
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
2016-02-03 18:09:35 -08:00
|
|
|
let mut status = 0 as c_int;
|
|
|
|
|
try!(cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) }));
|
|
|
|
|
self.status = Some(ExitStatus(status));
|
|
|
|
|
Ok(ExitStatus(status))
|
2015-02-06 09:42:57 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-23 22:25:49 -04:00
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use prelude::v1::*;
|
|
|
|
|
|
|
|
|
|
use ffi::OsStr;
|
|
|
|
|
use mem;
|
|
|
|
|
use ptr;
|
|
|
|
|
use libc;
|
2016-02-04 11:10:37 -08:00
|
|
|
use sys::cvt;
|
2015-05-23 22:25:49 -04:00
|
|
|
|
2015-09-18 10:19:23 -07:00
|
|
|
macro_rules! t {
|
|
|
|
|
($e:expr) => {
|
|
|
|
|
match $e {
|
|
|
|
|
Ok(t) => t,
|
|
|
|
|
Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-11 16:23:57 -04:00
|
|
|
#[cfg(not(target_os = "android"))]
|
2015-05-23 22:25:49 -04:00
|
|
|
extern {
|
2015-09-20 17:39:52 +02:00
|
|
|
#[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")]
|
2015-11-02 16:23:22 -08:00
|
|
|
fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int;
|
2015-05-23 22:25:49 -04:00
|
|
|
}
|
|
|
|
|
|
2015-06-11 16:23:57 -04:00
|
|
|
#[cfg(target_os = "android")]
|
2015-11-02 16:23:22 -08:00
|
|
|
unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int {
|
2015-12-18 13:29:49 +01:00
|
|
|
use slice;
|
|
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::<libc::sigset_t>());
|
2015-06-11 16:23:57 -04:00
|
|
|
let bit = (signum - 1) as usize;
|
|
|
|
|
raw[bit / 8] |= 1 << (bit % 8);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-04 09:03:01 -07:00
|
|
|
// See #14232 for more information, but it appears that signal delivery to a
|
|
|
|
|
// newly spawned process may just be raced in the OSX, so to prevent this
|
|
|
|
|
// test from being flaky we ignore it on OSX.
|
2015-05-23 22:25:49 -04:00
|
|
|
#[test]
|
2015-08-04 09:03:01 -07:00
|
|
|
#[cfg_attr(target_os = "macos", ignore)]
|
2015-10-24 20:51:34 -05:00
|
|
|
#[cfg_attr(target_os = "nacl", ignore)] // no signals on NaCl.
|
2015-05-23 22:25:49 -04:00
|
|
|
fn test_process_mask() {
|
|
|
|
|
unsafe {
|
|
|
|
|
// Test to make sure that a signal mask does not get inherited.
|
2016-02-04 11:10:37 -08:00
|
|
|
let mut cmd = Command::new(OsStr::new("cat"));
|
2015-05-23 22:25:49 -04:00
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
let mut set: libc::sigset_t = mem::uninitialized();
|
|
|
|
|
let mut old_set: libc::sigset_t = mem::uninitialized();
|
|
|
|
|
t!(cvt(libc::sigemptyset(&mut set)));
|
2015-09-18 10:19:23 -07:00
|
|
|
t!(cvt(sigaddset(&mut set, libc::SIGINT)));
|
2015-11-02 16:23:22 -08:00
|
|
|
t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set)));
|
2015-05-23 22:25:49 -04:00
|
|
|
|
2016-02-04 11:10:37 -08:00
|
|
|
cmd.stdin(Stdio::MakePipe);
|
|
|
|
|
cmd.stdout(Stdio::MakePipe);
|
|
|
|
|
|
|
|
|
|
let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null));
|
|
|
|
|
let stdin_write = pipes.stdin.take().unwrap();
|
|
|
|
|
let stdout_read = pipes.stdout.take().unwrap();
|
2015-05-23 22:25:49 -04:00
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &old_set,
|
|
|
|
|
ptr::null_mut())));
|
2015-05-23 22:25:49 -04:00
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT)));
|
2015-05-23 22:25:49 -04:00
|
|
|
// We need to wait until SIGINT is definitely delivered. The
|
|
|
|
|
// easiest way is to write something to cat, and try to read it
|
|
|
|
|
// back: if SIGINT is unmasked, it'll get delivered when cat is
|
|
|
|
|
// next scheduled.
|
|
|
|
|
let _ = stdin_write.write(b"Hello");
|
|
|
|
|
drop(stdin_write);
|
|
|
|
|
|
|
|
|
|
// Either EOF or failure (EPIPE) is okay.
|
|
|
|
|
let mut buf = [0; 5];
|
|
|
|
|
if let Ok(ret) = stdout_read.read(&mut buf) {
|
|
|
|
|
assert!(ret == 0);
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-18 10:19:23 -07:00
|
|
|
t!(cat.wait());
|
2015-05-23 22:25:49 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|