2015-02-03 05:39:14 +00:00
|
|
|
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2016-02-12 18:29:25 +00:00
|
|
|
#![unstable(reason = "not public", issue = "0", feature = "fd")]
|
|
|
|
|
2016-02-12 08:17:24 +00:00
|
|
|
use prelude::v1::*;
|
|
|
|
|
|
|
|
use io::{self, Read};
|
2015-02-03 05:39:14 +00:00
|
|
|
use libc::{self, c_int, size_t, c_void};
|
|
|
|
use mem;
|
2016-02-12 08:17:24 +00:00
|
|
|
use sync::atomic::{AtomicBool, Ordering};
|
2015-02-03 05:39:14 +00:00
|
|
|
use sys::cvt;
|
2015-02-06 00:50:11 +00:00
|
|
|
use sys_common::AsInner;
|
2016-02-12 08:17:24 +00:00
|
|
|
use sys_common::io::read_to_end_uninitialized;
|
2015-02-03 05:39:14 +00:00
|
|
|
|
|
|
|
pub struct FileDesc {
|
|
|
|
fd: c_int,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl FileDesc {
|
|
|
|
pub fn new(fd: c_int) -> FileDesc {
|
|
|
|
FileDesc { fd: fd }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn raw(&self) -> c_int { self.fd }
|
|
|
|
|
2015-04-13 14:21:32 +00:00
|
|
|
/// Extracts the actual filedescriptor without closing it.
|
2015-02-03 05:39:14 +00:00
|
|
|
pub fn into_raw(self) -> c_int {
|
|
|
|
let fd = self.fd;
|
2015-05-07 17:49:39 +00:00
|
|
|
mem::forget(self);
|
2015-02-03 05:39:14 +00:00
|
|
|
fd
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let ret = cvt(unsafe {
|
2015-02-03 05:39:14 +00:00
|
|
|
libc::read(self.fd,
|
|
|
|
buf.as_mut_ptr() as *mut c_void,
|
|
|
|
buf.len() as size_t)
|
2016-03-23 03:01:37 +00:00
|
|
|
})?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(ret as usize)
|
|
|
|
}
|
|
|
|
|
2016-02-12 08:17:24 +00:00
|
|
|
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
|
|
|
let mut me = self;
|
|
|
|
(&mut me).read_to_end(buf)
|
|
|
|
}
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
2016-03-23 03:01:37 +00:00
|
|
|
let ret = cvt(unsafe {
|
2015-02-03 05:39:14 +00:00
|
|
|
libc::write(self.fd,
|
|
|
|
buf.as_ptr() as *const c_void,
|
|
|
|
buf.len() as size_t)
|
2016-03-23 03:01:37 +00:00
|
|
|
})?;
|
2015-02-03 05:39:14 +00:00
|
|
|
Ok(ret as usize)
|
|
|
|
}
|
2015-04-03 22:30:10 +00:00
|
|
|
|
2016-02-10 09:28:51 +00:00
|
|
|
#[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten")))]
|
2015-04-03 22:30:10 +00:00
|
|
|
pub fn set_cloexec(&self) {
|
|
|
|
unsafe {
|
2015-11-03 00:23:22 +00:00
|
|
|
let ret = libc::ioctl(self.fd, libc::FIOCLEX);
|
2016-06-23 11:57:55 +00:00
|
|
|
assert_eq!(ret, 0);
|
2015-04-03 22:30:10 +00:00
|
|
|
}
|
|
|
|
}
|
2016-02-10 09:28:51 +00:00
|
|
|
#[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))]
|
2015-10-25 01:51:34 +00:00
|
|
|
pub fn set_cloexec(&self) {
|
|
|
|
unsafe {
|
2015-12-08 18:38:37 +00:00
|
|
|
let previous = libc::fcntl(self.fd, libc::F_GETFD);
|
|
|
|
let ret = libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC);
|
2016-06-23 11:57:55 +00:00
|
|
|
assert_eq!(ret, 0);
|
2015-10-25 01:51:34 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-21 05:24:23 +00:00
|
|
|
|
2016-02-12 18:29:25 +00:00
|
|
|
pub fn set_nonblocking(&self, nonblocking: bool) {
|
|
|
|
unsafe {
|
|
|
|
let previous = libc::fcntl(self.fd, libc::F_GETFL);
|
2016-06-23 11:57:55 +00:00
|
|
|
assert!(previous != -1);
|
2016-02-12 18:29:25 +00:00
|
|
|
let new = if nonblocking {
|
|
|
|
previous | libc::O_NONBLOCK
|
|
|
|
} else {
|
|
|
|
previous & !libc::O_NONBLOCK
|
|
|
|
};
|
|
|
|
let ret = libc::fcntl(self.fd, libc::F_SETFL, new);
|
2016-06-23 11:57:55 +00:00
|
|
|
assert!(ret != -1);
|
2016-02-12 18:29:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-21 05:24:23 +00:00
|
|
|
pub fn duplicate(&self) -> io::Result<FileDesc> {
|
|
|
|
// We want to atomically duplicate this file descriptor and set the
|
|
|
|
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
|
|
|
|
// flag, however, isn't supported on older Linux kernels (earlier than
|
|
|
|
// 2.6.24).
|
|
|
|
//
|
|
|
|
// To detect this and ensure that CLOEXEC is still set, we
|
|
|
|
// follow a strategy similar to musl [1] where if passing
|
|
|
|
// F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
|
|
|
|
// supported (the third parameter, 0, is always valid), so we stop
|
2016-02-04 21:22:51 +00:00
|
|
|
// trying that.
|
2016-01-21 05:24:23 +00:00
|
|
|
//
|
|
|
|
// Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
|
|
|
|
// resolve so we at least compile this.
|
|
|
|
//
|
|
|
|
// [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
|
|
|
|
#[cfg(target_os = "android")]
|
|
|
|
use libc::F_DUPFD as F_DUPFD_CLOEXEC;
|
|
|
|
#[cfg(not(target_os = "android"))]
|
|
|
|
use libc::F_DUPFD_CLOEXEC;
|
|
|
|
|
|
|
|
let make_filedesc = |fd| {
|
|
|
|
let fd = FileDesc::new(fd);
|
|
|
|
fd.set_cloexec();
|
|
|
|
fd
|
|
|
|
};
|
2016-02-04 21:22:51 +00:00
|
|
|
static TRY_CLOEXEC: AtomicBool =
|
|
|
|
AtomicBool::new(!cfg!(target_os = "android"));
|
2016-01-21 05:24:23 +00:00
|
|
|
let fd = self.raw();
|
2016-02-04 21:22:51 +00:00
|
|
|
if TRY_CLOEXEC.load(Ordering::Relaxed) {
|
2016-01-21 05:24:23 +00:00
|
|
|
match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) {
|
2016-02-04 21:22:51 +00:00
|
|
|
// We *still* call the `set_cloexec` method as apparently some
|
|
|
|
// linux kernel at some point stopped setting CLOEXEC even
|
|
|
|
// though it reported doing so on F_DUPFD_CLOEXEC.
|
|
|
|
Ok(fd) => {
|
|
|
|
return Ok(if cfg!(target_os = "linux") {
|
|
|
|
make_filedesc(fd)
|
|
|
|
} else {
|
|
|
|
FileDesc::new(fd)
|
|
|
|
})
|
|
|
|
}
|
2016-01-21 05:24:23 +00:00
|
|
|
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {
|
|
|
|
TRY_CLOEXEC.store(false, Ordering::Relaxed);
|
|
|
|
}
|
2016-02-04 21:22:51 +00:00
|
|
|
Err(e) => return Err(e),
|
2016-01-21 05:24:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_filedesc)
|
|
|
|
}
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
|
2016-02-12 08:17:24 +00:00
|
|
|
impl<'a> Read for &'a FileDesc {
|
|
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
|
|
(**self).read(buf)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
|
|
|
unsafe { read_to_end_uninitialized(self, buf) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-06 00:50:11 +00:00
|
|
|
impl AsInner<c_int> for FileDesc {
|
|
|
|
fn as_inner(&self) -> &c_int { &self.fd }
|
|
|
|
}
|
|
|
|
|
2015-02-03 05:39:14 +00:00
|
|
|
impl Drop for FileDesc {
|
|
|
|
fn drop(&mut self) {
|
2015-04-03 22:44:14 +00:00
|
|
|
// Note that errors are ignored when closing a file descriptor. The
|
|
|
|
// reason for this is that if an error occurs we don't actually know if
|
|
|
|
// the file descriptor was closed or not, and if we retried (for
|
|
|
|
// something like EINTR), we might close another valid file descriptor
|
|
|
|
// (opened after we closed ours.
|
|
|
|
let _ = unsafe { libc::close(self.fd) };
|
2015-02-03 05:39:14 +00:00
|
|
|
}
|
|
|
|
}
|