1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 #![unstable(reason = "not public", issue = "0", feature = "fd")]
16 use libc
::{self, c_int, size_t, c_void}
;
18 use sync
::atomic
::{AtomicBool, Ordering}
;
20 use sys_common
::AsInner
;
21 use sys_common
::io
::read_to_end_uninitialized
;
28 pub fn new(fd
: c_int
) -> FileDesc
{
32 pub fn raw(&self) -> c_int { self.fd }
34 /// Extracts the actual filedescriptor without closing it.
35 pub fn into_raw(self) -> c_int
{
41 pub fn read(&self, buf
: &mut [u8]) -> io
::Result
<usize> {
42 let ret
= cvt(unsafe {
44 buf
.as_mut_ptr() as *mut c_void
,
50 pub fn read_to_end(&self, buf
: &mut Vec
<u8>) -> io
::Result
<usize> {
52 (&mut me
).read_to_end(buf
)
55 pub fn write(&self, buf
: &[u8]) -> io
::Result
<usize> {
56 let ret
= cvt(unsafe {
58 buf
.as_ptr() as *const c_void
,
64 #[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten")))]
65 pub fn set_cloexec(&self) {
67 let ret
= libc
::ioctl(self.fd
, libc
::FIOCLEX
);
68 debug_assert_eq
!(ret
, 0);
71 #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))]
72 pub fn set_cloexec(&self) {
74 let previous
= libc
::fcntl(self.fd
, libc
::F_GETFD
);
75 let ret
= libc
::fcntl(self.fd
, libc
::F_SETFD
, previous
| libc
::FD_CLOEXEC
);
76 debug_assert_eq
!(ret
, 0);
80 pub fn set_nonblocking(&self, nonblocking
: bool
) {
82 let previous
= libc
::fcntl(self.fd
, libc
::F_GETFL
);
83 debug_assert
!(previous
!= -1);
84 let new
= if nonblocking
{
85 previous
| libc
::O_NONBLOCK
87 previous
& !libc
::O_NONBLOCK
89 let ret
= libc
::fcntl(self.fd
, libc
::F_SETFL
, new
);
90 debug_assert
!(ret
!= -1);
94 pub fn duplicate(&self) -> io
::Result
<FileDesc
> {
95 // We want to atomically duplicate this file descriptor and set the
96 // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
97 // flag, however, isn't supported on older Linux kernels (earlier than
100 // To detect this and ensure that CLOEXEC is still set, we
101 // follow a strategy similar to musl [1] where if passing
102 // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
103 // supported (the third parameter, 0, is always valid), so we stop
106 // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
107 // resolve so we at least compile this.
109 // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
110 #[cfg(target_os = "android")]
111 use libc
::F_DUPFD
as F_DUPFD_CLOEXEC
;
112 #[cfg(not(target_os = "android"))]
113 use libc
::F_DUPFD_CLOEXEC
;
115 let make_filedesc
= |fd
| {
116 let fd
= FileDesc
::new(fd
);
120 static TRY_CLOEXEC
: AtomicBool
=
121 AtomicBool
::new(!cfg
!(target_os
= "android"));
123 if TRY_CLOEXEC
.load(Ordering
::Relaxed
) {
124 match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }
) {
125 // We *still* call the `set_cloexec` method as apparently some
126 // linux kernel at some point stopped setting CLOEXEC even
127 // though it reported doing so on F_DUPFD_CLOEXEC.
129 return Ok(if cfg
!(target_os
= "linux") {
135 Err(ref e
) if e
.raw_os_error() == Some(libc
::EINVAL
) => {
136 TRY_CLOEXEC
.store(false, Ordering
::Relaxed
);
138 Err(e
) => return Err(e
),
141 cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }
).map(make_filedesc
)
145 impl<'a
> Read
for &'a FileDesc
{
146 fn read(&mut self, buf
: &mut [u8]) -> io
::Result
<usize> {
150 fn read_to_end(&mut self, buf
: &mut Vec
<u8>) -> io
::Result
<usize> {
151 unsafe { read_to_end_uninitialized(self, buf) }
155 impl AsInner
<c_int
> for FileDesc
{
156 fn as_inner(&self) -> &c_int { &self.fd }
159 impl Drop
for FileDesc
{
161 // Note that errors are ignored when closing a file descriptor. The
162 // reason for this is that if an error occurs we don't actually know if
163 // the file descriptor was closed or not, and if we retried (for
164 // something like EINTR), we might close another valid file descriptor
165 // (opened after we closed ours.
166 let _
= unsafe { libc::close(self.fd) }
;