1 //! Portably monitor a group of file descriptors for readiness.
2 use crate::errno::Errno;
3 use crate::sys::time::{TimeSpec, TimeVal};
4 use crate::Result;
5 use libc::{self, c_int};
6 use std::convert::TryFrom;
7 use std::iter::FusedIterator;
8 use std::mem;
9 use std::ops::Range;
10 use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
11 use std::ptr::{null, null_mut};
12
13 pub use libc::FD_SETSIZE;
14
15 /// Contains a set of file descriptors used by [`select`]
16 #[repr(transparent)]
17 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
18 pub struct FdSet<'fd> {
19 set: libc::fd_set,
20 _fd: std::marker::PhantomData<BorrowedFd<'fd>>,
21 }
22
assert_fd_valid(fd: RawFd)23 fn assert_fd_valid(fd: RawFd) {
24 assert!(
25 usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
26 "fd must be in the range 0..FD_SETSIZE",
27 );
28 }
29
30 impl<'fd> FdSet<'fd> {
31 /// Create an empty `FdSet`
new() -> FdSet<'fd>32 pub fn new() -> FdSet<'fd> {
33 let mut fdset = mem::MaybeUninit::uninit();
34 unsafe {
35 libc::FD_ZERO(fdset.as_mut_ptr());
36 Self {
37 set: fdset.assume_init(),
38 _fd: std::marker::PhantomData,
39 }
40 }
41 }
42
43 /// Add a file descriptor to an `FdSet`
insert(&mut self, fd: BorrowedFd<'fd>)44 pub fn insert(&mut self, fd: BorrowedFd<'fd>) {
45 assert_fd_valid(fd.as_raw_fd());
46 unsafe { libc::FD_SET(fd.as_raw_fd(), &mut self.set) };
47 }
48
49 /// Remove a file descriptor from an `FdSet`
remove(&mut self, fd: BorrowedFd<'fd>)50 pub fn remove(&mut self, fd: BorrowedFd<'fd>) {
51 assert_fd_valid(fd.as_raw_fd());
52 unsafe { libc::FD_CLR(fd.as_raw_fd(), &mut self.set) };
53 }
54
55 /// Test an `FdSet` for the presence of a certain file descriptor.
contains(&self, fd: BorrowedFd<'fd>) -> bool56 pub fn contains(&self, fd: BorrowedFd<'fd>) -> bool {
57 assert_fd_valid(fd.as_raw_fd());
58 unsafe { libc::FD_ISSET(fd.as_raw_fd(), &self.set) }
59 }
60
61 /// Remove all file descriptors from this `FdSet`.
clear(&mut self)62 pub fn clear(&mut self) {
63 unsafe { libc::FD_ZERO(&mut self.set) };
64 }
65
66 /// Finds the highest file descriptor in the set.
67 ///
68 /// Returns `None` if the set is empty.
69 ///
70 /// This can be used to calculate the `nfds` parameter of the [`select`] function.
71 ///
72 /// # Example
73 ///
74 /// ```
75 /// # use std::os::unix::io::{AsRawFd, BorrowedFd};
76 /// # use nix::sys::select::FdSet;
77 /// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
78 /// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
79 /// let mut set = FdSet::new();
80 /// set.insert(fd_four);
81 /// set.insert(fd_nine);
82 /// assert_eq!(set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()), Some(9));
83 /// ```
84 ///
85 /// [`select`]: fn.select.html
highest(&self) -> Option<BorrowedFd<'_>>86 pub fn highest(&self) -> Option<BorrowedFd<'_>> {
87 self.fds(None).next_back()
88 }
89
90 /// Returns an iterator over the file descriptors in the set.
91 ///
92 /// For performance, it takes an optional higher bound: the iterator will
93 /// not return any elements of the set greater than the given file
94 /// descriptor.
95 ///
96 /// # Examples
97 ///
98 /// ```
99 /// # use nix::sys::select::FdSet;
100 /// # use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd};
101 /// let mut set = FdSet::new();
102 /// let fd_four = unsafe {BorrowedFd::borrow_raw(4)};
103 /// let fd_nine = unsafe {BorrowedFd::borrow_raw(9)};
104 /// set.insert(fd_four);
105 /// set.insert(fd_nine);
106 /// let fds: Vec<RawFd> = set.fds(None).map(|borrowed_fd|borrowed_fd.as_raw_fd()).collect();
107 /// assert_eq!(fds, vec![4, 9]);
108 /// ```
109 #[inline]
fds(&self, highest: Option<RawFd>) -> Fds110 pub fn fds(&self, highest: Option<RawFd>) -> Fds {
111 Fds {
112 set: self,
113 range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
114 }
115 }
116 }
117
118 impl<'fd> Default for FdSet<'fd> {
default() -> Self119 fn default() -> Self {
120 Self::new()
121 }
122 }
123
124 /// Iterator over `FdSet`.
125 #[derive(Debug)]
126 pub struct Fds<'a, 'fd> {
127 set: &'a FdSet<'fd>,
128 range: Range<usize>,
129 }
130
131 impl<'a, 'fd> Iterator for Fds<'a, 'fd> {
132 type Item = BorrowedFd<'fd>;
133
next(&mut self) -> Option<Self::Item>134 fn next(&mut self) -> Option<Self::Item> {
135 for i in &mut self.range {
136 let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
137 if self.set.contains(borrowed_i) {
138 return Some(borrowed_i);
139 }
140 }
141 None
142 }
143
144 #[inline]
size_hint(&self) -> (usize, Option<usize>)145 fn size_hint(&self) -> (usize, Option<usize>) {
146 let (_, upper) = self.range.size_hint();
147 (0, upper)
148 }
149 }
150
151 impl<'a, 'fd> DoubleEndedIterator for Fds<'a, 'fd> {
152 #[inline]
next_back(&mut self) -> Option<BorrowedFd<'fd>>153 fn next_back(&mut self) -> Option<BorrowedFd<'fd>> {
154 while let Some(i) = self.range.next_back() {
155 let borrowed_i = unsafe { BorrowedFd::borrow_raw(i as RawFd) };
156 if self.set.contains(borrowed_i) {
157 return Some(borrowed_i);
158 }
159 }
160 None
161 }
162 }
163
164 impl<'a, 'fd> FusedIterator for Fds<'a, 'fd> {}
165
166 /// Monitors file descriptors for readiness
167 ///
168 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
169 /// file descriptors that are ready for the given operation are set.
170 ///
171 /// When this function returns, `timeout` has an implementation-defined value.
172 ///
173 /// # Parameters
174 ///
175 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
176 /// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
177 /// to the maximum of that.
178 /// * `readfds`: File descriptors to check for being ready to read.
179 /// * `writefds`: File descriptors to check for being ready to write.
180 /// * `errorfds`: File descriptors to check for pending error conditions.
181 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
182 /// indefinitely).
183 ///
184 /// # References
185 ///
186 /// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
187 ///
188 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
select<'a, 'fd, N, R, W, E, T>( nfds: N, readfds: R, writefds: W, errorfds: E, timeout: T, ) -> Result<c_int> where 'fd: 'a, N: Into<Option<c_int>>, R: Into<Option<&'a mut FdSet<'fd>>>, W: Into<Option<&'a mut FdSet<'fd>>>, E: Into<Option<&'a mut FdSet<'fd>>>, T: Into<Option<&'a mut TimeVal>>,189 pub fn select<'a, 'fd, N, R, W, E, T>(
190 nfds: N,
191 readfds: R,
192 writefds: W,
193 errorfds: E,
194 timeout: T,
195 ) -> Result<c_int>
196 where
197 'fd: 'a,
198 N: Into<Option<c_int>>,
199 R: Into<Option<&'a mut FdSet<'fd>>>,
200 W: Into<Option<&'a mut FdSet<'fd>>>,
201 E: Into<Option<&'a mut FdSet<'fd>>>,
202 T: Into<Option<&'a mut TimeVal>>,
203 {
204 let mut readfds = readfds.into();
205 let mut writefds = writefds.into();
206 let mut errorfds = errorfds.into();
207 let timeout = timeout.into();
208
209 let nfds = nfds.into().unwrap_or_else(|| {
210 readfds
211 .iter_mut()
212 .chain(writefds.iter_mut())
213 .chain(errorfds.iter_mut())
214 .map(|set| {
215 set.highest()
216 .map(|borrowed_fd| borrowed_fd.as_raw_fd())
217 .unwrap_or(-1)
218 })
219 .max()
220 .unwrap_or(-1)
221 + 1
222 });
223
224 let readfds = readfds
225 .map(|set| set as *mut _ as *mut libc::fd_set)
226 .unwrap_or(null_mut());
227 let writefds = writefds
228 .map(|set| set as *mut _ as *mut libc::fd_set)
229 .unwrap_or(null_mut());
230 let errorfds = errorfds
231 .map(|set| set as *mut _ as *mut libc::fd_set)
232 .unwrap_or(null_mut());
233 let timeout = timeout
234 .map(|tv| tv as *mut _ as *mut libc::timeval)
235 .unwrap_or(null_mut());
236
237 let res =
238 unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
239
240 Errno::result(res)
241 }
242
243 feature! {
244 #![feature = "signal"]
245
246 use crate::sys::signal::SigSet;
247
248 /// Monitors file descriptors for readiness with an altered signal mask.
249 ///
250 /// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
251 /// file descriptors that are ready for the given operation are set.
252 ///
253 /// When this function returns, the original signal mask is restored.
254 ///
255 /// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
256 ///
257 /// # Parameters
258 ///
259 /// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
260 /// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
261 /// to the maximum of that.
262 /// * `readfds`: File descriptors to check for read readiness
263 /// * `writefds`: File descriptors to check for write readiness
264 /// * `errorfds`: File descriptors to check for pending error conditions.
265 /// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
266 /// indefinitely).
267 /// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
268 /// ready (`None` to set no alternative signal mask).
269 ///
270 /// # References
271 ///
272 /// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
273 ///
274 /// [The new pselect() system call](https://lwn.net/Articles/176911/)
275 ///
276 /// [`FdSet::highest`]: struct.FdSet.html#method.highest
277 pub fn pselect<'a, 'fd, N, R, W, E, T, S>(nfds: N,
278 readfds: R,
279 writefds: W,
280 errorfds: E,
281 timeout: T,
282 sigmask: S) -> Result<c_int>
283 where
284 'fd: 'a,
285 N: Into<Option<c_int>>,
286 R: Into<Option<&'a mut FdSet<'fd>>>,
287 W: Into<Option<&'a mut FdSet<'fd>>>,
288 E: Into<Option<&'a mut FdSet<'fd>>>,
289 T: Into<Option<&'a TimeSpec>>,
290 S: Into<Option<&'a SigSet>>,
291 {
292 let mut readfds = readfds.into();
293 let mut writefds = writefds.into();
294 let mut errorfds = errorfds.into();
295 let sigmask = sigmask.into();
296 let timeout = timeout.into();
297
298 let nfds = nfds.into().unwrap_or_else(|| {
299 readfds.iter_mut()
300 .chain(writefds.iter_mut())
301 .chain(errorfds.iter_mut())
302 .map(|set| set.highest().map(|borrowed_fd|borrowed_fd.as_raw_fd()).unwrap_or(-1))
303 .max()
304 .unwrap_or(-1) + 1
305 });
306
307 let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
308 let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
309 let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
310 let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
311 let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
312
313 let res = unsafe {
314 libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
315 };
316
317 Errno::result(res)
318 }
319 }
320