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