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