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