ensc_libc_util/
fd.rs

1#![allow(unused_unsafe)]
2
3use std::os::fd::{AsFd, AsRawFd, BorrowedFd, 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 AsFd for Fd {
41    fn as_fd(&self) -> BorrowedFd<'_> {
42        match self {
43            Fd::None(location)	=> panic!("fd consumed at {location:?}"),
44            Fd::Owned(fd)	=> fd.as_fd(),
45            Fd::Borrowed(fd)	=> unsafe { BorrowedFd::borrow_raw(*fd) }
46        }
47    }
48}
49
50impl Fd {
51    /// # Safety
52    ///
53    /// The resource pointed to by `fd` must be open and suitable for assuming
54    /// ownership. The resource must not require any cleanup other than `close`.
55    pub unsafe fn from_raw_fd(fd: RawFd) -> Self {
56	OwnedFd::from_raw_fd(fd).into()
57    }
58
59    /// # Safety
60    ///
61    /// Given fd must not be closed as long as the created Fd is alive.
62    pub unsafe fn share_raw_fd(fd: RawFd) -> Self {
63	Self::Borrowed(fd)
64    }
65
66    #[track_caller]
67    pub fn close(&mut self) {
68	*self = Self::None(std::panic::Location::caller())
69    }
70
71    pub fn try_as_raw_fd(&self) -> std::io::Result<RawFd> {
72	match self {
73	    Self::None(_)		=> Err(std::io::Error::from_raw_os_error(libc::EBADF)),
74	    Self::Owned(fd)		=> Ok(fd.as_raw_fd()),
75	    Self::Borrowed(fd)		=> Ok(fd.as_raw_fd()),
76	}
77    }
78
79    pub fn is_readable(&self) -> std::io::Result<bool> {
80        self.select(true)
81    }
82
83    pub fn is_writeable(&self) -> std::io::Result<bool> {
84        self.select(false)
85    }
86
87    fn select(&self, is_read: bool) -> std::io::Result<bool> {
88        let fd = self.try_as_raw_fd()?;
89
90        let mut fds = std::mem::MaybeUninit::<libc::fd_set>::uninit();
91        let mut timeout = libc::timeval {
92            tv_sec:  0,
93            tv_usec: 0,
94        };
95
96        unsafe { libc::FD_ZERO(fds.as_mut_ptr()) };
97
98        let mut fds = unsafe { fds.assume_init() };
99
100        unsafe { libc::FD_SET(fd, &mut fds) };
101
102        let read_fds = match is_read {
103            true	=> &mut fds as * mut libc::fd_set,
104            false	=> null_mut(),
105        };
106
107        let write_fds = match is_read {
108            false	=> &mut fds as * mut libc::fd_set,
109            true	=> null_mut(),
110        };
111
112        match unsafe { libc::select(fd + 1, read_fds, write_fds, null_mut(), &mut timeout) } {
113            -1		=> Err(std::io::Error::last_os_error()),
114            _		=> Ok(unsafe { libc::FD_ISSET(fd, &fds) }),
115       }
116    }
117
118    /// # Safety
119    /// require p to match the request
120    pub unsafe fn ioctl_ptr<T>(&self,  request: libc::c_ulong, p: *mut T) -> std::io::Result<i32> {
121	let fd = self.try_as_raw_fd()?;
122
123        retry(|| unsafe { libc::ioctl(fd, request, p) })
124    }
125
126    /// # Safety
127    /// require p to match the request
128    pub unsafe fn ioctl_ptrc<T>(&self,  request: libc::c_ulong, p: *const T) -> std::io::Result<i32>
129    {
130	let p_mut: *mut T  = std::mem::transmute(p);
131
132	self.ioctl_ptr(request, p_mut)
133    }
134
135    /// # Safety
136    /// allocated memory must be released manually
137    pub unsafe fn mmap(&self, addr: *mut libc::c_void, len: libc::size_t, prot: libc::c_int,
138		       flags: libc::c_int, offset: libc::off_t) -> std::io::Result<*mut libc::c_void>
139    {
140	let fd = self.try_as_raw_fd()?;
141
142	match unsafe { libc::mmap(addr, len, prot, flags, fd, offset) } {
143	    rc if rc == libc::MAP_FAILED	=> Err(std::io::Error::last_os_error()),
144	    addr				=> Ok(addr),
145	}
146    }
147}