sys_util/
signal.rs

1// Copyright 2017 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use libc::{
6    c_int, pthread_kill, pthread_sigmask, pthread_t, sigaction, sigaddset, sigemptyset, siginfo_t,
7    sigismember, sigpending, sigset_t, sigtimedwait, timespec, EAGAIN, EINTR, EINVAL, SA_RESTART,
8    SIG_BLOCK, SIG_UNBLOCK,
9};
10
11use std::mem;
12use std::os::unix::thread::JoinHandleExt;
13use std::ptr::{null, null_mut};
14use std::result;
15use std::thread::JoinHandle;
16
17use {errno, errno_result};
18
19#[derive(Debug)]
20pub enum Error {
21    /// Couldn't create a sigset.
22    CreateSigset(errno::Error),
23    /// The wrapped signal has already been blocked.
24    SignalAlreadyBlocked(c_int),
25    /// Failed to check if the requested signal is in the blocked set already.
26    CompareBlockedSignals(errno::Error),
27    /// The signal could not be blocked.
28    BlockSignal(errno::Error),
29    /// The signal mask could not be retrieved.
30    RetrieveSignalMask(i32),
31    /// The signal could not be unblocked.
32    UnblockSignal(errno::Error),
33    /// Failed to wait for given signal.
34    ClearWaitPending(errno::Error),
35    /// Failed to get pending signals.
36    ClearGetPending(errno::Error),
37    /// Failed to check if given signal is in the set of pending signals.
38    ClearCheckPending(errno::Error),
39}
40
41pub type SignalResult<T> = result::Result<T, Error>;
42
43#[link(name = "c")]
44extern "C" {
45    fn __libc_current_sigrtmin() -> c_int;
46    fn __libc_current_sigrtmax() -> c_int;
47}
48
49/// Returns the minimum (inclusive) real-time signal number.
50#[allow(non_snake_case)]
51pub fn SIGRTMIN() -> c_int {
52    unsafe { __libc_current_sigrtmin() }
53}
54
55/// Returns the maximum (inclusive) real-time signal number.
56#[allow(non_snake_case)]
57pub fn SIGRTMAX() -> c_int {
58    unsafe { __libc_current_sigrtmax() }
59}
60
61fn valid_signal_num(num: c_int) -> bool {
62    num >= SIGRTMIN() && num <= SIGRTMAX()
63}
64
65/// Registers `handler` as the signal handler of signum `num`.
66///
67/// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range.
68///
69/// This is considered unsafe because the given handler will be called asynchronously, interrupting
70/// whatever the thread was doing and therefore must only do async-signal-safe operations.
71pub unsafe fn register_signal_handler(
72    num: c_int,
73    handler: extern "C" fn() -> (),
74) -> errno::Result<()> {
75    if !valid_signal_num(num) {
76        return Err(errno::Error::new(EINVAL));
77    }
78
79    let mut sigact: sigaction = mem::zeroed();
80    sigact.sa_flags = SA_RESTART;
81    sigact.sa_sigaction = handler as *const () as usize;
82
83    let ret = sigaction(num, &sigact, null_mut());
84    if ret < 0 {
85        return errno_result();
86    }
87
88    Ok(())
89}
90
91/// Creates `sigset` from an array of signal numbers.
92///
93/// This is a helper function used when we want to manipulate signals.
94pub fn create_sigset(signals: &[c_int]) -> errno::Result<sigset_t> {
95    // sigset will actually be initialized by sigemptyset below.
96    let mut sigset: sigset_t = unsafe { mem::zeroed() };
97
98    // Safe - return value is checked.
99    let ret = unsafe { sigemptyset(&mut sigset) };
100    if ret < 0 {
101        return errno_result();
102    }
103
104    for signal in signals {
105        // Safe - return value is checked.
106        let ret = unsafe { sigaddset(&mut sigset, *signal) };
107        if ret < 0 {
108            return errno_result();
109        }
110    }
111
112    Ok(sigset)
113}
114
115/// Retrieves the signal mask of the current thread as a vector of c_ints.
116pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
117    let mut mask = Vec::new();
118
119    // Safe - return values are checked.
120    unsafe {
121        let mut old_sigset: sigset_t = mem::zeroed();
122        let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t);
123        if ret < 0 {
124            return Err(Error::RetrieveSignalMask(ret));
125        }
126
127        for num in 0..=SIGRTMAX() {
128            if sigismember(&old_sigset, num) > 0 {
129                mask.push(num);
130            }
131        }
132    }
133
134    Ok(mask)
135}
136
137/// Masks given signal.
138///
139/// If signal is already blocked the call will fail with Error::SignalAlreadyBlocked
140/// result.
141pub fn block_signal(num: c_int) -> SignalResult<()> {
142    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
143
144    // Safe - return values are checked.
145    unsafe {
146        let mut old_sigset: sigset_t = mem::zeroed();
147        let ret = pthread_sigmask(SIG_BLOCK, &sigset, &mut old_sigset as *mut sigset_t);
148        if ret < 0 {
149            return Err(Error::BlockSignal(errno::Error::last()));
150        }
151        let ret = sigismember(&old_sigset, num);
152        if ret < 0 {
153            return Err(Error::CompareBlockedSignals(errno::Error::last()));
154        } else if ret > 0 {
155            return Err(Error::SignalAlreadyBlocked(num));
156        }
157    }
158    Ok(())
159}
160
161/// Unmasks given signal.
162pub fn unblock_signal(num: c_int) -> SignalResult<()> {
163    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
164
165    // Safe - return value is checked.
166    let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
167    if ret < 0 {
168        return Err(Error::UnblockSignal(errno::Error::last()));
169    }
170    Ok(())
171}
172
173/// Clears pending signal.
174pub fn clear_signal(num: c_int) -> SignalResult<()> {
175    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
176
177    while {
178        // This is safe as we are rigorously checking return values
179        // of libc calls.
180        unsafe {
181            let mut siginfo: siginfo_t = mem::zeroed();
182            let ts = timespec {
183                tv_sec: 0,
184                tv_nsec: 0,
185            };
186            // Attempt to consume one instance of pending signal. If signal
187            // is not pending, the call will fail with EAGAIN or EINTR.
188            let ret = sigtimedwait(&sigset, &mut siginfo, &ts);
189            if ret < 0 {
190                let e = errno::Error::last();
191                match e.errno() {
192                    EAGAIN | EINTR => {}
193                    _ => {
194                        return Err(Error::ClearWaitPending(errno::Error::last()));
195                    }
196                }
197            }
198
199            // This sigset will be actually filled with `sigpending` call.
200            let mut chkset: sigset_t = mem::zeroed();
201            // See if more instances of the signal are pending.
202            let ret = sigpending(&mut chkset);
203            if ret < 0 {
204                return Err(Error::ClearGetPending(errno::Error::last()));
205            }
206
207            let ret = sigismember(&chkset, num);
208            if ret < 0 {
209                return Err(Error::ClearCheckPending(errno::Error::last()));
210            }
211
212            // This is do-while loop condition.
213            ret != 0
214        }
215    } {}
216
217    Ok(())
218}
219
220/// Trait for threads that can be signalled via `pthread_kill`.
221///
222/// Note that this is only useful for signals between SIGRTMIN and SIGRTMAX because these are
223/// guaranteed to not be used by the C runtime.
224///
225/// This is marked unsafe because the implementation of this trait must guarantee that the returned
226/// pthread_t is valid and has a lifetime at least that of the trait object.
227pub unsafe trait Killable {
228    fn pthread_handle(&self) -> pthread_t;
229
230    /// Sends the signal `num` to this killable thread.
231    ///
232    /// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range.
233    fn kill(&self, num: c_int) -> errno::Result<()> {
234        if !valid_signal_num(num) {
235            return Err(errno::Error::new(EINVAL));
236        }
237
238        // Safe because we ensure we are using a valid pthread handle, a valid signal number, and
239        // check the return result.
240        let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
241        if ret < 0 {
242            return errno_result();
243        }
244        Ok(())
245    }
246}
247
248// Safe because we fulfill our contract of returning a genuine pthread handle.
249unsafe impl<T> Killable for JoinHandle<T> {
250    fn pthread_handle(&self) -> pthread_t {
251        self.as_pthread_t()
252    }
253}