1//! Portably monitor a group of file descriptors for readiness.
2use crate::errno::Errno;
3use crate::sys::time::{TimeSpec, TimeVal};
4use crate::Result;
5use libc::{self, c_int};
6use std::convert::TryFrom;
7use std::iter::FusedIterator;
8use std::mem;
9use std::ops::Range;
10use std::os::unix::io::RawFd;
11use std::ptr::{null, null_mut};
12
13pub 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)]
18pub struct FdSet(libc::fd_set);
19
20fn 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
27impl FdSet {
28 /// Create an empty `FdSet`
29 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`
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`
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.
50 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`.
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
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]
99 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
107impl Default for FdSet {
108 fn default() -> Self {
109 Self::new()
110 }
111}
112
113/// Iterator over `FdSet`.
114#[derive(Debug)]
115pub struct Fds<'a> {
116 set: &'a FdSet,
117 range: Range<usize>,
118}
119
120impl<'a> Iterator for Fds<'a> {
121 type Item = RawFd;
122
123 fn next(&mut self) -> Option<RawFd> {
124 for i: usize in &mut self.range {
125 if self.set.contains(fd:i as RawFd) {
126 return Some(i as RawFd);
127 }
128 }
129 None
130 }
131
132 #[inline]
133 fn size_hint(&self) -> (usize, Option<usize>) {
134 let (_, upper: Option) = self.range.size_hint();
135 (0, upper)
136 }
137}
138
139impl<'a> DoubleEndedIterator for Fds<'a> {
140 #[inline]
141 fn next_back(&mut self) -> Option<RawFd> {
142 while let Some(i: usize) = self.range.next_back() {
143 if self.set.contains(fd:i as RawFd) {
144 return Some(i as RawFd);
145 }
146 }
147 None
148 }
149}
150
151impl<'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
176pub 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>
183where
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
225feature! {
226#![feature = "signal"]
227
228use 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
259pub 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>
265where
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)]
303mod 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]
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]
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]
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]
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]
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]
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]
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]
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