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 pub unsafe fn from_raw_fd(fd: RawFd) -> Self {
46 OwnedFd::from_raw_fd(fd).into()
47 }
48
49 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 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 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 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}