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