about summary refs log tree commit diff
path: root/src/libstd/sys/unix
diff options
context:
space:
mode:
authorTobias Bucher <tobiasbucher5991@gmail.com>2015-08-24 13:57:11 +0200
committerTobias Bucher <tobiasbucher5991@gmail.com>2015-08-30 10:24:05 +0200
commit1f81ef4d0f2547cacc316b01ad03603ad772e38e (patch)
treee7495a22e3340d8930b5e4be7f95620b9a9b6a51 /src/libstd/sys/unix
parent4bb90232daab451cf58359e0c5874bc905d2f101 (diff)
downloadrust-1f81ef4d0f2547cacc316b01ad03603ad772e38e.tar.gz
rust-1f81ef4d0f2547cacc316b01ad03603ad772e38e.zip
Atomically set CLOEXEC on duplicated sockets
For Bitrig, NetBSD and OpenBSD the constant was incorrectly in posix01, when
it's actually posix08, so we move it. This is a [breaking-change], but we
already had one in #27930.

Fix NetBSD's F_DUPFD_CLOEXEC constant.

For a similar feature detection, see this musl thread:
http://comments.gmane.org/gmane.linux.lib.musl.general/2963

This assumes that an int literal has type `c_int` for varidic functions.
Diffstat (limited to 'src/libstd/sys/unix')
-rw-r--r--src/libstd/sys/unix/net.rs30
1 files changed, 25 insertions, 5 deletions
diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs
index e65f64f2029..f1a9518d08d 100644
--- a/src/libstd/sys/unix/net.rs
+++ b/src/libstd/sys/unix/net.rs
@@ -13,9 +13,10 @@ use prelude::v1::*;
 use ffi::CStr;
 use io;
 use libc::{self, c_int, size_t};
+use net::SocketAddr;
 use str;
+use sync::atomic::{self, AtomicBool};
 use sys::c;
-use net::SocketAddr;
 use sys::fd::FileDesc;
 use sys_common::{AsInner, FromInner, IntoInner};
 use sys_common::net::{getsockopt, setsockopt};
@@ -66,10 +67,29 @@ impl Socket {
     }
 
     pub fn duplicate(&self) -> io::Result<Socket> {
-        let fd = try!(cvt(unsafe { libc::dup(self.0.raw()) }));
-        let fd = FileDesc::new(fd);
-        fd.set_cloexec();
-        Ok(Socket(fd))
+        use libc::funcs::posix88::fcntl::fcntl;
+        let make_socket = |fd| {
+            let fd = FileDesc::new(fd);
+            fd.set_cloexec();
+            Socket(fd)
+        };
+        static EMULATE_F_DUPFD_CLOEXEC: AtomicBool = AtomicBool::new(false);
+        if !EMULATE_F_DUPFD_CLOEXEC.load(atomic::Ordering::Relaxed) {
+            match cvt(unsafe { fcntl(self.0.raw(), libc::F_DUPFD_CLOEXEC, 0) }) {
+                // `EINVAL` can only be returned on two occasions: Invalid
+                // command (second parameter) or invalid third parameter. 0 is
+                // always a valid third parameter, so it must be the second
+                // parameter.
+                //
+                // Store the result in a global variable so we don't try each
+                // syscall twice.
+                Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
+                    EMULATE_F_DUPFD_CLOEXEC.store(true, atomic::Ordering::Relaxed);
+                }
+                res => return res.map(make_socket),
+            }
+        }
+        cvt(unsafe { fcntl(self.0.raw(), libc::F_DUPFD, 0) }).map(make_socket)
     }
 
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {