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::{AsRawFd, BorrowedFd, 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<'fd> {
19     set: libc::fd_set,
20     _fd: std::marker::PhantomData<BorrowedFd<'fd>>,
21 }
22 
assert_fd_valid(fd: RawFd)23 fn assert_fd_valid(fd: RawFd) {
24     assert!(
25         usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
26         "fd must be in the range 0..FD_SETSIZE",
27     );
28 }
29 
30 impl<'fd> FdSet<'fd> {
31     /// Create an empty `FdSet`
new() -> FdSet<'fd>32     pub fn new() -> FdSet<'fd> {
33         let mut fdset = mem::MaybeUninit::uninit();
34         unsafe {
35             libc::FD_ZERO(fdset.as_mut_ptr());
36             Self {
37                 set: fdset.assume_init(),
38                 _fd: std::marker::PhantomData,
39             }
40         }
41     }
42 
43     /// Add a file descriptor to an `FdSet`
insert(&mut self, fd: BorrowedFd<'fd>)44     pub fn insert(&mut self, fd: BorrowedFd<'fd>) {
45         assert_fd_valid(fd.as_raw_fd());
46         unsafe { libc::FD_SET(fd.as_raw_fd(), &mut self.set) };
47     }
48 
49     /// Remove a file descriptor from an `FdSet`
remove(&mut self, fd: BorrowedFd<'fd>)50     pub fn remove(&mut self, fd: BorrowedFd<'fd>) {
51         assert_fd_valid(fd.as_raw_fd());
52         unsafe { libc::FD_CLR(fd.as_raw_fd(), &mut self.set) };
53     }
54 
55     /// Test an `FdSet` for the presence of a certain file descriptor.
contains(&self, fd: BorrowedFd<'fd>) -> bool56     pub fn contains(&self, fd: BorrowedFd<'fd>) -> bool {
57         assert_fd_valid(fd.as_raw_fd());
58         unsafe { libc::FD_ISSET(fd.as_raw_fd(), &self.set) }
59     }
60 
61     /// Remove all file descriptors from this `FdSet`.
clear(&mut self)62     pub fn clear(&mut self) {
63         unsafe { libc::FD_ZERO(&mut self.set) };
64     }
65 
66     /// Finds the highest file descriptor in the set.
67     ///
68     /// Returns `None` if the set is empty.
69     ///
70     /// This can be used to calculate the `nfds` parameter of the [`select`] function.
71     ///
72     /// # Example
73     ///
74     /// ```
75     /// # use std::os::unix::io::{AsRawFd, BorrowedFd};
76     /// # use nix::sys::select::FdSet;
77     /// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
78     /// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
79     /// let mut set = FdSet::new();
80     /// set.insert(fd_four);
81     /// set.insert(fd_nine);
82     /// assert_eq!(set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()), Some(9));
83     /// ```
84     ///
85     /// [`select`]: fn.select.html
highest(&self) -> Option<BorrowedFd<'_>>86     pub fn highest(&self) -> Option<BorrowedFd<'_>> {
87         self.fds(None).next_back()
88     }
89 
90     /// Returns an iterator over the file descriptors in the set.
91     ///
92     /// For performance, it takes an optional higher bound: the iterator will
93     /// not return any elements of the set greater than the given file
94     /// descriptor.
95     ///
96     /// # Examples
97     ///
98     /// ```
99     /// # use nix::sys::select::FdSet;
100     /// # use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
101     /// let mut set = FdSet::new();
102     /// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
103     /// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
104     /// set.insert(fd_four);
105     /// set.insert(fd_nine);
106     /// let fds: Vec<RawFd> = set.fds(None).map(|borrowed_fd|borrowed_fd.as_raw_fd()).collect();
107     /// assert_eq!(fds, vec![4, 9]);
108     /// ```
109     #[inline]
fds(&self, highest: Option<RawFd>) -> Fds110     pub fn fds(&self, highest: Option<RawFd>) -> Fds {
111         Fds {
112             set: self,
113             range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
114         }
115     }
116 }
117 
118 impl<'fd> Default for FdSet<'fd> {
default() -> Self119     fn default() -> Self {
120         Self::new()
121     }
122 }
123 
124 /// Iterator over `FdSet`.
125 #[derive(Debug)]
126 pub struct Fds<'a, 'fd> {
127     set: &'a FdSet<'fd>,
128     range: Range<usize>,
129 }
130 
131 impl<'a, 'fd> Iterator for Fds<'a, 'fd> {
132     type Item = BorrowedFd<'fd>;
133 
next(&mut self) -> Option<Self::Item>134     fn next(&mut self) -> Option<Self::Item> {
135         for i in &mut self.range {
136             let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
137             if self.set.contains(borrowed_i) {
138                 return Some(borrowed_i);
139             }
140         }
141         None
142     }
143 
144     #[inline]
size_hint(&self) -> (usize, Option<usize>)145     fn size_hint(&self) -> (usize, Option<usize>) {
146         let (_, upper) = self.range.size_hint();
147         (0, upper)
148     }
149 }
150 
151 impl<'a, 'fd> DoubleEndedIterator for Fds<'a, 'fd> {
152     #[inline]
next_back(&mut self) -> Option<BorrowedFd<'fd>>153     fn next_back(&mut self) -> Option<BorrowedFd<'fd>> {
154         while let Some(i) = self.range.next_back() {
155             let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
156             if self.set.contains(borrowed_i) {
157                 return Some(borrowed_i);
158             }
159         }
160         None
161     }
162 }
163 
164 impl<'a, 'fd> FusedIterator for Fds<'a, 'fd> {}
165 
166 /// Monitors file descriptors for readiness
167 ///
168 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
169 /// file descriptors that are ready for the given operation are set.
170 ///
171 /// When this function returns, `timeout` has an implementation-defined value.
172 ///
173 /// # Parameters
174 ///
175 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
176 ///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
177 ///   to the maximum of that.
178 /// * `readfds`: File descriptors to check for being ready to read.
179 /// * `writefds`: File descriptors to check for being ready to write.
180 /// * `errorfds`: File descriptors to check for pending error conditions.
181 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
182 ///   indefinitely).
183 ///
184 /// # References
185 ///
186 /// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
187 ///
188 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
select<'a, 'fd, N, R, W, E, T>( nfds: N, readfds: R, writefds: W, errorfds: E, timeout: T, ) -> Result<c_int> where 'fd: 'a, N: Into<Option<c_int>>, R: Into<Option<&'a mut FdSet<'fd>>>, W: Into<Option<&'a mut FdSet<'fd>>>, E: Into<Option<&'a mut FdSet<'fd>>>, T: Into<Option<&'a mut TimeVal>>,189 pub fn select<'a, 'fd, N, R, W, E, T>(
190     nfds: N,
191     readfds: R,
192     writefds: W,
193     errorfds: E,
194     timeout: T,
195 ) -> Result<c_int>
196 where
197     'fd: 'a,
198     N: Into<Option<c_int>>,
199     R: Into<Option<&'a mut FdSet<'fd>>>,
200     W: Into<Option<&'a mut FdSet<'fd>>>,
201     E: Into<Option<&'a mut FdSet<'fd>>>,
202     T: Into<Option<&'a mut TimeVal>>,
203 {
204     let mut readfds = readfds.into();
205     let mut writefds = writefds.into();
206     let mut errorfds = errorfds.into();
207     let timeout = timeout.into();
208 
209     let nfds = nfds.into().unwrap_or_else(|| {
210         readfds
211             .iter_mut()
212             .chain(writefds.iter_mut())
213             .chain(errorfds.iter_mut())
214             .map(|set| {
215                 set.highest()
216                     .map(|borrowed_fd| borrowed_fd.as_raw_fd())
217                     .unwrap_or(-1)
218             })
219             .max()
220             .unwrap_or(-1)
221             + 1
222     });
223 
224     let readfds = readfds
225         .map(|set| set as *mut _ as *mut libc::fd_set)
226         .unwrap_or(null_mut());
227     let writefds = writefds
228         .map(|set| set as *mut _ as *mut libc::fd_set)
229         .unwrap_or(null_mut());
230     let errorfds = errorfds
231         .map(|set| set as *mut _ as *mut libc::fd_set)
232         .unwrap_or(null_mut());
233     let timeout = timeout
234         .map(|tv| tv as *mut _ as *mut libc::timeval)
235         .unwrap_or(null_mut());
236 
237     let res =
238         unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
239 
240     Errno::result(res)
241 }
242 
243 feature! {
244 #![feature = "signal"]
245 
246 use crate::sys::signal::SigSet;
247 
248 /// Monitors file descriptors for readiness with an altered signal mask.
249 ///
250 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
251 /// file descriptors that are ready for the given operation are set.
252 ///
253 /// When this function returns, the original signal mask is restored.
254 ///
255 /// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
256 ///
257 /// # Parameters
258 ///
259 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
260 ///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
261 ///   to the maximum of that.
262 /// * `readfds`: File descriptors to check for read readiness
263 /// * `writefds`: File descriptors to check for write readiness
264 /// * `errorfds`: File descriptors to check for pending error conditions.
265 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
266 ///   indefinitely).
267 /// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
268 ///    ready (`None` to set no alternative signal mask).
269 ///
270 /// # References
271 ///
272 /// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
273 ///
274 /// [The new pselect() system call](https://lwn.net/Articles/176911/)
275 ///
276 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
277 pub fn pselect<'a, 'fd, N, R, W, E, T, S>(nfds: N,
278     readfds: R,
279     writefds: W,
280     errorfds: E,
281     timeout: T,
282                                      sigmask: S) -> Result<c_int>
283 where
284     'fd: 'a,
285     N: Into<Option<c_int>>,
286     R: Into<Option<&'a mut FdSet<'fd>>>,
287     W: Into<Option<&'a mut FdSet<'fd>>>,
288     E: Into<Option<&'a mut FdSet<'fd>>>,
289     T: Into<Option<&'a TimeSpec>>,
290     S: Into<Option<&'a SigSet>>,
291 {
292     let mut readfds = readfds.into();
293     let mut writefds = writefds.into();
294     let mut errorfds = errorfds.into();
295     let sigmask = sigmask.into();
296     let timeout = timeout.into();
297 
298     let nfds = nfds.into().unwrap_or_else(|| {
299         readfds.iter_mut()
300             .chain(writefds.iter_mut())
301             .chain(errorfds.iter_mut())
302             .map(|set| set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()).unwrap_or(-1))
303             .max()
304             .unwrap_or(-1) + 1
305     });
306 
307     let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
308     let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
309     let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
310     let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
311     let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
312 
313     let res = unsafe {
314         libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
315     };
316 
317     Errno::result(res)
318 }
319 }
320