]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
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. | |
4 | // | |
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. | |
10 | ||
54a0048b SL |
11 | #![unstable(reason = "not public", issue = "0", feature = "fd")] |
12 | ||
13 | use prelude::v1::*; | |
14 | ||
15 | use io::{self, Read}; | |
85aaf69f SL |
16 | use libc::{self, c_int, size_t, c_void}; |
17 | use mem; | |
54a0048b | 18 | use sync::atomic::{AtomicBool, Ordering}; |
85aaf69f SL |
19 | use sys::cvt; |
20 | use sys_common::AsInner; | |
54a0048b | 21 | use sys_common::io::read_to_end_uninitialized; |
85aaf69f SL |
22 | |
23 | pub struct FileDesc { | |
24 | fd: c_int, | |
25 | } | |
26 | ||
27 | impl FileDesc { | |
28 | pub fn new(fd: c_int) -> FileDesc { | |
29 | FileDesc { fd: fd } | |
30 | } | |
31 | ||
32 | pub fn raw(&self) -> c_int { self.fd } | |
33 | ||
9346a6ac | 34 | /// Extracts the actual filedescriptor without closing it. |
85aaf69f SL |
35 | pub fn into_raw(self) -> c_int { |
36 | let fd = self.fd; | |
bd371182 | 37 | mem::forget(self); |
85aaf69f SL |
38 | fd |
39 | } | |
40 | ||
41 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { | |
54a0048b | 42 | let ret = cvt(unsafe { |
85aaf69f SL |
43 | libc::read(self.fd, |
44 | buf.as_mut_ptr() as *mut c_void, | |
45 | buf.len() as size_t) | |
54a0048b | 46 | })?; |
85aaf69f SL |
47 | Ok(ret as usize) |
48 | } | |
49 | ||
54a0048b SL |
50 | pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { |
51 | let mut me = self; | |
52 | (&mut me).read_to_end(buf) | |
53 | } | |
54 | ||
85aaf69f | 55 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { |
54a0048b | 56 | let ret = cvt(unsafe { |
85aaf69f SL |
57 | libc::write(self.fd, |
58 | buf.as_ptr() as *const c_void, | |
59 | buf.len() as size_t) | |
54a0048b | 60 | })?; |
85aaf69f SL |
61 | Ok(ret as usize) |
62 | } | |
9346a6ac | 63 | |
7453a54e | 64 | #[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten")))] |
9346a6ac AL |
65 | pub fn set_cloexec(&self) { |
66 | unsafe { | |
92a42be0 SL |
67 | let ret = libc::ioctl(self.fd, libc::FIOCLEX); |
68 | debug_assert_eq!(ret, 0); | |
69 | } | |
70 | } | |
7453a54e | 71 | #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))] |
92a42be0 SL |
72 | pub fn set_cloexec(&self) { |
73 | unsafe { | |
9cc50fc6 SL |
74 | let previous = libc::fcntl(self.fd, libc::F_GETFD); |
75 | let ret = libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC); | |
9346a6ac AL |
76 | debug_assert_eq!(ret, 0); |
77 | } | |
78 | } | |
7453a54e | 79 | |
54a0048b SL |
80 | pub fn set_nonblocking(&self, nonblocking: bool) { |
81 | unsafe { | |
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 | |
86 | } else { | |
87 | previous & !libc::O_NONBLOCK | |
88 | }; | |
89 | let ret = libc::fcntl(self.fd, libc::F_SETFL, new); | |
90 | debug_assert!(ret != -1); | |
91 | } | |
92 | } | |
93 | ||
7453a54e SL |
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 | |
98 | // 2.6.24). | |
99 | // | |
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 | |
104 | // trying that. | |
105 | // | |
106 | // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to | |
107 | // resolve so we at least compile this. | |
108 | // | |
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; | |
114 | ||
115 | let make_filedesc = |fd| { | |
116 | let fd = FileDesc::new(fd); | |
117 | fd.set_cloexec(); | |
118 | fd | |
119 | }; | |
120 | static TRY_CLOEXEC: AtomicBool = | |
121 | AtomicBool::new(!cfg!(target_os = "android")); | |
122 | let fd = self.raw(); | |
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. | |
128 | Ok(fd) => { | |
129 | return Ok(if cfg!(target_os = "linux") { | |
130 | make_filedesc(fd) | |
131 | } else { | |
132 | FileDesc::new(fd) | |
133 | }) | |
134 | } | |
135 | Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => { | |
136 | TRY_CLOEXEC.store(false, Ordering::Relaxed); | |
137 | } | |
138 | Err(e) => return Err(e), | |
139 | } | |
140 | } | |
141 | cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_filedesc) | |
142 | } | |
85aaf69f SL |
143 | } |
144 | ||
54a0048b SL |
145 | impl<'a> Read for &'a FileDesc { |
146 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
147 | (**self).read(buf) | |
148 | } | |
149 | ||
150 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { | |
151 | unsafe { read_to_end_uninitialized(self, buf) } | |
152 | } | |
153 | } | |
154 | ||
85aaf69f SL |
155 | impl AsInner<c_int> for FileDesc { |
156 | fn as_inner(&self) -> &c_int { &self.fd } | |
157 | } | |
158 | ||
159 | impl Drop for FileDesc { | |
160 | fn drop(&mut self) { | |
9346a6ac AL |
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) }; | |
85aaf69f SL |
167 | } |
168 | } |