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