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