Skip to content

Commit d5381f3

Browse files
committed
Use the real pipe2(2) on all BSD targets.
All supported non-Apple platforms now use the native syscall. Only ios and macos lack it. Deprecate pipe2 on those platforms, because it's impossible to guarantee atomicity with a userland implementation. It was added in: * DragonflyBSD 4.2 * FreeBSD 10.0 * NetBSD 6.0 * OpenBSD 5.7
1 parent 86ebf7b commit d5381f3

File tree

3 files changed

+57
-10
lines changed

3 files changed

+57
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
4747
([#800](https://github.com/nix-rust/nix/pull/800))
4848

4949
### Changed
50+
- Use native `pipe` on all BSD targets. Users should notice no difference.
51+
([#777](https://github.com/nix-rust/nix/pull/777))
5052
- Renamed existing `ptrace` wrappers to encourage namespacing ([#692](https://github.com/nix-rust/nix/pull/692))
5153
- Marked `sys::ptrace::ptrace` as `unsafe`.
5254
- Changed function signature of `socket()` and `socketpair()`. The `protocol` argument

src/unistd.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -876,10 +876,22 @@ pub fn pipe() -> Result<(RawFd, RawFd)> {
876876
}
877877
}
878878

879-
// libc only defines `pipe2` in `libc::notbsd`.
880-
#[cfg(any(target_os = "linux",
881-
target_os = "android",
882-
target_os = "emscripten"))]
879+
/// Like `pipe`, but allows setting certain file descriptor flags.
880+
///
881+
/// The following flags are supported, and will be set atomically as the pipe is
882+
/// created:
883+
///
884+
/// `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors.
885+
/// `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe.
886+
///
887+
/// See also [pipe(2)](http://man7.org/linux/man-pages/man2/pipe.2.html)
888+
#[cfg(any(target_os = "android",
889+
target_os = "dragonfly",
890+
target_os = "emscripten",
891+
target_os = "freebsd",
892+
target_os = "linux",
893+
target_os = "netbsd",
894+
target_os = "openbsd"))]
883895
pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
884896
let mut fds: [c_int; 2] = unsafe { mem::uninitialized() };
885897

@@ -890,9 +902,19 @@ pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
890902
Ok((fds[0], fds[1]))
891903
}
892904

893-
#[cfg(not(any(target_os = "linux",
894-
target_os = "android",
895-
target_os = "emscripten")))]
905+
/// Like `pipe`, but allows setting certain file descriptor flags.
906+
///
907+
/// The following flags are supported, and will be set after the pipe is
908+
/// created:
909+
///
910+
/// `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors.
911+
/// `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe.
912+
#[cfg(any(target_os = "ios",
913+
target_os = "macos"))]
914+
#[deprecated(
915+
since="0.10.0",
916+
note="pipe2(2) is not actually atomic on these platforms. Use pipe(2) and fcntl(2) instead"
917+
)]
896918
pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
897919
let mut fds: [c_int; 2] = unsafe { mem::uninitialized() };
898920

@@ -905,9 +927,8 @@ pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
905927
Ok((fds[0], fds[1]))
906928
}
907929

908-
#[cfg(not(any(target_os = "linux",
909-
target_os = "android",
910-
target_os = "emscripten")))]
930+
#[cfg(any(target_os = "ios",
931+
target_os = "macos"))]
911932
fn pipe2_setflags(fd1: RawFd, fd2: RawFd, flags: OFlag) -> Result<()> {
912933
use fcntl::FdFlag;
913934
use fcntl::FcntlArg::F_SETFL;

test/test_unistd.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate tempdir;
22

3+
use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
34
use nix::unistd::*;
45
use nix::unistd::ForkResult::*;
56
use nix::sys::wait::*;
@@ -380,3 +381,26 @@ fn test_sysconf_unsupported() {
380381
let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
381382
assert!(open_max.expect("sysconf failed").is_none())
382383
}
384+
385+
// Test that we can create a pair of pipes. No need to verify that they pass
386+
// data; that's the domain of the OS, not nix.
387+
#[test]
388+
fn test_pipe() {
389+
let (fd0, fd1) = pipe().unwrap();
390+
let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode);
391+
// S_IFIFO means it's a pipe
392+
assert_eq!(m0, SFlag::S_IFIFO);
393+
let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode);
394+
assert_eq!(m1, SFlag::S_IFIFO);
395+
}
396+
397+
// pipe2(2) is the same as pipe(2), except it allows setting some flags. Check
398+
// that we can set a flag.
399+
#[test]
400+
fn test_pipe2() {
401+
let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
402+
let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap());
403+
assert!(f0.contains(FdFlag::FD_CLOEXEC));
404+
let f1 = FdFlag::from_bits_truncate(fcntl(fd1, FcntlArg::F_GETFD).unwrap());
405+
assert!(f1.contains(FdFlag::FD_CLOEXEC));
406+
}

0 commit comments

Comments
 (0)