ctrlc_async/platform/unix/
mod.rs1use crate::error::Error as CtrlcError;
11use nix::unistd;
12use std::os::unix::io::RawFd;
13use std::future::Future;
14
15static mut PIPE: (RawFd, RawFd) = (-1, -1);
16
17pub type Error = nix::Error;
19
20pub type Signal = nix::sys::signal::Signal;
22
23extern "C" fn os_handler(_: nix::libc::c_int) {
24 unsafe {
26 let _ = unistd::write(PIPE.1, &[0u8]);
27 }
28}
29
30#[inline]
32#[cfg(any(target_os = "ios", target_os = "macos"))]
33fn pipe2(flags: nix::fcntl::OFlag) -> nix::Result<(RawFd, RawFd)> {
34 use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
35
36 let pipe = unistd::pipe()?;
37
38 let mut res = Ok(0);
39
40 if flags.contains(OFlag::O_CLOEXEC) {
41 res = res
42 .and_then(|_| fcntl(pipe.0, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)))
43 .and_then(|_| fcntl(pipe.1, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)));
44 }
45
46 if flags.contains(OFlag::O_NONBLOCK) {
47 res = res
48 .and_then(|_| fcntl(pipe.0, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)))
49 .and_then(|_| fcntl(pipe.1, FcntlArg::F_SETFL(OFlag::O_NONBLOCK)));
50 }
51
52 match res {
53 Ok(_) => Ok(pipe),
54 Err(e) => {
55 let _ = unistd::close(pipe.0);
56 let _ = unistd::close(pipe.1);
57 Err(e)
58 }
59 }
60}
61
62#[inline]
63#[cfg(not(any(target_os = "ios", target_os = "macos")))]
64fn pipe2(flags: nix::fcntl::OFlag) -> nix::Result<(RawFd, RawFd)> {
65 unistd::pipe2(flags)
66}
67
68#[inline]
77pub unsafe fn init_os_handler() -> Result<impl Future<Output=Result<(), CtrlcError>>, Error>
78{
79 use nix::fcntl;
80 use nix::sys::signal;
81
82 PIPE = pipe2(fcntl::OFlag::O_CLOEXEC)?;
83
84 let close_pipe = |e: nix::Error| -> Error {
85 let _ = unistd::close(PIPE.1);
88 let _ = unistd::close(PIPE.0);
89 e
90 };
91
92 if let Err(e) = fcntl::fcntl(PIPE.1, fcntl::FcntlArg::F_SETFL(fcntl::OFlag::O_NONBLOCK)) {
94 return Err(close_pipe(e));
95 }
96
97 let handler = signal::SigHandler::Handler(os_handler);
98 let new_action = signal::SigAction::new(
99 handler,
100 signal::SaFlags::SA_RESTART,
101 signal::SigSet::empty(),
102 );
103
104 #[allow(unused_variables)]
105 let sigint_old = match signal::sigaction(signal::Signal::SIGINT, &new_action) {
106 Ok(old) => old,
107 Err(e) => return Err(close_pipe(e)),
108 };
109
110 #[cfg(feature = "termination")]
111 {
112 let sigterm_old = match signal::sigaction(signal::Signal::SIGTERM, &new_action) {
113 Ok(old) => old,
114 Err(e) => {
115 signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
116 return Err(close_pipe(e));
117 }
118 };
119 match signal::sigaction(signal::Signal::SIGHUP, &new_action) {
120 Ok(_) => {}
121 Err(e) => {
122 signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
123 signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
124 return Err(close_pipe(e));
125 }
126 }
127 }
128
129 Ok(
130 async move {
131 use std::io;
132 use nix::sys::aio::AioCb;
133 use nix::sys::aio::LioOpcode;
134 use nix::sys::signal::SigevNotify;
135 let mut buf = [0u8];
136
137 loop {
141 let mut aio = AioCb::from_mut_slice( PIPE.0, 0, &mut buf[..], 0, SigevNotify::SigevNone, LioOpcode::LIO_NOP);
142 aio.read()?;
143 while aio.error() == Err(nix::errno::Errno::EINPROGRESS) {
144 #[cfg(feature = "tokio")]
145 tokio::time::sleep(std::time::Duration::from_millis(10)).await;
146 #[cfg(not(feature = "tokio"))]
147 std::thread::sleep(std::time::Duration::from_millis(10));
148 }
149 match aio.aio_return() {
150 Ok(1) => break,
151 Ok(_) => {
152 return Err(CtrlcError::System(io::ErrorKind::UnexpectedEof.into()))
153 },
154 Err(nix::errno::Errno::EINTR) => {}
155 Err(e) => return Err(e.into()),
156 }
157 }
158 Ok(())
159 }
160 )
161}