blob: a576c7e4929c4b0ba46842e590e61aeafffd91e6 [file] [log] [blame]
Andrew Walbran12f61402020-10-14 11:10:53 +01001use std::iter::FusedIterator;
2use std::mem;
3use std::ops::Range;
4use std::os::unix::io::RawFd;
5use std::ptr::{null, null_mut};
6use libc::{self, c_int};
7use crate::Result;
8use crate::errno::Errno;
9use crate::sys::signal::SigSet;
10use crate::sys::time::{TimeSpec, TimeVal};
11
12pub use libc::FD_SETSIZE;
13
14#[repr(transparent)]
15#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
16pub struct FdSet(libc::fd_set);
17
18impl FdSet {
19 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
27 pub fn insert(&mut self, fd: RawFd) {
28 unsafe { libc::FD_SET(fd, &mut self.0) };
29 }
30
31 pub fn remove(&mut self, fd: RawFd) {
32 unsafe { libc::FD_CLR(fd, &mut self.0) };
33 }
34
35 pub fn contains(&mut self, fd: RawFd) -> bool {
36 unsafe { libc::FD_ISSET(fd, &mut self.0) }
37 }
38
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
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]
84 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
92impl Default for FdSet {
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98/// Iterator over `FdSet`.
99#[derive(Debug)]
100pub struct Fds<'a> {
101 set: &'a mut FdSet,
102 range: Range<usize>,
103}
104
105impl<'a> Iterator for Fds<'a> {
106 type Item = RawFd;
107
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]
118 fn size_hint(&self) -> (usize, Option<usize>) {
119 let (_, upper) = self.range.size_hint();
120 (0, upper)
121 }
122}
123
124impl<'a> DoubleEndedIterator for Fds<'a> {
125 #[inline]
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
136impl<'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
161pub 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>
166where
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
229pub 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>
235where
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)]
273mod 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]
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]
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]
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]
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]
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]
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]
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]
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}