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