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