use std::io;
use std::os::fd::RawFd;
use super::interrupted;
#[inline(always)]
pub(crate) unsafe fn syscall(nr: libc::c_long, args: [usize; 6]) -> io::Result<usize> {
let [a0, a1, a2, a3, a4, a5] = args;
let ret: isize;
#[cfg(target_arch = "x86_64")]
unsafe {
core::arch::asm!(
"syscall",
inlateout("rax") nr as isize => ret,
in("rdi") a0,
in("rsi") a1,
in("rdx") a2,
in("r10") a3,
in("r8") a4,
in("r9") a5,
lateout("rcx") _,
lateout("r11") _,
options(nostack),
);
}
#[cfg(target_arch = "aarch64")]
unsafe {
core::arch::asm!(
"svc 0",
in("x8") nr,
inlateout("x0") a0 => ret,
in("x1") a1,
in("x2") a2,
in("x3") a3,
in("x4") a4,
in("x5") a5,
options(nostack),
);
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
ret = unsafe { libc::syscall(nr, a0, a1, a2, a3, a4, a5) } as isize;
if ret == -1 {
return Err(io::Error::last_os_error());
}
}
if (-4095..0).contains(&ret) {
return Err(io::Error::from_raw_os_error(-ret as i32));
}
Ok(ret as usize)
}
#[cfg(all(
any(target_os = "linux", target_os = "android"),
any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
)
))]
const SO_INQ: libc::c_int = 84;
pub(crate) const UNIX_INQ: bool = cfg!(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
));
pub(crate) fn enable_inq(fd: RawFd, tcp: bool) -> bool {
let (level, name) = if tcp {
(libc::IPPROTO_TCP, libc::TCP_INQ)
} else {
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
))]
{
(libc::SOL_SOCKET, SO_INQ)
}
#[cfg(not(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
)))]
return false;
};
let one: libc::c_int = 1;
let ret = unsafe {
libc::setsockopt(
fd,
level,
name,
(&one as *const libc::c_int).cast(),
size_of::<libc::c_int>() as libc::socklen_t,
)
};
ret == 0
}
#[cfg(ring)]
pub(crate) fn end_queued(fd: RawFd, tcp: bool) -> bool {
let ends = if tcp {
libc::POLLERR
} else {
libc::POLLRDHUP | libc::POLLHUP | libc::POLLERR
};
let mut look = libc::pollfd {
fd,
events: libc::POLLRDHUP,
revents: 0,
};
unsafe { libc::poll(&mut look, 1, 0) == 1 && look.revents & ends != 0 }
}
#[inline(always)]
pub(crate) fn read_inq(fd: RawFd, buf: &mut [u8]) -> io::Result<(usize, Option<usize>)> {
const CONTROL: usize =
unsafe { libc::CMSG_SPACE(size_of::<libc::c_int>() as libc::c_uint) } as usize;
let mut control = [0u64; CONTROL.div_ceil(size_of::<u64>())];
let mut iov = libc::iovec {
iov_base: buf.as_mut_ptr().cast(),
iov_len: buf.len(),
};
let mut msg: libc::msghdr = unsafe { core::mem::zeroed() };
msg.msg_iov = &mut iov;
msg.msg_iovlen = 1;
msg.msg_control = control.as_mut_ptr().cast();
msg.msg_controllen = CONTROL as _;
let called = unsafe {
syscall(
libc::SYS_recvmsg,
[fd as usize, (&raw mut msg) as usize, 0, 0, 0, 0],
)
};
let n = match called {
Ok(n) => n,
Err(err) => return read_inq_failed(fd, buf, err),
};
let left = unsafe {
let cmsg = libc::CMSG_FIRSTHDR(&msg);
(!cmsg.is_null() && is_inq((*cmsg).cmsg_level, (*cmsg).cmsg_type))
.then(|| libc::CMSG_DATA(cmsg).cast::<u32>().read_unaligned() as usize)
};
Ok((n, left))
}
#[cold]
#[inline(never)]
fn read_inq_failed(
fd: RawFd,
buf: &mut [u8],
err: io::Error,
) -> io::Result<(usize, Option<usize>)> {
if interrupted(&err) {
return read_inq(fd, buf);
}
Err(err)
}
fn is_inq(level: libc::c_int, kind: libc::c_int) -> bool {
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "riscv64"
))]
if level == libc::SOL_SOCKET && kind == SO_INQ {
return true;
}
level == libc::IPPROTO_TCP && kind == libc::TCP_CM_INQ
}