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 pub unsafe fn from_raw_fd(fd: RawFd) -> Self {
56 OwnedFd::from_raw_fd(fd).into()
57 }
58
59 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 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 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 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}