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 // FIXME: Change to repr(transparent) once it's stable
13 #[repr(C)]
14 #[derive(Clone, Copy)]
15 #[allow(missing_debug_implementations)]
16 pub struct FdSet(libc::fd_set);
17 
18 impl FdSet {
new() -> FdSet19     pub fn new() -> FdSet {
20         let mut fdset = unsafe { mem::uninitialized() };
21         unsafe { libc::FD_ZERO(&mut fdset) };
22         FdSet(fdset)
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 /// Monitors file descriptors for readiness
74 ///
75 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
76 /// file descriptors that are ready for the given operation are set.
77 ///
78 /// When this function returns, `timeout` has an implementation-defined value.
79 ///
80 /// # Parameters
81 ///
82 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
83 ///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
84 ///   to the maximum of that.
85 /// * `readfds`: File descriptors to check for being ready to read.
86 /// * `writefds`: File descriptors to check for being ready to write.
87 /// * `errorfds`: File descriptors to check for pending error conditions.
88 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
89 ///   indefinitely).
90 ///
91 /// # References
92 ///
93 /// [select(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
94 ///
95 /// [`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>>,96 pub fn select<'a, N, R, W, E, T>(nfds: N,
97                                  readfds: R,
98                                  writefds: W,
99                                  errorfds: E,
100                                  timeout: T) -> Result<c_int>
101 where
102     N: Into<Option<c_int>>,
103     R: Into<Option<&'a mut FdSet>>,
104     W: Into<Option<&'a mut FdSet>>,
105     E: Into<Option<&'a mut FdSet>>,
106     T: Into<Option<&'a mut TimeVal>>,
107 {
108     let mut readfds = readfds.into();
109     let mut writefds = writefds.into();
110     let mut errorfds = errorfds.into();
111     let timeout = timeout.into();
112 
113     let nfds = nfds.into().unwrap_or_else(|| {
114         readfds.iter_mut()
115             .chain(writefds.iter_mut())
116             .chain(errorfds.iter_mut())
117             .map(|set| set.highest().unwrap_or(-1))
118             .max()
119             .unwrap_or(-1) + 1
120     });
121 
122     let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
123     let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
124     let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
125     let timeout = timeout.map(|tv| tv as *mut _ as *mut libc::timeval)
126                          .unwrap_or(null_mut());
127 
128     let res = unsafe {
129         libc::select(nfds, readfds, writefds, errorfds, timeout)
130     };
131 
132     Errno::result(res)
133 }
134 
135 /// Monitors file descriptors for readiness with an altered signal mask.
136 ///
137 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
138 /// file descriptors that are ready for the given operation are set.
139 ///
140 /// When this function returns, the original signal mask is restored.
141 ///
142 /// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
143 ///
144 /// # Parameters
145 ///
146 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
147 ///   is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
148 ///   to the maximum of that.
149 /// * `readfds`: File descriptors to check for read readiness
150 /// * `writefds`: File descriptors to check for write readiness
151 /// * `errorfds`: File descriptors to check for pending error conditions.
152 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
153 ///   indefinitely).
154 /// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
155 ///    ready (`None` to set no alternative signal mask).
156 ///
157 /// # References
158 ///
159 /// [pselect(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
160 ///
161 /// [The new pselect() system call](https://lwn.net/Articles/176911/)
162 ///
163 /// [`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>>,164 pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
165                                      readfds: R,
166                                      writefds: W,
167                                      errorfds: E,
168                                      timeout: T,
169                                      sigmask: S) -> Result<c_int>
170 where
171     N: Into<Option<c_int>>,
172     R: Into<Option<&'a mut FdSet>>,
173     W: Into<Option<&'a mut FdSet>>,
174     E: Into<Option<&'a mut FdSet>>,
175     T: Into<Option<&'a TimeSpec>>,
176     S: Into<Option<&'a SigSet>>,
177 {
178     let mut readfds = readfds.into();
179     let mut writefds = writefds.into();
180     let mut errorfds = errorfds.into();
181     let sigmask = sigmask.into();
182     let timeout = timeout.into();
183 
184     let nfds = nfds.into().unwrap_or_else(|| {
185         readfds.iter_mut()
186             .chain(writefds.iter_mut())
187             .chain(errorfds.iter_mut())
188             .map(|set| set.highest().unwrap_or(-1))
189             .max()
190             .unwrap_or(-1) + 1
191     });
192 
193     let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
194     let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
195     let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
196     let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
197     let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
198 
199     let res = unsafe {
200         libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
201     };
202 
203     Errno::result(res)
204 }
205 
206 
207 #[cfg(test)]
208 mod tests {
209     use super::*;
210     use std::os::unix::io::RawFd;
211     use sys::time::{TimeVal, TimeValLike};
212     use unistd::{write, pipe};
213 
214     #[test]
fdset_insert()215     fn fdset_insert() {
216         let mut fd_set = FdSet::new();
217 
218         for i in 0..FD_SETSIZE {
219             assert!(!fd_set.contains(i as RawFd));
220         }
221 
222         fd_set.insert(7);
223 
224         assert!(fd_set.contains(7));
225     }
226 
227     #[test]
fdset_remove()228     fn fdset_remove() {
229         let mut fd_set = FdSet::new();
230 
231         for i in 0..FD_SETSIZE {
232             assert!(!fd_set.contains(i as RawFd));
233         }
234 
235         fd_set.insert(7);
236         fd_set.remove(7);
237 
238         for i in 0..FD_SETSIZE {
239             assert!(!fd_set.contains(i as RawFd));
240         }
241     }
242 
243     #[test]
fdset_clear()244     fn fdset_clear() {
245         let mut fd_set = FdSet::new();
246         fd_set.insert(1);
247         fd_set.insert((FD_SETSIZE / 2) as RawFd);
248         fd_set.insert((FD_SETSIZE - 1) as RawFd);
249 
250         fd_set.clear();
251 
252         for i in 0..FD_SETSIZE {
253             assert!(!fd_set.contains(i as RawFd));
254         }
255     }
256 
257     #[test]
fdset_highest()258     fn fdset_highest() {
259         let mut set = FdSet::new();
260         assert_eq!(set.highest(), None);
261         set.insert(0);
262         assert_eq!(set.highest(), Some(0));
263         set.insert(90);
264         assert_eq!(set.highest(), Some(90));
265         set.remove(0);
266         assert_eq!(set.highest(), Some(90));
267         set.remove(90);
268         assert_eq!(set.highest(), None);
269 
270         set.insert(4);
271         set.insert(5);
272         set.insert(7);
273         assert_eq!(set.highest(), Some(7));
274     }
275 
276     #[test]
test_select()277     fn test_select() {
278         let (r1, w1) = pipe().unwrap();
279         write(w1, b"hi!").unwrap();
280         let (r2, _w2) = pipe().unwrap();
281 
282         let mut fd_set = FdSet::new();
283         fd_set.insert(r1);
284         fd_set.insert(r2);
285 
286         let mut timeout = TimeVal::seconds(10);
287         assert_eq!(1, select(None,
288                              &mut fd_set,
289                              None,
290                              None,
291                              &mut timeout).unwrap());
292         assert!(fd_set.contains(r1));
293         assert!(!fd_set.contains(r2));
294     }
295 
296     #[test]
test_select_nfds()297     fn test_select_nfds() {
298         let (r1, w1) = pipe().unwrap();
299         write(w1, b"hi!").unwrap();
300         let (r2, _w2) = pipe().unwrap();
301 
302         let mut fd_set = FdSet::new();
303         fd_set.insert(r1);
304         fd_set.insert(r2);
305 
306         let mut timeout = TimeVal::seconds(10);
307         assert_eq!(1, select(Some(fd_set.highest().unwrap() + 1),
308                              &mut fd_set,
309                              None,
310                              None,
311                              &mut timeout).unwrap());
312         assert!(fd_set.contains(r1));
313         assert!(!fd_set.contains(r2));
314     }
315 
316     #[test]
test_select_nfds2()317     fn test_select_nfds2() {
318         let (r1, w1) = pipe().unwrap();
319         write(w1, b"hi!").unwrap();
320         let (r2, _w2) = pipe().unwrap();
321 
322         let mut fd_set = FdSet::new();
323         fd_set.insert(r1);
324         fd_set.insert(r2);
325 
326         let mut timeout = TimeVal::seconds(10);
327         assert_eq!(1, select(::std::cmp::max(r1, r2) + 1,
328                              &mut fd_set,
329                              None,
330                              None,
331                              &mut timeout).unwrap());
332         assert!(fd_set.contains(r1));
333         assert!(!fd_set.contains(r2));
334     }
335 }
336