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