1 use std::iter::FusedIterator;
2 use std::mem;
3 use std::ops::Range;
4 use std::os::unix::io::RawFd;
5 use std::ptr::{null, null_mut};
6 use libc::{self, c_int};
7 use crate::Result;
8 use crate::errno::Errno;
9 use crate::sys::signal::SigSet;
10 use crate::sys::time::{TimeSpec, TimeVal};
11 
12 pub use libc::FD_SETSIZE;
13 
14 #[repr(transparent)]
15 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
16 pub struct FdSet(libc::fd_set);
17 
18 impl FdSet {
new() -> FdSet19     pub fn new() -> FdSet {
20         let mut fdset = mem::MaybeUninit::uninit();
21         unsafe {
22             libc::FD_ZERO(fdset.as_mut_ptr());
23             FdSet(fdset.assume_init())
24         }
25     }
26 
insert(&mut self, fd: RawFd)27     pub fn insert(&mut self, fd: RawFd) {
28         unsafe { libc::FD_SET(fd, &mut self.0) };
29     }
30 
remove(&mut self, fd: RawFd)31     pub fn remove(&mut self, fd: RawFd) {
32         unsafe { libc::FD_CLR(fd, &mut self.0) };
33     }
34 
contains(&mut self, fd: RawFd) -> bool35     pub fn contains(&mut self, fd: RawFd) -> bool {
36         unsafe { libc::FD_ISSET(fd, &mut self.0) }
37     }
38 
clear(&mut self)39     pub fn clear(&mut self) {
40         unsafe { libc::FD_ZERO(&mut self.0) };
41     }
42 
43     /// Finds the highest file descriptor in the set.
44     ///
45     /// Returns `None` if the set is empty.
46     ///
47     /// This can be used to calculate the `nfds` parameter of the [`select`] function.
48     ///
49     /// # Example
50     ///
51     /// ```
52     /// # use nix::sys::select::FdSet;
53     /// # fn main() {
54     /// let mut set = FdSet::new();
55     /// set.insert(4);
56     /// set.insert(9);
57     /// assert_eq!(set.highest(), Some(9));
58     /// # }
59     /// ```
60     ///
61     /// [`select`]: fn.select.html
highest(&mut self) -> Option<RawFd>62     pub fn highest(&mut self) -> Option<RawFd> {
63         self.fds(None).next_back()
64     }
65 
66     /// Returns an iterator over the file descriptors in the set.
67     ///
68     /// For performance, it takes an optional higher bound: the iterator will
69     /// not return any elements of the set greater than the given file
70     /// descriptor.
71     ///
72     /// # Examples
73     ///
74     /// ```
75     /// # use nix::sys::select::FdSet;
76     /// # use std::os::unix::io::RawFd;
77     /// let mut set = FdSet::new();
78     /// set.insert(4);
79     /// set.insert(9);
80     /// let fds: Vec<RawFd> = set.fds(None).collect();
81     /// assert_eq!(fds, vec![4, 9]);
82     /// ```
83     #[inline]
fds(&mut self, highest: Option<RawFd>) -> Fds84     pub fn fds(&mut self, highest: Option<RawFd>) -> Fds {
85         Fds {
86             set: self,
87             range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
88         }
89     }
90 }
91 
92 impl Default for FdSet {
default() -> Self93     fn default() -> Self {
94         Self::new()
95     }
96 }
97 
98 /// Iterator over `FdSet`.
99 #[derive(Debug)]
100 pub struct Fds<'a> {
101     set: &'a mut FdSet,
102     range: Range<usize>,
103 }
104 
105 impl<'a> Iterator for Fds<'a> {
106     type Item = RawFd;
107 
next(&mut self) -> Option<RawFd>108     fn next(&mut self) -> Option<RawFd> {
109         while let Some(i) = self.range.next() {
110             if self.set.contains(i as RawFd) {
111                 return Some(i as RawFd);
112             }
113         }
114         None
115     }
116 
117     #[inline]
size_hint(&self) -> (usize, Option<usize>)118     fn size_hint(&self) -> (usize, Option<usize>) {
119         let (_, upper) = self.range.size_hint();
120         (0, upper)
121     }
122 }
123 
124 impl<'a> DoubleEndedIterator for Fds<'a> {
125     #[inline]
next_back(&mut self) -> Option<RawFd>126     fn next_back(&mut self) -> Option<RawFd> {
127         while let Some(i) = self.range.next_back() {
128             if self.set.contains(i as RawFd) {
129                 return Some(i as RawFd);
130             }
131         }
132         None
133     }
134 }
135 
136 impl<'a> FusedIterator for Fds<'a> {}
137 
138 /// Monitors file descriptors for readiness
139 ///
140 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
141 /// file descriptors that are ready for the given operation are set.
142 ///
143 /// When this function returns, `timeout` has an implementation-defined value.
144 ///
145 /// # Parameters
146 ///
147 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
148 ///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
149 ///   to the maximum of that.
150 /// * `readfds`: File descriptors to check for being ready to read.
151 /// * `writefds`: File descriptors to check for being ready to write.
152 /// * `errorfds`: File descriptors to check for pending error conditions.
153 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
154 ///   indefinitely).
155 ///
156 /// # References
157 ///
158 /// [select(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
159 ///
160 /// [`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>>,161 pub fn select<'a, N, R, W, E, T>(nfds: N,
162     readfds: R,
163     writefds: W,
164     errorfds: E,
165                                  timeout: T) -> Result<c_int>
166 where
167     N: Into<Option<c_int>>,
168     R: Into<Option<&'a mut FdSet>>,
169     W: Into<Option<&'a mut FdSet>>,
170     E: Into<Option<&'a mut FdSet>>,
171     T: Into<Option<&'a mut TimeVal>>,
172 {
173     let mut readfds = readfds.into();
174     let mut writefds = writefds.into();
175     let mut errorfds = errorfds.into();
176     let timeout = timeout.into();
177 
178     let nfds = nfds.into().unwrap_or_else(|| {
179         readfds.iter_mut()
180             .chain(writefds.iter_mut())
181             .chain(errorfds.iter_mut())
182             .map(|set| set.highest().unwrap_or(-1))
183             .max()
184             .unwrap_or(-1) + 1
185     });
186 
187     let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
188     let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
189     let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
190     let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval)
191         .unwrap_or(null_mut());
192 
193     let res = unsafe {
194         libc::select(nfds, readfds, writefds, errorfds, timeout)
195     };
196 
197     Errno::result(res)
198 }
199 
200 /// Monitors file descriptors for readiness with an altered signal mask.
201 ///
202 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
203 /// file descriptors that are ready for the given operation are set.
204 ///
205 /// When this function returns, the original signal mask is restored.
206 ///
207 /// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
208 ///
209 /// # Parameters
210 ///
211 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
212 ///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
213 ///   to the maximum of that.
214 /// * `readfds`: File descriptors to check for read readiness
215 /// * `writefds`: File descriptors to check for write readiness
216 /// * `errorfds`: File descriptors to check for pending error conditions.
217 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
218 ///   indefinitely).
219 /// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
220 ///    ready (`None` to set no alternative signal mask).
221 ///
222 /// # References
223 ///
224 /// [pselect(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
225 ///
226 /// [The new pselect() system call](https://lwn.net/Articles/176911/)
227 ///
228 /// [`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>>,229 pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
230     readfds: R,
231     writefds: W,
232     errorfds: E,
233     timeout: T,
234                                      sigmask: S) -> Result<c_int>
235 where
236     N: Into<Option<c_int>>,
237     R: Into<Option<&'a mut FdSet>>,
238     W: Into<Option<&'a mut FdSet>>,
239     E: Into<Option<&'a mut FdSet>>,
240     T: Into<Option<&'a TimeSpec>>,
241     S: Into<Option<&'a SigSet>>,
242 {
243     let mut readfds = readfds.into();
244     let mut writefds = writefds.into();
245     let mut errorfds = errorfds.into();
246     let sigmask = sigmask.into();
247     let timeout = timeout.into();
248 
249     let nfds = nfds.into().unwrap_or_else(|| {
250         readfds.iter_mut()
251             .chain(writefds.iter_mut())
252             .chain(errorfds.iter_mut())
253             .map(|set| set.highest().unwrap_or(-1))
254             .max()
255             .unwrap_or(-1) + 1
256     });
257 
258     let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
259     let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
260     let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
261     let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
262     let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
263 
264     let res = unsafe {
265         libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
266     };
267 
268     Errno::result(res)
269 }
270 
271 
272 #[cfg(test)]
273 mod tests {
274     use super::*;
275     use std::os::unix::io::RawFd;
276     use crate::sys::time::{TimeVal, TimeValLike};
277     use crate::unistd::{write, pipe};
278 
279     #[test]
fdset_insert()280     fn fdset_insert() {
281         let mut fd_set = FdSet::new();
282 
283         for i in 0..FD_SETSIZE {
284             assert!(!fd_set.contains(i as RawFd));
285         }
286 
287         fd_set.insert(7);
288 
289         assert!(fd_set.contains(7));
290     }
291 
292     #[test]
fdset_remove()293     fn fdset_remove() {
294         let mut fd_set = FdSet::new();
295 
296         for i in 0..FD_SETSIZE {
297             assert!(!fd_set.contains(i as RawFd));
298         }
299 
300         fd_set.insert(7);
301         fd_set.remove(7);
302 
303         for i in 0..FD_SETSIZE {
304             assert!(!fd_set.contains(i as RawFd));
305         }
306     }
307 
308     #[test]
fdset_clear()309     fn fdset_clear() {
310         let mut fd_set = FdSet::new();
311         fd_set.insert(1);
312         fd_set.insert((FD_SETSIZE / 2) as RawFd);
313         fd_set.insert((FD_SETSIZE - 1) as RawFd);
314 
315         fd_set.clear();
316 
317         for i in 0..FD_SETSIZE {
318             assert!(!fd_set.contains(i as RawFd));
319         }
320     }
321 
322     #[test]
fdset_highest()323     fn fdset_highest() {
324         let mut set = FdSet::new();
325         assert_eq!(set.highest(), None);
326         set.insert(0);
327         assert_eq!(set.highest(), Some(0));
328         set.insert(90);
329         assert_eq!(set.highest(), Some(90));
330         set.remove(0);
331         assert_eq!(set.highest(), Some(90));
332         set.remove(90);
333         assert_eq!(set.highest(), None);
334 
335         set.insert(4);
336         set.insert(5);
337         set.insert(7);
338         assert_eq!(set.highest(), Some(7));
339     }
340 
341     #[test]
fdset_fds()342     fn fdset_fds() {
343         let mut set = FdSet::new();
344         assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![]);
345         set.insert(0);
346         assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0]);
347         set.insert(90);
348         assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0, 90]);
349 
350         // highest limit
351         assert_eq!(set.fds(Some(89)).collect::<Vec<_>>(), vec![0]);
352         assert_eq!(set.fds(Some(90)).collect::<Vec<_>>(), vec![0, 90]);
353     }
354 
355     #[test]
test_select()356     fn test_select() {
357         let (r1, w1) = pipe().unwrap();
358         write(w1, b"hi!").unwrap();
359         let (r2, _w2) = pipe().unwrap();
360 
361         let mut fd_set = FdSet::new();
362         fd_set.insert(r1);
363         fd_set.insert(r2);
364 
365         let mut timeout = TimeVal::seconds(10);
366         assert_eq!(1, select(None,
367                              &mut fd_set,
368                              None,
369                              None,
370                              &mut timeout).unwrap());
371         assert!(fd_set.contains(r1));
372         assert!(!fd_set.contains(r2));
373     }
374 
375     #[test]
test_select_nfds()376     fn test_select_nfds() {
377         let (r1, w1) = pipe().unwrap();
378         write(w1, b"hi!").unwrap();
379         let (r2, _w2) = pipe().unwrap();
380 
381         let mut fd_set = FdSet::new();
382         fd_set.insert(r1);
383         fd_set.insert(r2);
384 
385         let mut timeout = TimeVal::seconds(10);
386         assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
387                 &mut fd_set,
388                 None,
389                 None,
390                              &mut timeout).unwrap());
391         assert!(fd_set.contains(r1));
392         assert!(!fd_set.contains(r2));
393     }
394 
395     #[test]
test_select_nfds2()396     fn test_select_nfds2() {
397         let (r1, w1) = pipe().unwrap();
398         write(w1, b"hi!").unwrap();
399         let (r2, _w2) = pipe().unwrap();
400 
401         let mut fd_set = FdSet::new();
402         fd_set.insert(r1);
403         fd_set.insert(r2);
404 
405         let mut timeout = TimeVal::seconds(10);
406         assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
407                 &mut fd_set,
408                 None,
409                 None,
410                              &mut timeout).unwrap());
411         assert!(fd_set.contains(r1));
412         assert!(!fd_set.contains(r2));
413     }
414 }
415