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