use super::poller::last_error;
use crate::{Error, ErrorKind, Result};
use std::os::{
fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
unix::net::UnixStream,
};
pub(super) fn null() -> Result<OwnedFd> {
let raw = unsafe { libc::open(c"/dev/null".as_ptr(), libc::O_RDWR | libc::O_CLOEXEC) };
if raw < 0 {
return Err(last_error());
}
Ok(unsafe { OwnedFd::from_raw_fd(raw) })
}
pub(super) fn stream_pair() -> Result<(OwnedFd, OwnedFd)> {
let (parent, child) = UnixStream::pair().map_err(Error::from)?;
Ok((OwnedFd::from(parent), OwnedFd::from(child)))
}
pub(super) fn one_way() -> Result<(OwnedFd, OwnedFd)> {
let mut fds = [0 as RawFd; 2];
#[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))]
let created = unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) };
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "freebsd")))]
let created = unsafe { libc::pipe(fds.as_mut_ptr()) };
if created < 0 {
return Err(last_error());
}
let (read, write) = unsafe { (OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])) };
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "freebsd")))]
for fd in [&read, &write] {
if unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) } < 0 {
return Err(last_error());
}
}
Ok((read, write))
}
pub(super) fn lift(fd: OwnedFd, floor: RawFd) -> Result<OwnedFd> {
if fd.as_raw_fd() >= floor {
return Ok(fd);
}
let raw = unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_DUPFD_CLOEXEC, floor) };
if raw < 0 {
return Err(last_error());
}
Ok(unsafe { OwnedFd::from_raw_fd(raw) })
}
pub(super) fn reserve(donor: RawFd, numbers: impl Iterator<Item = RawFd>) -> Result<Vec<OwnedFd>> {
let mut held = Vec::new();
for number in numbers {
if unsafe { libc::fcntl(number, libc::F_GETFD) } >= 0 {
continue;
}
if last_error().os != Some(libc::EBADF) {
return Err(last_error());
}
if unsafe { libc::dup2(donor, number) } < 0 {
return Err(last_error());
}
held.push(unsafe { OwnedFd::from_raw_fd(number) });
if unsafe { libc::fcntl(number, libc::F_SETFD, libc::FD_CLOEXEC) } < 0 {
return Err(last_error());
}
}
Ok(held)
}
pub(super) fn checked_floor(numbers: impl Iterator<Item = u32>) -> Result<RawFd> {
let mut floor = 0;
for number in numbers {
let number = RawFd::try_from(number).map_err(|_| Error::new(ErrorKind::InvalidInput))?;
floor = floor.max(number);
}
floor
.checked_add(1)
.ok_or_else(|| Error::new(ErrorKind::InvalidInput))
}