1 use std::mem;
2 use std::os::unix::io::RawFd;
3 use std::ptr::{null, null_mut};
4 use libc::{self, c_int};
5 use Result;
6 use errno::Errno;
7 use sys::signal::SigSet;
8 use sys::time::{TimeSpec, TimeVal};
9
10 pub use libc::FD_SETSIZE;
11
12 // FIXME: Change to repr(transparent) once it's stable
13 #[repr(C)]
14 #[derive(Clone, Copy)]
15 #[allow(missing_debug_implementations)]
16 pub struct FdSet(libc::fd_set);
17
18 impl FdSet {
new() -> FdSet19 pub fn new() -> FdSet {
20 let mut fdset = unsafe { mem::uninitialized() };
21 unsafe { libc::FD_ZERO(&mut fdset) };
22 FdSet(fdset)
23 }
24
insert(&mut self, fd: RawFd)25 pub fn insert(&mut self, fd: RawFd) {
26 unsafe { libc::FD_SET(fd, &mut self.0) };
27 }
28
remove(&mut self, fd: RawFd)29 pub fn remove(&mut self, fd: RawFd) {
30 unsafe { libc::FD_CLR(fd, &mut self.0) };
31 }
32
contains(&mut self, fd: RawFd) -> bool33 pub fn contains(&mut self, fd: RawFd) -> bool {
34 unsafe { libc::FD_ISSET(fd, &mut self.0) }
35 }
36
clear(&mut self)37 pub fn clear(&mut self) {
38 unsafe { libc::FD_ZERO(&mut self.0) };
39 }
40
41 /// Finds the highest file descriptor in the set.
42 ///
43 /// Returns `None` if the set is empty.
44 ///
45 /// This can be used to calculate the `nfds` parameter of the [`select`] function.
46 ///
47 /// # Example
48 ///
49 /// ```
50 /// # extern crate nix;
51 /// # use nix::sys::select::FdSet;
52 /// # fn main() {
53 /// let mut set = FdSet::new();
54 /// set.insert(4);
55 /// set.insert(9);
56 /// assert_eq!(set.highest(), Some(9));
57 /// # }
58 /// ```
59 ///
60 /// [`select`]: fn.select.html
highest(&mut self) -> Option<RawFd>61 pub fn highest(&mut self) -> Option<RawFd> {
62 for i in (0..FD_SETSIZE).rev() {
63 let i = i as RawFd;
64 if unsafe { libc::FD_ISSET(i, self as *mut _ as *mut libc::fd_set) } {
65 return Some(i)
66 }
67 }
68
69 None
70 }
71 }
72
73 /// Monitors file descriptors for readiness
74 ///
75 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
76 /// file descriptors that are ready for the given operation are set.
77 ///
78 /// When this function returns, `timeout` has an implementation-defined value.
79 ///
80 /// # Parameters
81 ///
82 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
83 /// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
84 /// to the maximum of that.
85 /// * `readfds`: File descriptors to check for being ready to read.
86 /// * `writefds`: File descriptors to check for being ready to write.
87 /// * `errorfds`: File descriptors to check for pending error conditions.
88 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
89 /// indefinitely).
90 ///
91 /// # References
92 ///
93 /// [select(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
94 ///
95 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
select<'a, N, R, W, E, T>(nfds: N, readfds: R, writefds: W, errorfds: E, timeout: T) -> Result<c_int> where N: Into<Option<c_int>>, R: Into<Option<&'a mut FdSet>>, W: Into<Option<&'a mut FdSet>>, E: Into<Option<&'a mut FdSet>>, T: Into<Option<&'a mut TimeVal>>,96 pub fn select<'a, N, R, W, E, T>(nfds: N,
97 readfds: R,
98 writefds: W,
99 errorfds: E,
100 timeout: T) -> Result<c_int>
101 where
102 N: Into<Option<c_int>>,
103 R: Into<Option<&'a mut FdSet>>,
104 W: Into<Option<&'a mut FdSet>>,
105 E: Into<Option<&'a mut FdSet>>,
106 T: Into<Option<&'a mut TimeVal>>,
107 {
108 let mut readfds = readfds.into();
109 let mut writefds = writefds.into();
110 let mut errorfds = errorfds.into();
111 let timeout = timeout.into();
112
113 let nfds = nfds.into().unwrap_or_else(|| {
114 readfds.iter_mut()
115 .chain(writefds.iter_mut())
116 .chain(errorfds.iter_mut())
117 .map(|set| set.highest().unwrap_or(-1))
118 .max()
119 .unwrap_or(-1) + 1
120 });
121
122 let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
123 let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
124 let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
125 let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval)
126 .unwrap_or(null_mut());
127
128 let res = unsafe {
129 libc::select(nfds, readfds, writefds, errorfds, timeout)
130 };
131
132 Errno::result(res)
133 }
134
135 /// Monitors file descriptors for readiness with an altered signal mask.
136 ///
137 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
138 /// file descriptors that are ready for the given operation are set.
139 ///
140 /// When this function returns, the original signal mask is restored.
141 ///
142 /// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
143 ///
144 /// # Parameters
145 ///
146 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
147 /// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
148 /// to the maximum of that.
149 /// * `readfds`: File descriptors to check for read readiness
150 /// * `writefds`: File descriptors to check for write readiness
151 /// * `errorfds`: File descriptors to check for pending error conditions.
152 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
153 /// indefinitely).
154 /// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
155 /// ready (`None` to set no alternative signal mask).
156 ///
157 /// # References
158 ///
159 /// [pselect(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
160 ///
161 /// [The new pselect() system call](https://lwn.net/Articles/176911/)
162 ///
163 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
pselect<'a, N, R, W, E, T, S>(nfds: N, readfds: R, writefds: W, errorfds: E, timeout: T, sigmask: S) -> Result<c_int> where N: Into<Option<c_int>>, R: Into<Option<&'a mut FdSet>>, W: Into<Option<&'a mut FdSet>>, E: Into<Option<&'a mut FdSet>>, T: Into<Option<&'a TimeSpec>>, S: Into<Option<&'a SigSet>>,164 pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
165 readfds: R,
166 writefds: W,
167 errorfds: E,
168 timeout: T,
169 sigmask: S) -> Result<c_int>
170 where
171 N: Into<Option<c_int>>,
172 R: Into<Option<&'a mut FdSet>>,
173 W: Into<Option<&'a mut FdSet>>,
174 E: Into<Option<&'a mut FdSet>>,
175 T: Into<Option<&'a TimeSpec>>,
176 S: Into<Option<&'a SigSet>>,
177 {
178 let mut readfds = readfds.into();
179 let mut writefds = writefds.into();
180 let mut errorfds = errorfds.into();
181 let sigmask = sigmask.into();
182 let timeout = timeout.into();
183
184 let nfds = nfds.into().unwrap_or_else(|| {
185 readfds.iter_mut()
186 .chain(writefds.iter_mut())
187 .chain(errorfds.iter_mut())
188 .map(|set| set.highest().unwrap_or(-1))
189 .max()
190 .unwrap_or(-1) + 1
191 });
192
193 let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
194 let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
195 let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
196 let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
197 let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
198
199 let res = unsafe {
200 libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
201 };
202
203 Errno::result(res)
204 }
205
206
207 #[cfg(test)]
208 mod tests {
209 use super::*;
210 use std::os::unix::io::RawFd;
211 use sys::time::{TimeVal, TimeValLike};
212 use unistd::{write, pipe};
213
214 #[test]
fdset_insert()215 fn fdset_insert() {
216 let mut fd_set = FdSet::new();
217
218 for i in 0..FD_SETSIZE {
219 assert!(!fd_set.contains(i as RawFd));
220 }
221
222 fd_set.insert(7);
223
224 assert!(fd_set.contains(7));
225 }
226
227 #[test]
fdset_remove()228 fn fdset_remove() {
229 let mut fd_set = FdSet::new();
230
231 for i in 0..FD_SETSIZE {
232 assert!(!fd_set.contains(i as RawFd));
233 }
234
235 fd_set.insert(7);
236 fd_set.remove(7);
237
238 for i in 0..FD_SETSIZE {
239 assert!(!fd_set.contains(i as RawFd));
240 }
241 }
242
243 #[test]
fdset_clear()244 fn fdset_clear() {
245 let mut fd_set = FdSet::new();
246 fd_set.insert(1);
247 fd_set.insert((FD_SETSIZE / 2) as RawFd);
248 fd_set.insert((FD_SETSIZE - 1) as RawFd);
249
250 fd_set.clear();
251
252 for i in 0..FD_SETSIZE {
253 assert!(!fd_set.contains(i as RawFd));
254 }
255 }
256
257 #[test]
fdset_highest()258 fn fdset_highest() {
259 let mut set = FdSet::new();
260 assert_eq!(set.highest(), None);
261 set.insert(0);
262 assert_eq!(set.highest(), Some(0));
263 set.insert(90);
264 assert_eq!(set.highest(), Some(90));
265 set.remove(0);
266 assert_eq!(set.highest(), Some(90));
267 set.remove(90);
268 assert_eq!(set.highest(), None);
269
270 set.insert(4);
271 set.insert(5);
272 set.insert(7);
273 assert_eq!(set.highest(), Some(7));
274 }
275
276 #[test]
test_select()277 fn test_select() {
278 let (r1, w1) = pipe().unwrap();
279 write(w1, b"hi!").unwrap();
280 let (r2, _w2) = pipe().unwrap();
281
282 let mut fd_set = FdSet::new();
283 fd_set.insert(r1);
284 fd_set.insert(r2);
285
286 let mut timeout = TimeVal::seconds(10);
287 assert_eq!(1, select(None,
288 &mut fd_set,
289 None,
290 None,
291 &mut timeout).unwrap());
292 assert!(fd_set.contains(r1));
293 assert!(!fd_set.contains(r2));
294 }
295
296 #[test]
test_select_nfds()297 fn test_select_nfds() {
298 let (r1, w1) = pipe().unwrap();
299 write(w1, b"hi!").unwrap();
300 let (r2, _w2) = pipe().unwrap();
301
302 let mut fd_set = FdSet::new();
303 fd_set.insert(r1);
304 fd_set.insert(r2);
305
306 let mut timeout = TimeVal::seconds(10);
307 assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
308 &mut fd_set,
309 None,
310 None,
311 &mut timeout).unwrap());
312 assert!(fd_set.contains(r1));
313 assert!(!fd_set.contains(r2));
314 }
315
316 #[test]
test_select_nfds2()317 fn test_select_nfds2() {
318 let (r1, w1) = pipe().unwrap();
319 write(w1, b"hi!").unwrap();
320 let (r2, _w2) = pipe().unwrap();
321
322 let mut fd_set = FdSet::new();
323 fd_set.insert(r1);
324 fd_set.insert(r2);
325
326 let mut timeout = TimeVal::seconds(10);
327 assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
328 &mut fd_set,
329 None,
330 None,
331 &mut timeout).unwrap());
332 assert!(fd_set.contains(r1));
333 assert!(!fd_set.contains(r2));
334 }
335 }
336