ensc_libc_util/
fd.rs

1#![allow(unused_unsafe)]
2
3use std::os::fd::{AsRawFd, FromRawFd, OwnedFd};
4use std::os::unix::io::RawFd;
5use std::ptr::null_mut;
6
7#[derive(Debug)]
8pub enum Fd {
9    None(&'static std::panic::Location<'static>),
10    Owned(OwnedFd),
11    Borrowed(RawFd),
12}
13
14fn _errno() -> i32 {
15    std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
16}
17
18fn retry<F: Fn() -> i32>(f: F) -> std::io::Result<i32> {
19    loop {
20	let rc = f();
21
22	if rc >= 0 {
23	    break Ok(rc);
24	} else {
25	    let e = std::io::Error::last_os_error();
26
27	    if e.kind() != std::io::ErrorKind::Interrupted {
28		break Err(e)
29	    }
30	}
31    }
32}
33
34impl From<OwnedFd> for Fd {
35    fn from(value: OwnedFd) -> Self {
36        Self::Owned(value)
37    }
38}
39
40impl Fd {
41    /// # Safety
42    ///
43    /// The resource pointed to by `fd` must be open and suitable for assuming
44    /// ownership. The resource must not require any cleanup other than `close`.
45    pub unsafe fn from_raw_fd(fd: RawFd) -> Self {
46	OwnedFd::from_raw_fd(fd).into()
47    }
48
49    /// # Safety
50    ///
51    /// Given fd must not be closed as long as the created Fd is alive.
52    pub unsafe fn share_raw_fd(fd: RawFd) -> Self {
53	Self::Borrowed(fd)
54    }
55
56    #[track_caller]
57    pub fn close(&mut self) {
58	*self = Self::None(std::panic::Location::caller())
59    }
60
61    pub fn try_as_raw_fd(&self) -> std::io::Result<RawFd> {
62	match self {
63	    Self::None(_)		=> Err(std::io::Error::from_raw_os_error(libc::EBADF)),
64	    Self::Owned(fd)		=> Ok(fd.as_raw_fd()),
65	    Self::Borrowed(fd)		=> Ok(fd.as_raw_fd()),
66	}
67    }
68
69    pub fn is_readable(&self) -> std::io::Result<bool> {
70        self.select(true)
71    }
72
73    pub fn is_writeable(&self) -> std::io::Result<bool> {
74        self.select(false)
75    }
76
77    fn select(&self, is_read: bool) -> std::io::Result<bool> {
78        let fd = self.try_as_raw_fd()?;
79
80        let mut fds = std::mem::MaybeUninit::<libc::fd_set>::uninit();
81        let mut timeout = libc::timeval {
82            tv_sec:  0,
83            tv_usec: 0,
84        };
85
86        unsafe { libc::FD_ZERO(fds.as_mut_ptr()) };
87
88        let mut fds = unsafe { fds.assume_init() };
89
90        unsafe { libc::FD_SET(fd, &mut fds) };
91
92        let read_fds = match is_read {
93            true	=> &mut fds as * mut libc::fd_set,
94            false	=> null_mut(),
95        };
96
97        let write_fds = match is_read {
98            false	=> &mut fds as * mut libc::fd_set,
99            true	=> null_mut(),
100        };
101
102        match unsafe { libc::select(fd + 1, read_fds, write_fds, null_mut(), &mut timeout) } {
103            -1		=> Err(std::io::Error::last_os_error()),
104            _		=> Ok(unsafe { libc::FD_ISSET(fd, &fds) }),
105       }
106    }
107
108    /// # Safety
109    /// require p to match the request
110    pub unsafe fn ioctl_ptr<T>(&self,  request: libc::c_ulong, p: *mut T) -> std::io::Result<i32> {
111	let fd = self.try_as_raw_fd()?;
112
113        retry(|| unsafe { libc::ioctl(fd, request, p) })
114    }
115
116    /// # Safety
117    /// require p to match the request
118    pub unsafe fn ioctl_ptrc<T>(&self,  request: libc::c_ulong, p: *const T) -> std::io::Result<i32>
119    {
120	let p_mut: *mut T  = std::mem::transmute(p);
121
122	self.ioctl_ptr(request, p_mut)
123    }
124
125    /// # Safety
126    /// allocated memory must be released manually
127    pub unsafe fn mmap(&self, addr: *mut libc::c_void, len: libc::size_t, prot: libc::c_int,
128		       flags: libc::c_int, offset: libc::off_t) -> std::io::Result<*mut libc::c_void>
129    {
130	let fd = self.try_as_raw_fd()?;
131
132	match unsafe { libc::mmap(addr, len, prot, flags, fd, offset) } {
133	    rc if rc == libc::MAP_FAILED	=> Err(std::io::Error::last_os_error()),
134	    addr				=> Ok(addr),
135	}
136    }
137}