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