Skip to content

Commit 812b309

Browse files
committed
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.
1 parent 4631518 commit 812b309

File tree

2 files changed

+38
-4
lines changed

2 files changed

+38
-4
lines changed

src/libstd/sys/unix/pipe.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use sys::fd::FileDesc;
1211
use io;
13-
use libc;
12+
use libc::{self, c_int};
13+
use sys::cvt_r;
14+
use sys::fd::FileDesc;
1415

1516
////////////////////////////////////////////////////////////////////////////////
1617
// Anonymous pipes
@@ -20,6 +21,24 @@ pub struct AnonPipe(FileDesc);
2021

2122
pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
2223
let mut fds = [0; 2];
24+
25+
// Unfortunately the only known way right now to create atomically set the
26+
// CLOEXEC flag is to use the `pipe2` syscall on Linux. This was added in
27+
// 2.6.27, however, and because we support 2.6.18 we must detect this
28+
// support dynamically.
29+
if cfg!(target_os = "linux") {
30+
weak! { fn pipe2(*mut c_int, c_int) -> c_int }
31+
if let Some(pipe) = pipe2.get() {
32+
match cvt_r(|| unsafe { pipe(fds.as_mut_ptr(), libc::O_CLOEXEC) }) {
33+
Ok(_) => {
34+
return Ok((AnonPipe(FileDesc::new(fds[0])),
35+
AnonPipe(FileDesc::new(fds[1]))))
36+
}
37+
Err(ref e) if e.raw_os_error() == Some(libc::ENOSYS) => {}
38+
Err(e) => return Err(e),
39+
}
40+
}
41+
}
2342
if unsafe { libc::pipe(fds.as_mut_ptr()) == 0 } {
2443
Ok((AnonPipe::from_fd(fds[0]), AnonPipe::from_fd(fds[1])))
2544
} else {

src/test/run-pass/fds-are-cloexec.rs

+17-2
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
extern crate libc;
1717

1818
use std::env;
19-
use std::fs::{self, File};
19+
use std::fs::File;
2020
use std::io;
2121
use std::net::{TcpListener, TcpStream, UdpSocket};
2222
use std::os::unix::prelude::*;
23-
use std::process::Command;
23+
use std::process::{Command, Stdio};
2424
use std::thread;
2525

2626
fn main() {
@@ -45,6 +45,17 @@ fn parent() {
4545
let udp1 = UdpSocket::bind("127.0.0.1:0").unwrap();
4646
let udp2 = udp1.try_clone().unwrap();
4747

48+
let mut child = Command::new(env::args().next().unwrap())
49+
.arg("100")
50+
.stdout(Stdio::piped())
51+
.stdin(Stdio::piped())
52+
.stderr(Stdio::piped())
53+
.spawn().unwrap();
54+
let pipe1 = child.stdin.take().unwrap();
55+
let pipe2 = child.stdout.take().unwrap();
56+
let pipe3 = child.stderr.take().unwrap();
57+
58+
4859
let status = Command::new(env::args().next().unwrap())
4960
.arg(file.as_raw_fd().to_string())
5061
.arg(tcp1.as_raw_fd().to_string())
@@ -55,9 +66,13 @@ fn parent() {
5566
.arg(tcp6.as_raw_fd().to_string())
5667
.arg(udp1.as_raw_fd().to_string())
5768
.arg(udp2.as_raw_fd().to_string())
69+
.arg(pipe1.as_raw_fd().to_string())
70+
.arg(pipe2.as_raw_fd().to_string())
71+
.arg(pipe3.as_raw_fd().to_string())
5872
.status()
5973
.unwrap();
6074
assert!(status.success());
75+
child.wait().unwrap();
6176
}
6277

6378
fn child(args: &[String]) {

0 commit comments

Comments
 (0)