ctrlc/platform/unix/
mod.rsuse crate::error::Error as CtrlcError;
use nix::unistd;
use std::os::fd::BorrowedFd;
use std::os::fd::IntoRawFd;
use std::os::unix::io::RawFd;
static mut PIPE: (RawFd, RawFd) = (-1, -1);
pub type Error = nix::Error;
pub type Signal = nix::sys::signal::Signal;
extern "C" fn os_handler(_: nix::libc::c_int) {
unsafe {
let fd = BorrowedFd::borrow_raw(PIPE.1);
let _ = unistd::write(fd, &[0u8]);
}
}
#[inline]
#[cfg(any(
target_os = "ios",
target_os = "macos",
target_os = "haiku",
target_os = "aix",
target_os = "nto",
))]
fn pipe2(flags: nix::fcntl::OFlag) -> nix::Result<(RawFd, RawFd)> {
use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
let pipe = unistd::pipe()?;
let pipe = (pipe.0.into_raw_fd(), pipe.1.into_raw_fd());
let mut res = Ok(0);
if flags.contains(OFlag::O_CLOEXEC) {
res = res
.and_then(|_| fcntl(pipe.0, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)))
.and_then(|_| fcntl(pipe.1, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)));
}
if flags.contains(OFlag::O_NONBLOCK) {
res = res
.and_then(|_| fcntl(pipe.0, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)))
.and_then(|_| fcntl(pipe.1, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)));
}
match res {
Ok(_) => Ok(pipe),
Err(e) => {
let _ = unistd::close(pipe.0);
let _ = unistd::close(pipe.1);
Err(e)
}
}
}
#[inline]
#[cfg(not(any(
target_os = "ios",
target_os = "macos",
target_os = "haiku",
target_os = "aix",
target_os = "nto",
)))]
fn pipe2(flags: nix::fcntl::OFlag) -> nix::Result<(RawFd, RawFd)> {
let pipe = unistd::pipe2(flags)?;
Ok((pipe.0.into_raw_fd(), pipe.1.into_raw_fd()))
}
#[inline]
pub unsafe fn init_os_handler(overwrite: bool) -> Result<(), Error> {
use nix::fcntl;
use nix::sys::signal;
PIPE = pipe2(fcntl::OFlag::O_CLOEXEC)?;
let close_pipe = |e: nix::Error| -> Error {
let _ = unistd::close(PIPE.1);
let _ = unistd::close(PIPE.0);
e
};
if let Err(e) = fcntl::fcntl(PIPE.1, fcntl::FcntlArg::F_SETFL(fcntl::OFlag::O_NONBLOCK)) {
return Err(close_pipe(e));
}
let handler = signal::SigHandler::Handler(os_handler);
#[cfg(not(target_os = "nto"))]
let new_action = signal::SigAction::new(
handler,
signal::SaFlags::SA_RESTART,
signal::SigSet::empty(),
);
#[cfg(target_os = "nto")]
let new_action =
signal::SigAction::new(handler, signal::SaFlags::empty(), signal::SigSet::empty());
let sigint_old = match signal::sigaction(signal::Signal::SIGINT, &new_action) {
Ok(old) => old,
Err(e) => return Err(close_pipe(e)),
};
if !overwrite && sigint_old.handler() != signal::SigHandler::SigDfl {
signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
return Err(close_pipe(nix::Error::EEXIST));
}
#[cfg(feature = "termination")]
{
let sigterm_old = match signal::sigaction(signal::Signal::SIGTERM, &new_action) {
Ok(old) => old,
Err(e) => {
signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
return Err(close_pipe(e));
}
};
if !overwrite && sigterm_old.handler() != signal::SigHandler::SigDfl {
signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
return Err(close_pipe(nix::Error::EEXIST));
}
let sighup_old = match signal::sigaction(signal::Signal::SIGHUP, &new_action) {
Ok(old) => old,
Err(e) => {
signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
return Err(close_pipe(e));
}
};
if !overwrite && sighup_old.handler() != signal::SigHandler::SigDfl {
signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
signal::sigaction(signal::Signal::SIGHUP, &sighup_old).unwrap();
return Err(close_pipe(nix::Error::EEXIST));
}
}
Ok(())
}
#[inline]
pub unsafe fn block_ctrl_c() -> Result<(), CtrlcError> {
use std::io;
let mut buf = [0u8];
loop {
match unistd::read(PIPE.0, &mut buf[..]) {
Ok(1) => break,
Ok(_) => return Err(CtrlcError::System(io::ErrorKind::UnexpectedEof.into())),
Err(nix::errno::Errno::EINTR) => {}
Err(e) => return Err(e.into()),
}
}
Ok(())
}