]>
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 | ||
54a0048b | 13 | use io::{self, Read}; |
85aaf69f SL |
14 | use libc::{self, c_int, size_t, c_void}; |
15 | use mem; | |
54a0048b | 16 | use sync::atomic::{AtomicBool, Ordering}; |
85aaf69f SL |
17 | use sys::cvt; |
18 | use sys_common::AsInner; | |
54a0048b | 19 | use sys_common::io::read_to_end_uninitialized; |
85aaf69f SL |
20 | |
21 | pub struct FileDesc { | |
22 | fd: c_int, | |
23 | } | |
24 | ||
25 | impl FileDesc { | |
26 | pub fn new(fd: c_int) -> FileDesc { | |
27 | FileDesc { fd: fd } | |
28 | } | |
29 | ||
30 | pub fn raw(&self) -> c_int { self.fd } | |
31 | ||
9346a6ac | 32 | /// Extracts the actual filedescriptor without closing it. |
85aaf69f SL |
33 | pub fn into_raw(self) -> c_int { |
34 | let fd = self.fd; | |
bd371182 | 35 | mem::forget(self); |
85aaf69f SL |
36 | fd |
37 | } | |
38 | ||
39 | pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { | |
54a0048b | 40 | let ret = cvt(unsafe { |
85aaf69f SL |
41 | libc::read(self.fd, |
42 | buf.as_mut_ptr() as *mut c_void, | |
43 | buf.len() as size_t) | |
54a0048b | 44 | })?; |
85aaf69f SL |
45 | Ok(ret as usize) |
46 | } | |
47 | ||
54a0048b SL |
48 | pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { |
49 | let mut me = self; | |
50 | (&mut me).read_to_end(buf) | |
51 | } | |
52 | ||
85aaf69f | 53 | pub fn write(&self, buf: &[u8]) -> io::Result<usize> { |
54a0048b | 54 | let ret = cvt(unsafe { |
85aaf69f SL |
55 | libc::write(self.fd, |
56 | buf.as_ptr() as *const c_void, | |
57 | buf.len() as size_t) | |
54a0048b | 58 | })?; |
85aaf69f SL |
59 | Ok(ret as usize) |
60 | } | |
9346a6ac | 61 | |
9e0c209e SL |
62 | #[cfg(not(any(target_env = "newlib", |
63 | target_os = "solaris", | |
64 | target_os = "emscripten", | |
65 | target_os = "haiku")))] | |
3157f602 | 66 | pub fn set_cloexec(&self) -> io::Result<()> { |
9346a6ac | 67 | unsafe { |
3157f602 XL |
68 | cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; |
69 | Ok(()) | |
92a42be0 SL |
70 | } |
71 | } | |
9e0c209e SL |
72 | #[cfg(any(target_env = "newlib", |
73 | target_os = "solaris", | |
74 | target_os = "emscripten", | |
75 | target_os = "haiku"))] | |
3157f602 | 76 | pub fn set_cloexec(&self) -> io::Result<()> { |
92a42be0 | 77 | unsafe { |
3157f602 XL |
78 | let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; |
79 | cvt(libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC))?; | |
80 | Ok(()) | |
9346a6ac AL |
81 | } |
82 | } | |
7453a54e | 83 | |
3157f602 | 84 | pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { |
54a0048b | 85 | unsafe { |
3157f602 | 86 | let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?; |
54a0048b SL |
87 | let new = if nonblocking { |
88 | previous | libc::O_NONBLOCK | |
89 | } else { | |
90 | previous & !libc::O_NONBLOCK | |
91 | }; | |
3157f602 XL |
92 | cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; |
93 | Ok(()) | |
54a0048b SL |
94 | } |
95 | } | |
96 | ||
7453a54e SL |
97 | pub fn duplicate(&self) -> io::Result<FileDesc> { |
98 | // We want to atomically duplicate this file descriptor and set the | |
99 | // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This | |
100 | // flag, however, isn't supported on older Linux kernels (earlier than | |
101 | // 2.6.24). | |
102 | // | |
103 | // To detect this and ensure that CLOEXEC is still set, we | |
104 | // follow a strategy similar to musl [1] where if passing | |
105 | // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not | |
106 | // supported (the third parameter, 0, is always valid), so we stop | |
107 | // trying that. | |
108 | // | |
109 | // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to | |
110 | // resolve so we at least compile this. | |
111 | // | |
112 | // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963 | |
9e0c209e | 113 | #[cfg(any(target_os = "android", target_os = "haiku"))] |
7453a54e | 114 | use libc::F_DUPFD as F_DUPFD_CLOEXEC; |
9e0c209e | 115 | #[cfg(not(any(target_os = "android", target_os="haiku")))] |
7453a54e SL |
116 | use libc::F_DUPFD_CLOEXEC; |
117 | ||
118 | let make_filedesc = |fd| { | |
119 | let fd = FileDesc::new(fd); | |
3157f602 XL |
120 | fd.set_cloexec()?; |
121 | Ok(fd) | |
7453a54e SL |
122 | }; |
123 | static TRY_CLOEXEC: AtomicBool = | |
124 | AtomicBool::new(!cfg!(target_os = "android")); | |
125 | let fd = self.raw(); | |
126 | if TRY_CLOEXEC.load(Ordering::Relaxed) { | |
127 | match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) { | |
128 | // We *still* call the `set_cloexec` method as apparently some | |
129 | // linux kernel at some point stopped setting CLOEXEC even | |
130 | // though it reported doing so on F_DUPFD_CLOEXEC. | |
131 | Ok(fd) => { | |
132 | return Ok(if cfg!(target_os = "linux") { | |
3157f602 | 133 | make_filedesc(fd)? |
7453a54e SL |
134 | } else { |
135 | FileDesc::new(fd) | |
136 | }) | |
137 | } | |
138 | Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => { | |
139 | TRY_CLOEXEC.store(false, Ordering::Relaxed); | |
140 | } | |
141 | Err(e) => return Err(e), | |
142 | } | |
143 | } | |
3157f602 | 144 | cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc) |
7453a54e | 145 | } |
85aaf69f SL |
146 | } |
147 | ||
54a0048b SL |
148 | impl<'a> Read for &'a FileDesc { |
149 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
150 | (**self).read(buf) | |
151 | } | |
152 | ||
153 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { | |
154 | unsafe { read_to_end_uninitialized(self, buf) } | |
155 | } | |
156 | } | |
157 | ||
85aaf69f SL |
158 | impl AsInner<c_int> for FileDesc { |
159 | fn as_inner(&self) -> &c_int { &self.fd } | |
160 | } | |
161 | ||
162 | impl Drop for FileDesc { | |
163 | fn drop(&mut self) { | |
9346a6ac AL |
164 | // Note that errors are ignored when closing a file descriptor. The |
165 | // reason for this is that if an error occurs we don't actually know if | |
166 | // the file descriptor was closed or not, and if we retried (for | |
167 | // something like EINTR), we might close another valid file descriptor | |
168 | // (opened after we closed ours. | |
169 | let _ = unsafe { libc::close(self.fd) }; | |
85aaf69f SL |
170 | } |
171 | } |