use crate::io::FileDesc;
use crate::sys::cvt_r;
use crate::IntoInner;
use libc::{self, c_void, size_t};
use std::fs::File;
use std::io::{Result, SeekFrom};
use std::mem;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::process::Stdio;
#[derive(Debug, PartialEq, Eq)]
pub struct RawIo {
fd: RawFd,
}
impl Into<Stdio> for RawIo {
fn into(self) -> Stdio {
unsafe { FromRawFd::from_raw_fd(self.into_inner()) }
}
}
impl FromRawFd for FileDesc {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self::new(fd)
}
}
impl AsRawFd for FileDesc {
fn as_raw_fd(&self) -> RawFd {
self.inner().inner()
}
}
impl IntoRawFd for FileDesc {
fn into_raw_fd(self) -> RawFd {
unsafe { self.into_inner().into_inner() }
}
}
impl From<File> for FileDesc {
fn from(file: File) -> Self {
unsafe { FromRawFd::from_raw_fd(file.into_raw_fd()) }
}
}
impl mio::Evented for FileDesc {
fn register(
&self,
poll: &mio::Poll,
token: mio::Token,
interest: mio::Ready,
opts: mio::PollOpt,
) -> Result<()> {
mio::unix::EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts)
}
fn reregister(
&self,
poll: &mio::Poll,
token: mio::Token,
interest: mio::Ready,
opts: mio::PollOpt,
) -> Result<()> {
mio::unix::EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &mio::Poll) -> Result<()> {
mio::unix::EventedFd(&self.as_raw_fd()).deregister(poll)
}
}
impl RawIo {
pub unsafe fn new(fd: RawFd) -> Self {
RawIo { fd }
}
pub unsafe fn into_inner(self) -> RawFd {
let fd = self.fd;
mem::forget(self);
fd
}
pub fn inner(&self) -> RawFd {
self.fd
}
pub fn duplicate(&self) -> Result<Self> {
unsafe { Ok(RawIo::new(cvt_r(|| libc::dup(self.fd))?)) }
}
pub fn read_inner(&self, buf: &mut [u8]) -> Result<usize> {
let ret = cvt_r(|| unsafe {
libc::read(
self.fd,
buf.as_mut_ptr() as *mut c_void,
buf.len() as size_t,
)
})?;
Ok(ret as usize)
}
pub fn write_inner(&self, buf: &[u8]) -> Result<usize> {
let ret = cvt_r(|| unsafe {
libc::write(self.fd, buf.as_ptr() as *const c_void, buf.len() as size_t)
})?;
Ok(ret as usize)
}
pub fn flush_inner(&self) -> Result<()> {
Ok(())
}
pub fn seek(&self, pos: SeekFrom) -> Result<u64> {
let (whence, pos) = match pos {
SeekFrom::Start(off) => (libc::SEEK_SET, off as libc::off_t),
SeekFrom::End(off) => (libc::SEEK_END, off as libc::off_t),
SeekFrom::Current(off) => (libc::SEEK_CUR, off as libc::off_t),
};
let n = cvt_r(|| unsafe { libc::lseek(self.fd, pos, whence) })?;
Ok(n as u64)
}
#[cfg_attr(
any(target_os = "linux", target_os = "android", target_os = "emscripten"),
allow(dead_code)
)]
pub fn set_cloexec(&self, set: bool) -> Result<()> {
unsafe {
let flags = cvt_r(|| libc::fcntl(self.fd, libc::F_GETFD))?;
let new_flags = if set {
flags | libc::FD_CLOEXEC
} else {
flags & !libc::FD_CLOEXEC
};
cvt_r(|| libc::fcntl(self.fd, libc::F_SETFD, new_flags)).map(|_| ())
}
}
pub fn set_nonblock(&mut self, set: bool) -> Result<()> {
unsafe {
let flags = cvt_r(|| libc::fcntl(self.fd, libc::F_GETFL))?;
let new_flags = if set {
flags | libc::O_NONBLOCK
} else {
flags & !libc::O_NONBLOCK
};
cvt_r(|| libc::fcntl(self.fd, libc::F_SETFL, new_flags)).map(|_| ())
}
}
}
impl Drop for RawIo {
fn drop(&mut self) {
let _ = unsafe { libc::close(self.fd) };
}
}
unsafe fn dup_fd_cloexec(fd: RawFd) -> Result<RawIo> {
let min_fd = libc::STDERR_FILENO + 1;
Ok(RawIo::new(cvt_r(|| {
libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, min_fd)
})?))
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
pub fn pipe() -> Result<(RawIo, RawIo)> {
unsafe {
let mut fds = [0; 2];
cvt_r(|| libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?;
let reader = RawIo::new(fds[0]);
let writer = RawIo::new(fds[1]);
Ok((reader, writer))
}
}
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "emscripten")))]
pub fn pipe() -> Result<(RawIo, RawIo)> {
unsafe {
let mut fds = [0; 2];
cvt_r(|| libc::pipe(fds.as_mut_ptr()))?;
let reader = RawIo::new(fds[0]);
let writer = RawIo::new(fds[1]);
reader.set_cloexec(true)?;
writer.set_cloexec(true)?;
Ok((reader, writer))
}
}
pub fn dup_stdio() -> Result<(RawIo, RawIo, RawIo)> {
unsafe {
Ok((
dup_fd_cloexec(libc::STDIN_FILENO)?,
dup_fd_cloexec(libc::STDOUT_FILENO)?,
dup_fd_cloexec(libc::STDERR_FILENO)?,
))
}
}
pub fn getpid() -> libc::pid_t {
unsafe { libc::getpid() }
}