From 812b309c4791e08aa5bd8dda26c820af43c5fa29 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 4 Feb 2016 15:23:26 -0800 Subject: [PATCH] std: Try to use pipe2 on Linux for pipes This commit attempts to use the `pipe2` syscall on Linux to atomically set the CLOEXEC flag for pipes created. Unfortunately this was added in 2.6.27 so we have to dynamically determine whether we can use it or not. This commit also updates the `fds-are-cloexec.rs` test to test stdio handles for spawned processes as well. --- src/libstd/sys/unix/pipe.rs | 23 +++++++++++++++++++++-- src/test/run-pass/fds-are-cloexec.rs | 19 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) 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]) {