| 1 | // Copyright (c) 2017 CtrlC developers | 
| 2 | // Licensed under the Apache License, Version 2.0 | 
|---|
| 3 | // <LICENSE-APACHE or | 
|---|
| 4 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT | 
|---|
| 5 | // license <LICENSE-MIT or http://opensource.org/licenses/MIT>, | 
|---|
| 6 | // at your option. All files in the project carrying such | 
|---|
| 7 | // notice may not be copied, modified, or distributed except | 
|---|
| 8 | // according to those terms. | 
|---|
| 9 |  | 
|---|
| 10 | use crate::error::Error as CtrlcError; | 
|---|
| 11 | use nix::unistd; | 
|---|
| 12 | use std::os::fd::BorrowedFd; | 
|---|
| 13 | use std::os::fd::IntoRawFd; | 
|---|
| 14 | use std::os::unix::io::RawFd; | 
|---|
| 15 |  | 
|---|
| 16 | static mut PIPE: (RawFd, RawFd) = (-1, -1); | 
|---|
| 17 |  | 
|---|
| 18 | /// Platform specific error type | 
|---|
| 19 | pub type Error = nix::Error; | 
|---|
| 20 |  | 
|---|
| 21 | /// Platform specific signal type | 
|---|
| 22 | pub type Signal = nix::sys::signal::Signal; | 
|---|
| 23 |  | 
|---|
| 24 | extern "C"fn os_handler(_: nix::libc::c_int) { | 
|---|
| 25 | // Assuming this always succeeds. Can't really handle errors in any meaningful way. | 
|---|
| 26 | unsafe { | 
|---|
| 27 | let fd: BorrowedFd<'_> = BorrowedFd::borrow_raw(fd:PIPE.1); | 
|---|
| 28 | let _ = unistd::write(fd, &[0u8]); | 
|---|
| 29 | } | 
|---|
| 30 | } | 
|---|
| 31 |  | 
|---|
| 32 | // pipe2(2) is not available on macOS, iOS, AIX or Haiku, so we need to use pipe(2) and fcntl(2) | 
|---|
| 33 | #[ inline] | 
|---|
| 34 | #[ cfg(any( | 
|---|
| 35 | target_os = "ios", | 
|---|
| 36 | target_os = "macos", | 
|---|
| 37 | target_os = "haiku", | 
|---|
| 38 | target_os = "aix", | 
|---|
| 39 | target_os = "nto", | 
|---|
| 40 | ))] | 
|---|
| 41 | fn pipe2(flags: nix::fcntl::OFlag) -> nix::Result<(RawFd, RawFd)> { | 
|---|
| 42 | use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag}; | 
|---|
| 43 |  | 
|---|
| 44 | let pipe = unistd::pipe()?; | 
|---|
| 45 | let pipe = (pipe.0.into_raw_fd(), pipe.1.into_raw_fd()); | 
|---|
| 46 |  | 
|---|
| 47 | let mut res = Ok(0); | 
|---|
| 48 |  | 
|---|
| 49 | if flags.contains(OFlag::O_CLOEXEC) { | 
|---|
| 50 | res = res | 
|---|
| 51 | .and_then(|_| fcntl(pipe.0, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC))) | 
|---|
| 52 | .and_then(|_| fcntl(pipe.1, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC))); | 
|---|
| 53 | } | 
|---|
| 54 |  | 
|---|
| 55 | if flags.contains(OFlag::O_NONBLOCK) { | 
|---|
| 56 | res = res | 
|---|
| 57 | .and_then(|_| fcntl(pipe.0, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))) | 
|---|
| 58 | .and_then(|_| fcntl(pipe.1, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))); | 
|---|
| 59 | } | 
|---|
| 60 |  | 
|---|
| 61 | match res { | 
|---|
| 62 | Ok(_) => Ok(pipe), | 
|---|
| 63 | Err(e) => { | 
|---|
| 64 | let _ = unistd::close(pipe.0); | 
|---|
| 65 | let _ = unistd::close(pipe.1); | 
|---|
| 66 | Err(e) | 
|---|
| 67 | } | 
|---|
| 68 | } | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | #[ inline] | 
|---|
| 72 | #[ cfg(not(any( | 
|---|
| 73 | target_os = "ios", | 
|---|
| 74 | target_os = "macos", | 
|---|
| 75 | target_os = "haiku", | 
|---|
| 76 | target_os = "aix", | 
|---|
| 77 | target_os = "nto", | 
|---|
| 78 | )))] | 
|---|
| 79 | fn pipe2(flags: nix::fcntl::OFlag) -> nix::Result<(RawFd, RawFd)> { | 
|---|
| 80 | let pipe: (OwnedFd, OwnedFd) = unistd::pipe2(flags)?; | 
|---|
| 81 | Ok((pipe.0.into_raw_fd(), pipe.1.into_raw_fd())) | 
|---|
| 82 | } | 
|---|
| 83 |  | 
|---|
| 84 | /// Register os signal handler. | 
|---|
| 85 | /// | 
|---|
| 86 | /// Must be called before calling [`block_ctrl_c()`](fn.block_ctrl_c.html) | 
|---|
| 87 | /// and should only be called once. | 
|---|
| 88 | /// | 
|---|
| 89 | /// # Errors | 
|---|
| 90 | /// Will return an error if a system error occurred. | 
|---|
| 91 | /// | 
|---|
| 92 | #[ inline] | 
|---|
| 93 | pub unsafe fn init_os_handler(overwrite: bool) -> Result<(), Error> { | 
|---|
| 94 | use nix::fcntl; | 
|---|
| 95 | use nix::sys::signal; | 
|---|
| 96 |  | 
|---|
| 97 | PIPE = pipe2(fcntl::OFlag::O_CLOEXEC)?; | 
|---|
| 98 |  | 
|---|
| 99 | let close_pipe = |e: nix::Error| -> Error { | 
|---|
| 100 | // Try to close the pipes. close() should not fail, | 
|---|
| 101 | // but if it does, there isn't much we can do | 
|---|
| 102 | let _ = unistd::close(PIPE.1); | 
|---|
| 103 | let _ = unistd::close(PIPE.0); | 
|---|
| 104 | e | 
|---|
| 105 | }; | 
|---|
| 106 |  | 
|---|
| 107 | // Make sure we never block on write in the os handler. | 
|---|
| 108 | if let Err(e) = fcntl::fcntl(PIPE.1, fcntl::FcntlArg::F_SETFL(fcntl::OFlag::O_NONBLOCK)) { | 
|---|
| 109 | return Err(close_pipe(e)); | 
|---|
| 110 | } | 
|---|
| 111 |  | 
|---|
| 112 | let handler = signal::SigHandler::Handler(os_handler); | 
|---|
| 113 | #[ cfg(not(target_os = "nto"))] | 
|---|
| 114 | let new_action = signal::SigAction::new( | 
|---|
| 115 | handler, | 
|---|
| 116 | signal::SaFlags::SA_RESTART, | 
|---|
| 117 | signal::SigSet::empty(), | 
|---|
| 118 | ); | 
|---|
| 119 | // SA_RESTART is not supported on QNX Neutrino 7.1 and before | 
|---|
| 120 | #[ cfg(target_os = "nto")] | 
|---|
| 121 | let new_action = | 
|---|
| 122 | signal::SigAction::new(handler, signal::SaFlags::empty(), signal::SigSet::empty()); | 
|---|
| 123 |  | 
|---|
| 124 | let sigint_old = match signal::sigaction(signal::Signal::SIGINT, &new_action) { | 
|---|
| 125 | Ok(old) => old, | 
|---|
| 126 | Err(e) => return Err(close_pipe(e)), | 
|---|
| 127 | }; | 
|---|
| 128 | if !overwrite && sigint_old.handler() != signal::SigHandler::SigDfl { | 
|---|
| 129 | signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap(); | 
|---|
| 130 | return Err(close_pipe(nix::Error::EEXIST)); | 
|---|
| 131 | } | 
|---|
| 132 |  | 
|---|
| 133 | #[ cfg(feature = "termination")] | 
|---|
| 134 | { | 
|---|
| 135 | let sigterm_old = match signal::sigaction(signal::Signal::SIGTERM, &new_action) { | 
|---|
| 136 | Ok(old) => old, | 
|---|
| 137 | Err(e) => { | 
|---|
| 138 | signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap(); | 
|---|
| 139 | return Err(close_pipe(e)); | 
|---|
| 140 | } | 
|---|
| 141 | }; | 
|---|
| 142 | if !overwrite && sigterm_old.handler() != signal::SigHandler::SigDfl { | 
|---|
| 143 | signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap(); | 
|---|
| 144 | signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap(); | 
|---|
| 145 | return Err(close_pipe(nix::Error::EEXIST)); | 
|---|
| 146 | } | 
|---|
| 147 | let sighup_old = match signal::sigaction(signal::Signal::SIGHUP, &new_action) { | 
|---|
| 148 | Ok(old) => old, | 
|---|
| 149 | Err(e) => { | 
|---|
| 150 | signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap(); | 
|---|
| 151 | signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap(); | 
|---|
| 152 | return Err(close_pipe(e)); | 
|---|
| 153 | } | 
|---|
| 154 | }; | 
|---|
| 155 | if !overwrite && sighup_old.handler() != signal::SigHandler::SigDfl { | 
|---|
| 156 | signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap(); | 
|---|
| 157 | signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap(); | 
|---|
| 158 | signal::sigaction(signal::Signal::SIGHUP, &sighup_old).unwrap(); | 
|---|
| 159 | return Err(close_pipe(nix::Error::EEXIST)); | 
|---|
| 160 | } | 
|---|
| 161 | } | 
|---|
| 162 |  | 
|---|
| 163 | Ok(()) | 
|---|
| 164 | } | 
|---|
| 165 |  | 
|---|
| 166 | /// Blocks until a Ctrl-C signal is received. | 
|---|
| 167 | /// | 
|---|
| 168 | /// Must be called after calling [`init_os_handler()`](fn.init_os_handler.html). | 
|---|
| 169 | /// | 
|---|
| 170 | /// # Errors | 
|---|
| 171 | /// Will return an error if a system error occurred. | 
|---|
| 172 | /// | 
|---|
| 173 | #[ inline] | 
|---|
| 174 | pub unsafe fn block_ctrl_c() -> Result<(), CtrlcError> { | 
|---|
| 175 | use std::io; | 
|---|
| 176 | let mut buf: [u8; 1] = [0u8]; | 
|---|
| 177 |  | 
|---|
| 178 | // TODO: Can we safely convert the pipe fd into a std::io::Read | 
|---|
| 179 | // with std::os::unix::io::FromRawFd, this would handle EINTR | 
|---|
| 180 | // and everything for us. | 
|---|
| 181 | loop { | 
|---|
| 182 | match unistd::read(fd:PIPE.0, &mut buf[..]) { | 
|---|
| 183 | Ok(1) => break, | 
|---|
| 184 | Ok(_) => return Err(CtrlcError::System(io::ErrorKind::UnexpectedEof.into())), | 
|---|
| 185 | Err(nix::errno::Errno::EINTR) => {} | 
|---|
| 186 | Err(e: Errno) => return Err(e.into()), | 
|---|
| 187 | } | 
|---|
| 188 | } | 
|---|
| 189 |  | 
|---|
| 190 | Ok(()) | 
|---|
| 191 | } | 
|---|
| 192 |  | 
|---|