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