This commit is contained in:
Jeremy Soller
2016-11-09 20:52:30 -07:00
parent 01e8378070
commit ced32a08f3

View File

@@ -18,7 +18,7 @@ use path::Path;
use sys::fd::FileDesc;
use sys::fs::{File, OpenOptions};
use sys::pipe::{self, AnonPipe};
use sys::{self, cvt};
use sys::cvt;
////////////////////////////////////////////////////////////////////////////////
// Command
@@ -145,78 +145,32 @@ impl Command {
pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
-> io::Result<(Process, StdioPipes)> {
const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";
if self.saw_nul {
return Err(io::Error::new(ErrorKind::InvalidInput,
"nul byte found in provided data"));
}
let (ours, theirs) = self.setup_io(default, needs_stdin)?;
let (input, output) = sys::pipe::anon_pipe()?;
let pid = unsafe {
match cvt(libc::clone(0))? {
match cvt(libc::clone(libc::CLONE_VFORK))? {
0 => {
drop(input);
let err = self.do_exec(theirs);
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());
let _ = libc::exit(1);
let _ = libc::exit((-err.raw_os_error().unwrap_or(libc::EINVAL)) as usize);
unreachable!();
}
n => n as pid_t,
}
};
let mut p = Process { pid: pid, status: None };
drop(output);
let mut bytes = [0; 8];
// loop to handle EINTR
loop {
match input.read(&mut bytes) {
Ok(0) => return Ok((p, ours)),
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))
let mut status_mux = 0;
if cvt(libc::waitpid(pid, &mut status_mux, libc::WNOHANG))? == pid {
match libc::Error::demux(status_mux) {
Ok(status) => Ok((Process { pid: pid, status: Some(ExitStatus::from(status as c_int)) }, ours)),
Err(err) => Err(io::Error::from_raw_os_error(err.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")
}
}
}
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;
((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
} else {
Ok((Process { pid: pid, status: None }, ours))
}
}