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