ctrlc/platform/unix/
mod.rs

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
10use crate::error::Error as CtrlcError;
11
12#[cfg(not(target_vendor = "apple"))]
13#[allow(static_mut_refs)] // rust-version = "1.69.0"
14mod implementation {
15    static mut SEMAPHORE: nix::libc::sem_t = unsafe { std::mem::zeroed() };
16    const SEM_THREAD_SHARED: nix::libc::c_int = 0;
17
18    pub unsafe fn sem_init() {
19        nix::libc::sem_init(&mut SEMAPHORE as *mut _, SEM_THREAD_SHARED, 0);
20    }
21
22    pub unsafe fn sem_post() {
23        // No errors apply. EOVERFLOW is hypothetically possible but it's equivalent to a success for our oneshot use-case.
24        let _ = nix::libc::sem_post(&mut SEMAPHORE as *mut _);
25    }
26
27    pub unsafe fn sem_wait_forever() {
28        // The only realistic error is EINTR, which is restartable.
29        while nix::libc::sem_wait(&mut SEMAPHORE as *mut _) == -1 {}
30    }
31}
32
33#[cfg(target_vendor = "apple")]
34mod implementation {
35    static mut SEMAPHORE: dispatch::ffi::dispatch_semaphore_t = std::ptr::null_mut();
36
37    pub unsafe fn sem_init() {
38        SEMAPHORE = dispatch::ffi::dispatch_semaphore_create(0);
39    }
40
41    pub unsafe fn sem_post() {
42        dispatch::ffi::dispatch_semaphore_signal(SEMAPHORE);
43    }
44
45    pub unsafe fn sem_wait_forever() {
46        dispatch::ffi::dispatch_semaphore_wait(SEMAPHORE, dispatch::ffi::DISPATCH_TIME_FOREVER);
47    }
48}
49
50/// Platform specific error type
51pub type Error = nix::Error;
52
53/// Platform specific signal type
54pub type Signal = nix::sys::signal::Signal;
55
56extern "C" fn os_handler(_: nix::libc::c_int) {
57    unsafe {
58        implementation::sem_post();
59    }
60}
61
62/// Register os signal handler.
63///
64/// Must be called before calling [`block_ctrl_c()`](fn.block_ctrl_c.html)
65/// and should only be called once.
66///
67/// # Errors
68/// Will return an error if a system error occurred.
69///
70#[inline]
71pub unsafe fn init_os_handler(overwrite: bool) -> Result<(), Error> {
72    use nix::sys::signal;
73
74    implementation::sem_init();
75
76    let handler = signal::SigHandler::Handler(os_handler);
77    #[cfg(not(target_os = "nto"))]
78    let new_action = signal::SigAction::new(
79        handler,
80        signal::SaFlags::SA_RESTART,
81        signal::SigSet::empty(),
82    );
83    // SA_RESTART is not supported on QNX Neutrino 7.1 and before
84    #[cfg(target_os = "nto")]
85    let new_action =
86        signal::SigAction::new(handler, signal::SaFlags::empty(), signal::SigSet::empty());
87
88    let sigint_old = signal::sigaction(signal::Signal::SIGINT, &new_action)?;
89    if !overwrite && sigint_old.handler() != signal::SigHandler::SigDfl {
90        signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
91        return Err(nix::Error::EEXIST);
92    }
93
94    #[cfg(feature = "termination")]
95    {
96        let sigterm_old = match signal::sigaction(signal::Signal::SIGTERM, &new_action) {
97            Ok(old) => old,
98            Err(e) => {
99                signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
100                return Err(e);
101            }
102        };
103        if !overwrite && sigterm_old.handler() != signal::SigHandler::SigDfl {
104            signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
105            signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
106            return Err(nix::Error::EEXIST);
107        }
108        let sighup_old = match signal::sigaction(signal::Signal::SIGHUP, &new_action) {
109            Ok(old) => old,
110            Err(e) => {
111                signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
112                signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
113                return Err(e);
114            }
115        };
116        if !overwrite && sighup_old.handler() != signal::SigHandler::SigDfl {
117            signal::sigaction(signal::Signal::SIGINT, &sigint_old).unwrap();
118            signal::sigaction(signal::Signal::SIGTERM, &sigterm_old).unwrap();
119            signal::sigaction(signal::Signal::SIGHUP, &sighup_old).unwrap();
120            return Err(nix::Error::EEXIST);
121        }
122    }
123
124    Ok(())
125}
126
127/// Blocks until a Ctrl-C signal is received.
128///
129/// Must be called after calling [`init_os_handler()`](fn.init_os_handler.html).
130///
131/// # Errors
132/// None.
133///
134#[inline]
135pub unsafe fn block_ctrl_c() -> Result<(), CtrlcError> {
136    implementation::sem_wait_forever();
137    Ok(())
138}