#![allow(clippy::unnecessary_cast)]
use libc::{self, fd_set, suseconds_t, time_t, timespec, timeval};
use libc::{FD_ISSET, FD_SET, FD_ZERO};
use rustix::io::Errno;
use rustix::process::{Pid, Signal};
use rustix::thread;
use std::cmp::max;
use std::ffi::CStr;
use std::io::Error;
use std::mem::{self, MaybeUninit};
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::ptr::null_mut;
use std::sync::Mutex;
use std::time::Duration;
fn last_errno() -> Errno {
Errno::from_io_error(&Error::last_os_error()).unwrap()
}
pub fn gettid() -> i64 {
thread::gettid().as_raw_nonzero().get() as i64
}
pub struct SelectFd<'fd> {
pub fd: BorrowedFd<'fd>,
pub mask: u32,
}
impl SelectFd<'_> {
pub const READABLE: u32 = 0x1;
pub const WRITEABLE: u32 = 0x2;
pub const EXCEPTION: u32 = 0x4;
}
pub fn select(select_fds: &mut [&mut SelectFd], timeout: Option<Duration>) -> Result<(), Errno> {
let mut tv_timeout = timeout.map(|d| timeval {
tv_sec: d.as_secs() as time_t,
tv_usec: d.subsec_micros() as suseconds_t,
});
let max_fd = select_fds
.iter()
.fold(0, |max_fd, sel_fd| max(max_fd, sel_fd.fd.as_raw_fd()));
unsafe {
let mut rd_fds = MaybeUninit::<fd_set>::uninit();
let mut wr_fds = MaybeUninit::<fd_set>::uninit();
let mut ex_fds = MaybeUninit::<fd_set>::uninit();
FD_ZERO(rd_fds.as_mut_ptr());
FD_ZERO(wr_fds.as_mut_ptr());
FD_ZERO(ex_fds.as_mut_ptr());
rd_fds.assume_init();
wr_fds.assume_init();
ex_fds.assume_init();
for sel_fd in select_fds.iter() {
if sel_fd.mask & SelectFd::READABLE != 0 {
FD_SET(sel_fd.fd.as_raw_fd(), rd_fds.as_mut_ptr());
}
if sel_fd.mask & SelectFd::WRITEABLE != 0 {
FD_SET(sel_fd.fd.as_raw_fd(), wr_fds.as_mut_ptr());
}
if sel_fd.mask & SelectFd::EXCEPTION != 0 {
FD_SET(sel_fd.fd.as_raw_fd(), ex_fds.as_mut_ptr());
}
}
let mut nfds;
loop {
nfds = libc::select(
max_fd + 1,
rd_fds.as_mut_ptr(),
wr_fds.as_mut_ptr(),
ex_fds.as_mut_ptr(),
if tv_timeout.is_some() {
tv_timeout.as_mut().unwrap() as *mut timeval
} else {
null_mut()
},
);
if nfds < 0 {
if last_errno() == Errno::INTR {
continue;
}
return Err(last_errno());
}
break;
}
for sel_fd in select_fds.iter_mut() {
sel_fd.mask = 0;
if FD_ISSET(sel_fd.fd.as_raw_fd(), rd_fds.as_mut_ptr()) {
sel_fd.mask |= SelectFd::READABLE;
}
if FD_ISSET(sel_fd.fd.as_raw_fd(), wr_fds.as_mut_ptr()) {
sel_fd.mask |= SelectFd::WRITEABLE;
}
if FD_ISSET(sel_fd.fd.as_raw_fd(), ex_fds.as_mut_ptr()) {
sel_fd.mask |= SelectFd::EXCEPTION;
}
}
};
Ok(())
}
pub fn ptsname<Fd: AsFd>(fd: Fd) -> Result<String, Errno> {
static MUTEX: Mutex<()> = Mutex::new(());
let _guard = MUTEX.lock();
let s_ref = unsafe {
let s_ptr = libc::ptsname(fd.as_fd().as_raw_fd());
if s_ptr.is_null() {
return Err(last_errno());
}
CStr::from_ptr(s_ptr).to_str().unwrap()
};
Ok(s_ref.to_string())
}
pub enum Fork {
Parent(Pid),
Child,
}
pub unsafe fn fork() -> Result<Fork, Errno> {
match unsafe { libc::fork() } {
pid if pid > 0 => Ok(Fork::Parent(Pid::from_raw(pid).unwrap())),
0 => Ok(Fork::Child),
_ => Err(last_errno()),
}
}
pub unsafe fn fast_exit(code: i32) {
unsafe { libc::_exit(code) }
}
pub fn read<Fd: AsFd>(fd: Fd, buf: &mut [u8]) -> Result<usize, Errno> {
loop {
let ret = unsafe {
libc::read(
fd.as_fd().as_raw_fd(),
buf.as_mut_ptr() as *mut libc::c_void,
buf.len(),
)
};
if ret < 0 {
if last_errno() == Errno::INTR {
continue;
}
return Err(last_errno());
}
return Ok(ret as usize);
}
}
pub fn write<Fd: AsFd>(fd: Fd, buf: &[u8]) -> Result<usize, Errno> {
let mut pos = 0;
while pos < buf.len() {
let ret = unsafe {
libc::write(
fd.as_fd().as_raw_fd(),
buf[pos..].as_ptr() as *mut libc::c_void,
buf.len() - pos,
)
};
if ret < 0 {
if last_errno() == Errno::INTR {
continue;
}
return Err(last_errno());
}
if ret == 0 {
break;
}
pos += ret as usize;
}
Ok(pos)
}
pub unsafe fn close_raw(fd: RawFd) {
loop {
if unsafe { libc::close(fd) } == 0 || last_errno() != Errno::INTR {
break;
}
}
}
pub fn fcntl_nonblock<Fd: AsFd>(fd: Fd, non_block: bool) -> Result<(), Errno> {
loop {
let mut flags = unsafe { libc::fcntl(fd.as_fd().as_raw_fd(), libc::F_GETFL) };
if flags < 0 {
if last_errno() == Errno::INTR {
continue;
}
return Err(last_errno());
}
if non_block {
flags |= libc::O_NONBLOCK;
} else {
flags &= !libc::O_NONBLOCK;
}
let ret =
unsafe { libc::fcntl(fd.as_fd().as_raw_fd(), libc::F_SETFL, flags as libc::c_uint) };
if ret < 0 {
if last_errno() == Errno::INTR {
continue;
}
return Err(last_errno());
}
return Ok(());
}
}
pub enum SigAction {
Default,
Ignore,
}
pub fn sigaction(sig: Signal, action: SigAction) -> Result<(), Errno> {
let hnd = match action {
SigAction::Default => libc::SIG_DFL,
SigAction::Ignore => libc::SIG_IGN,
};
let ret = unsafe {
let mut sa: libc::sigaction = mem::zeroed();
sa.sa_sigaction = hnd;
sa.sa_flags = libc::SA_RESTART;
libc::sigfillset(&mut sa.sa_mask as *mut libc::sigset_t);
libc::sigaction(sig.as_raw(), &sa, null_mut())
};
if ret < 0 {
return Err(last_errno());
}
Ok(())
}
pub enum SigMask {
Block,
Unblock,
}
pub fn sigmask(sig_list: &[Signal], action: SigMask) -> Result<(), Errno> {
let how = match action {
SigMask::Block => libc::SIG_BLOCK,
SigMask::Unblock => libc::SIG_UNBLOCK,
};
let ret = unsafe {
let mut sm: libc::sigset_t = mem::zeroed();
for sig in sig_list {
libc::sigaddset(&mut sm as *mut libc::sigset_t, sig.as_raw() as libc::c_int);
}
libc::pthread_sigmask(how, &mut sm as *mut libc::sigset_t, null_mut())
};
if ret < 0 {
return Err(last_errno());
}
Ok(())
}
pub fn sigwait(sig_list: &[Signal], timeout: Option<Duration>) -> Result<Option<Signal>, Errno> {
let mut ts_timeout = timeout.map(|d| timespec {
tv_sec: d.as_secs() as time_t,
tv_nsec: d.subsec_nanos() as i64,
});
let mut ret;
loop {
unsafe {
let mut sm: libc::sigset_t = mem::zeroed();
for sig in sig_list {
libc::sigaddset(&mut sm as *mut libc::sigset_t, sig.as_raw() as libc::c_int);
}
let mut sig_info: libc::siginfo_t = mem::zeroed();
if ts_timeout.is_some() {
ret = libc::sigtimedwait(
&mut sm as *mut libc::sigset_t,
&mut sig_info as *mut libc::siginfo_t,
ts_timeout.as_mut().unwrap() as *mut timespec,
);
} else {
ret = libc::sigwaitinfo(
&mut sm as *mut libc::sigset_t,
&mut sig_info as *mut libc::siginfo_t,
)
}
};
if ret < 0 {
if last_errno() == Errno::AGAIN {
return Ok(None);
}
if last_errno() == Errno::INTR {
continue;
}
return Err(last_errno());
}
break;
}
let sig_no = ret as i32;
match Signal::from_named_raw(sig_no) {
Some(sig) => Ok(Some(sig)),
None => Err(Errno::INVAL),
}
}