#![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 std::cmp::max;
use std::ffi::CStr;
use std::io::Error;
use std::mem::{self, MaybeUninit};
use std::os::fd::{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 struct SelectFd<'fd> {
pub fd: BorrowedFd<'fd>,
pub ready: bool,
}
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, wfd| max(max_fd, wfd.fd.as_raw_fd()));
unsafe {
let mut fds = MaybeUninit::<fd_set>::uninit();
FD_ZERO(fds.as_mut_ptr());
fds.assume_init();
for wfd in select_fds.iter() {
FD_SET(wfd.fd.as_raw_fd(), fds.as_mut_ptr());
}
let mut nfds;
loop {
nfds = libc::select(
max_fd + 1,
fds.as_mut_ptr(),
null_mut(),
null_mut(),
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 wfd in select_fds.iter_mut() {
if FD_ISSET(wfd.fd.as_raw_fd(), fds.as_mut_ptr()) {
wfd.ready = true;
}
}
};
Ok(())
}
pub fn ptsname(fd: BorrowedFd) -> Result<String, Errno> {
static MUTEX: Mutex<()> = Mutex::new(());
let _guard = MUTEX.lock();
let s_ref = unsafe {
let s_ptr = libc::ptsname(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: BorrowedFd, buf: &mut [u8]) -> Result<usize, Errno> {
loop {
let ret = unsafe {
libc::read(
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: BorrowedFd, buf: &[u8]) -> Result<usize, Errno> {
let mut pos = 0;
while pos < buf.len() {
let ret = unsafe {
libc::write(
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 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),
}
}