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::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(libc::fd_set); |
19 | |
20 | fn 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 | |
27 | impl 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 | |
107 | impl Default for FdSet { |
108 | fn default() -> Self { |
109 | Self::new() |
110 | } |
111 | } |
112 | |
113 | /// Iterator over `FdSet`. |
114 | #[derive (Debug)] |
115 | pub struct Fds<'a> { |
116 | set: &'a FdSet, |
117 | range: Range<usize>, |
118 | } |
119 | |
120 | impl<'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 | |
139 | impl<'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 | |
151 | impl<'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 |
176 | pub 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> |
183 | where |
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 | |
225 | feature! { |
226 | #![feature = "signal" ] |
227 | |
228 | use 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 |
259 | pub 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> |
265 | where |
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)] |
303 | mod 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 | |