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