diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index 5c29c4c08111d..9527b1e2243d3 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -8,9 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use sys::fd::FileDesc; use io; -use libc; +use libc::{self, c_int}; +use sys::cvt_r; +use sys::fd::FileDesc; //////////////////////////////////////////////////////////////////////////////// // Anonymous pipes @@ -20,6 +21,24 @@ pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { let mut fds = [0; 2]; + + // Unfortunately the only known way right now to create atomically set the + // CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in + // 2.6.27, however, and because we support 2.6.18 we must detect this + // support dynamically. + if cfg!(target_os = "linux") { + weak! { fn pipe2(*mut c_int, c_int) -> c_int } + if let Some(pipe) = pipe2.get() { + match cvt_r(|| unsafe { pipe(fds.as_mut_ptr(), libc::O_CLOEXEC) }) { + Ok(_) => { + return Ok((AnonPipe(FileDesc::new(fds[0])), + AnonPipe(FileDesc::new(fds[1])))) + } + Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {} + Err(e) => return Err(e), + } + } + } if unsafe { libc::pipe(fds.as_mut_ptr()) == 0 } { Ok((AnonPipe::from_fd(fds[0]), AnonPipe::from_fd(fds[1]))) } else { diff --git a/src/test/run-pass/fds-are-cloexec.rs b/src/test/run-pass/fds-are-cloexec.rs index 3be47e8430d59..3c7d2861c877e 100644 --- a/src/test/run-pass/fds-are-cloexec.rs +++ b/src/test/run-pass/fds-are-cloexec.rs @@ -16,11 +16,11 @@ extern crate libc; use std::env; -use std::fs::{self, File}; +use std::fs::File; use std::io; use std::net::{TcpListener, TcpStream, UdpSocket}; use std::os::unix::prelude::*; -use std::process::Command; +use std::process::{Command, Stdio}; use std::thread; fn main() { @@ -45,6 +45,17 @@ fn parent() { let udp1 = UdpSocket::bind("127.0.0.1:0").unwrap(); let udp2 = udp1.try_clone().unwrap(); + let mut child = Command::new(env::args().next().unwrap()) + .arg("100") + .stdout(Stdio::piped()) + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn().unwrap(); + let pipe1 = child.stdin.take().unwrap(); + let pipe2 = child.stdout.take().unwrap(); + let pipe3 = child.stderr.take().unwrap(); + + let status = Command::new(env::args().next().unwrap()) .arg(file.as_raw_fd().to_string()) .arg(tcp1.as_raw_fd().to_string()) @@ -55,9 +66,13 @@ fn parent() { .arg(tcp6.as_raw_fd().to_string()) .arg(udp1.as_raw_fd().to_string()) .arg(udp2.as_raw_fd().to_string()) + .arg(pipe1.as_raw_fd().to_string()) + .arg(pipe2.as_raw_fd().to_string()) + .arg(pipe3.as_raw_fd().to_string()) .status() .unwrap(); assert!(status.success()); + child.wait().unwrap(); } fn child(args: &[String]) {