use std::io;
#[cfg(windows)]
use std::sync::Once;
#[cfg(unix)]
use crate::os::CheckLibcResult;
#[cfg(any(unix, windows))]
use crate::os::CheckLibcZero;
#[cfg(any(unix, windows))]
pub use libc::sighandler_t;
#[cfg(unix)]
pub use libc::{SIG_DFL, SIG_ERR, SIG_IGN};
#[cfg(not(unix))]
pub const SIG_DFL: libc::sighandler_t = 0;
#[cfg(not(unix))]
pub const SIG_IGN: libc::sighandler_t = 1;
#[cfg(not(unix))]
pub const SIG_ERR: libc::sighandler_t = -1 as _;
#[cfg(unix)]
pub use libc::{SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK};
#[cfg(any(unix, windows))]
pub use libc::{SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM};
#[cfg(unix)]
pub use libc::{
SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGHUP, SIGIO, SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSTOP,
SIGSYS, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH,
SIGXCPU, SIGXFSZ,
};
#[cfg(all(
unix,
not(any(
target_vendor = "apple",
target_os = "openbsd",
target_os = "freebsd",
target_os = "netbsd"
))
))]
pub use libc::{SIGPWR, SIGSTKFLT};
#[cfg(all(unix, not(target_os = "android")))]
pub use libc::{ITIMER_PROF, ITIMER_REAL, ITIMER_VIRTUAL};
#[cfg(target_os = "android")]
pub const ITIMER_REAL: libc::c_int = 0;
#[cfg(target_os = "android")]
pub const ITIMER_VIRTUAL: libc::c_int = 1;
#[cfg(target_os = "android")]
pub const ITIMER_PROF: libc::c_int = 2;
#[cfg(unix)]
#[must_use]
pub fn timeval_to_double(tv: &libc::timeval) -> f64 {
tv.tv_sec as f64 + (tv.tv_usec as f64 / 1_000_000.0)
}
#[cfg(unix)]
#[must_use]
pub fn double_to_timeval(val: f64) -> libc::timeval {
libc::timeval {
tv_sec: val.trunc() as _,
tv_usec: (val.fract() * 1_000_000.0) as _,
}
}
#[cfg(unix)]
#[must_use]
pub fn itimerval_to_tuple(it: &libc::itimerval) -> (f64, f64) {
(
timeval_to_double(&it.it_value),
timeval_to_double(&it.it_interval),
)
}
#[cfg(all(unix, not(target_os = "redox")))]
unsafe extern "C" {
#[link_name = "siginterrupt"]
fn c_siginterrupt(sig: i32, flag: i32) -> i32;
}
#[cfg(any(target_os = "linux", target_os = "android"))]
mod ffi {
unsafe extern "C" {
pub(super) fn getitimer(
which: libc::c_int,
curr_value: *mut libc::itimerval,
) -> libc::c_int;
pub(super) fn setitimer(
which: libc::c_int,
new_value: *const libc::itimerval,
old_value: *mut libc::itimerval,
) -> libc::c_int;
}
}
#[cfg(any(unix, windows))]
pub unsafe fn probe_handler(signalnum: i32) -> Option<sighandler_t> {
let handler = unsafe { libc::signal(signalnum, libc::SIG_IGN) };
if handler == libc::SIG_ERR as sighandler_t {
None
} else {
unsafe { libc::signal(signalnum, handler) };
Some(handler)
}
}
#[cfg(any(unix, windows))]
pub unsafe fn install_handler(signalnum: i32, handler: sighandler_t) -> io::Result<sighandler_t> {
let old = unsafe { libc::signal(signalnum, handler) };
if old == libc::SIG_ERR as sighandler_t {
return Err(io::Error::last_os_error());
}
#[cfg(all(unix, not(target_os = "redox")))]
let _ = siginterrupt(signalnum, 1);
Ok(old)
}
#[cfg(any(unix, windows))]
pub fn raise_signal(signalnum: i32) -> io::Result<()> {
unsafe { libc::raise(signalnum) }.check_libc_zero()
}
#[cfg(unix)]
pub fn alarm(seconds: u32) -> u32 {
unsafe { libc::alarm(seconds) }
}
#[cfg(unix)]
pub fn pause() {
unsafe { libc::pause() };
}
#[cfg(unix)]
pub fn set_sigint_default_onstack() -> io::Result<()> {
let mut action: libc::sigaction = unsafe { core::mem::zeroed() };
action.sa_sigaction = libc::SIG_DFL;
action.sa_flags = libc::SA_ONSTACK;
unsafe { libc::sigemptyset(&mut action.sa_mask) }.check_libc_zero()?;
unsafe { libc::sigaction(libc::SIGINT, &action, core::ptr::null_mut()) }.check_libc_zero()
}
#[cfg(unix)]
pub fn send_sigint_to_self() -> io::Result<()> {
unsafe { libc::kill(libc::getpid(), libc::SIGINT) }.check_libc_zero()
}
#[cfg(unix)]
pub fn setitimer(which: i32, new: &libc::itimerval) -> io::Result<libc::itimerval> {
let mut old = core::mem::MaybeUninit::<libc::itimerval>::uninit();
#[cfg(any(target_os = "linux", target_os = "android"))]
let ret = unsafe { ffi::setitimer(which, new, old.as_mut_ptr()) };
#[cfg(not(any(target_os = "linux", target_os = "android")))]
let ret = unsafe { libc::setitimer(which, new, old.as_mut_ptr()) };
ret.check_libc_zero()?;
Ok(unsafe { old.assume_init() })
}
#[cfg(unix)]
pub fn getitimer(which: i32) -> io::Result<libc::itimerval> {
let mut old = core::mem::MaybeUninit::<libc::itimerval>::uninit();
#[cfg(any(target_os = "linux", target_os = "android"))]
let ret = unsafe { ffi::getitimer(which, old.as_mut_ptr()) };
#[cfg(not(any(target_os = "linux", target_os = "android")))]
let ret = unsafe { libc::getitimer(which, old.as_mut_ptr()) };
ret.check_libc_zero()?;
Ok(unsafe { old.assume_init() })
}
#[cfg(unix)]
pub fn sigemptyset() -> io::Result<libc::sigset_t> {
let mut set: libc::sigset_t = unsafe { core::mem::zeroed() };
unsafe { libc::sigemptyset(&mut set) }.check_libc_zero()?;
Ok(set)
}
#[cfg(unix)]
pub fn sigaddset(set: &mut libc::sigset_t, signum: i32) -> io::Result<()> {
unsafe { libc::sigaddset(set, signum) }.check_libc_zero()
}
#[cfg(unix)]
pub fn pthread_sigmask(how: i32, set: &libc::sigset_t) -> io::Result<libc::sigset_t> {
let mut old_mask: libc::sigset_t = unsafe { core::mem::zeroed() };
let err = unsafe { libc::pthread_sigmask(how, set, &mut old_mask) };
if err != 0 {
Err(io::Error::from_raw_os_error(err))
} else {
Ok(old_mask)
}
}
#[cfg(any(target_os = "android", target_os = "linux"))]
pub fn pidfd_send_signal(pidfd: i32, sig: i32, flags: u32) -> io::Result<()> {
let ret = unsafe {
libc::syscall(
libc::SYS_pidfd_send_signal,
pidfd,
sig,
core::ptr::null::<libc::siginfo_t>(),
flags,
) as libc::c_long
};
ret.check_libc_neg()?;
Ok(())
}
#[cfg(all(unix, not(target_os = "redox")))]
pub fn siginterrupt(signalnum: i32, flag: i32) -> io::Result<()> {
unsafe { c_siginterrupt(signalnum, flag) }.check_libc_neg()?;
Ok(())
}
#[cfg(windows)]
pub const VALID_SIGNALS: &[i32] = &[
libc::SIGINT,
libc::SIGILL,
libc::SIGFPE,
libc::SIGSEGV,
libc::SIGTERM,
21, libc::SIGABRT,
];
#[cfg(windows)]
pub const SIGBREAK: i32 = 21;
#[cfg(windows)]
pub const CTRL_C_EVENT: u32 = 0;
#[cfg(windows)]
pub const CTRL_BREAK_EVENT: u32 = 1;
#[cfg(windows)]
pub const INVALID_SOCKET: libc::SOCKET = windows_sys::Win32::Networking::WinSock::INVALID_SOCKET;
#[cfg(windows)]
fn init_winsock() {
static WSA_INIT: Once = Once::new();
WSA_INIT.call_once(|| unsafe {
let mut wsa_data = core::mem::MaybeUninit::uninit();
let _ = windows_sys::Win32::Networking::WinSock::WSAStartup(0x0101, wsa_data.as_mut_ptr());
});
}
#[cfg(windows)]
pub fn wakeup_fd_is_socket(fd: libc::SOCKET) -> io::Result<bool> {
use windows_sys::Win32::Networking::WinSock;
init_winsock();
let mut res = 0i32;
let mut res_size = core::mem::size_of::<i32>() as i32;
let getsockopt_res = unsafe {
WinSock::getsockopt(
fd,
WinSock::SOL_SOCKET,
WinSock::SO_ERROR,
&mut res as *mut i32 as *mut _,
&mut res_size,
)
};
if getsockopt_res == 0 {
return Ok(true);
}
let err = io::Error::last_os_error();
if err.raw_os_error() != Some(WinSock::WSAENOTSOCK) {
return Err(err);
}
let fd_i32 =
i32::try_from(fd).map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "invalid fd"))?;
let borrowed = unsafe { crate::crt_fd::Borrowed::try_borrow_raw(fd_i32) }?;
crate::fileutils::fstat(borrowed)?;
Ok(false)
}
#[cfg(windows)]
pub fn notify_signal(
signum: i32,
wakeup_fd: libc::SOCKET,
wakeup_is_socket: bool,
sigint_event: Option<isize>,
) {
if signum == libc::SIGINT
&& let Some(handle) = sigint_event
{
unsafe {
windows_sys::Win32::System::Threading::SetEvent(handle as _);
}
}
if wakeup_fd == INVALID_SOCKET {
return;
}
let sigbyte = signum as u8;
if wakeup_is_socket {
unsafe {
let _ = windows_sys::Win32::Networking::WinSock::send(
wakeup_fd,
&sigbyte as *const u8 as *const _,
1,
0,
);
}
} else {
unsafe {
let _ = libc::write(wakeup_fd as _, &sigbyte as *const u8 as *const _, 1);
}
}
}
#[cfg(unix)]
pub fn notify_signal(signum: i32, wakeup_fd: i32) {
if wakeup_fd == -1 {
return;
}
let sigbyte = signum as u8;
unsafe {
let _ = libc::write(wakeup_fd, &sigbyte as *const u8 as *const _, 1);
}
}
#[cfg(unix)]
pub fn strsignal(signalnum: i32) -> Option<String> {
let s = unsafe { libc::strsignal(signalnum) };
if s.is_null() {
None
} else {
let cstr = unsafe { core::ffi::CStr::from_ptr(s) };
Some(cstr.to_string_lossy().into_owned())
}
}
#[cfg(windows)]
pub fn strsignal(signalnum: i32) -> Option<String> {
let name = match signalnum {
libc::SIGINT => "Interrupt",
libc::SIGILL => "Illegal instruction",
libc::SIGFPE => "Floating-point exception",
libc::SIGSEGV => "Segmentation fault",
libc::SIGTERM => "Terminated",
21 => "Break",
libc::SIGABRT => "Aborted",
_ => return None,
};
Some(name.to_owned())
}
#[cfg(unix)]
pub fn valid_signals(max_signum: usize) -> io::Result<Vec<i32>> {
let mut mask: libc::sigset_t = unsafe { core::mem::zeroed() };
unsafe { libc::sigfillset(&mut mask) }.check_libc_zero()?;
let mut signals = Vec::new();
for signum in 1..max_signum {
if unsafe { libc::sigismember(&mask, signum as i32) } == 1 {
signals.push(signum as i32);
}
}
Ok(signals)
}
#[cfg(unix)]
pub fn sigset_contains(mask: libc::sigset_t, signum: i32) -> bool {
unsafe { libc::sigismember(&mask, signum) == 1 }
}
#[cfg(windows)]
pub fn valid_signals(_max_signum: usize) -> io::Result<Vec<i32>> {
Ok(VALID_SIGNALS.to_vec())
}