base/sys/linux/
signal.rs

1// Copyright 2017 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::cmp::Ordering;
6use std::convert::TryFrom;
7use std::io;
8use std::mem;
9use std::os::unix::thread::JoinHandleExt;
10use std::ptr::null;
11use std::ptr::null_mut;
12use std::result;
13use std::thread::JoinHandle;
14use std::time::Duration;
15
16use libc::c_int;
17use libc::pthread_kill;
18use libc::pthread_sigmask;
19use libc::pthread_t;
20use libc::sigaction;
21use libc::sigaddset;
22use libc::sigemptyset;
23use libc::siginfo_t;
24use libc::sigismember;
25use libc::sigpending;
26use libc::sigset_t;
27use libc::sigtimedwait;
28use libc::sigwait;
29use libc::timespec;
30use libc::EAGAIN;
31use libc::EINTR;
32use libc::EINVAL;
33use libc::SA_RESTART;
34use libc::SIG_BLOCK;
35use libc::SIG_DFL;
36use libc::SIG_UNBLOCK;
37use remain::sorted;
38use thiserror::Error;
39
40use super::errno_result;
41use super::Error as ErrnoError;
42use super::Pid;
43use super::Result;
44use crate::handle_eintr_errno;
45use crate::handle_eintr_rc;
46use crate::unix::duration_to_timespec;
47
48#[sorted]
49#[derive(Error, Debug)]
50pub enum Error {
51    /// The signal could not be blocked.
52    #[error("signal could not be blocked: {0}")]
53    BlockSignal(ErrnoError),
54    /// Failed to check if given signal is in the set of pending signals.
55    #[error("failed to check whether given signal is in the pending set: {0}")]
56    ClearCheckPending(ErrnoError),
57    /// Failed to get pending signals.
58    #[error("failed to get pending signals: {0}")]
59    ClearGetPending(ErrnoError),
60    /// Failed to wait for given signal.
61    #[error("failed to wait for given signal: {0}")]
62    ClearWaitPending(ErrnoError),
63    /// Failed to check if the requested signal is in the blocked set already.
64    #[error("failed to check whether requested signal is in the blocked set: {0}")]
65    CompareBlockedSignals(ErrnoError),
66    /// Couldn't create a sigset.
67    #[error("couldn't create a sigset: {0}")]
68    CreateSigset(ErrnoError),
69    /// Failed to get session id.
70    #[error("failed to get session id: {0}")]
71    GetSid(ErrnoError),
72    /// Failed to send signal to pid.
73    #[error("failed to send signal: {0}")]
74    Kill(ErrnoError),
75    /// The signal mask could not be retrieved.
76    #[error("failed to retrieve signal mask: {}", io::Error::from_raw_os_error(*.0))]
77    RetrieveSignalMask(i32),
78    /// Converted signum greater than SIGRTMAX.
79    #[error("got RT signal greater than max: {0:?}")]
80    RtSignumGreaterThanMax(Signal),
81    /// The wrapped signal has already been blocked.
82    #[error("signal {0} already blocked")]
83    SignalAlreadyBlocked(c_int),
84    /// Timeout reached.
85    #[error("timeout reached.")]
86    TimedOut,
87    /// The signal could not be unblocked.
88    #[error("signal could not be unblocked: {0}")]
89    UnblockSignal(ErrnoError),
90    /// Failed to convert signum to Signal.
91    #[error("unrecoginized signal number: {0}")]
92    UnrecognizedSignum(i32),
93    /// Failed to wait for signal.
94    #[error("failed to wait for signal: {0}")]
95    WaitForSignal(ErrnoError),
96    /// Failed to wait for pid.
97    #[error("failed to wait for process: {0}")]
98    WaitPid(ErrnoError),
99}
100
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
102#[repr(i32)]
103pub enum Signal {
104    Abort = libc::SIGABRT,
105    Alarm = libc::SIGALRM,
106    Bus = libc::SIGBUS,
107    Child = libc::SIGCHLD,
108    Continue = libc::SIGCONT,
109    ExceededFileSize = libc::SIGXFSZ,
110    FloatingPointException = libc::SIGFPE,
111    HangUp = libc::SIGHUP,
112    IllegalInstruction = libc::SIGILL,
113    Interrupt = libc::SIGINT,
114    Io = libc::SIGIO,
115    Kill = libc::SIGKILL,
116    Pipe = libc::SIGPIPE,
117    Power = libc::SIGPWR,
118    Profile = libc::SIGPROF,
119    Quit = libc::SIGQUIT,
120    SegmentationViolation = libc::SIGSEGV,
121    StackFault = libc::SIGSTKFLT,
122    Stop = libc::SIGSTOP,
123    Sys = libc::SIGSYS,
124    Trap = libc::SIGTRAP,
125    Terminate = libc::SIGTERM,
126    TtyIn = libc::SIGTTIN,
127    TtyOut = libc::SIGTTOU,
128    TtyStop = libc::SIGTSTP,
129    Urgent = libc::SIGURG,
130    User1 = libc::SIGUSR1,
131    User2 = libc::SIGUSR2,
132    VtAlarm = libc::SIGVTALRM,
133    Winch = libc::SIGWINCH,
134    Xcpu = libc::SIGXCPU,
135    // Rt signal numbers are be adjusted in the conversion to integer.
136    Rt0 = libc::SIGSYS + 1,
137    Rt1,
138    Rt2,
139    Rt3,
140    Rt4,
141    Rt5,
142    Rt6,
143    Rt7,
144    // Only 8 are guaranteed by POSIX, Linux has 32, but only 29 or 30 are usable.
145    Rt8,
146    Rt9,
147    Rt10,
148    Rt11,
149    Rt12,
150    Rt13,
151    Rt14,
152    Rt15,
153    Rt16,
154    Rt17,
155    Rt18,
156    Rt19,
157    Rt20,
158    Rt21,
159    Rt22,
160    Rt23,
161    Rt24,
162    Rt25,
163    Rt26,
164    Rt27,
165    Rt28,
166    Rt29,
167    Rt30,
168    Rt31,
169}
170
171impl std::fmt::Display for Signal {
172    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
173        let n = i32::from(*self);
174        // SAFETY: Trivially safe.
175        let ptr = unsafe { libc::strsignal(n) };
176        if ptr.is_null() {
177            f.write_str("[null]")
178        } else {
179            // SAFETY: `ptr` is a valid non-null c string returned by `strsignal`. It is only valid
180            // until the next call to `strsignal`, so we immediately make a copy. Technically
181            // `strsignal` is documented not threadsafe because the string becomes invalid when the
182            // locale changes in some implementations, but there doesn't seem to be a standard
183            // alternative.
184            let s = unsafe { std::ffi::CStr::from_ptr(ptr) }
185                .to_str()
186                .unwrap()
187                .to_string();
188            f.write_str(&s)
189        }
190    }
191}
192
193impl From<Signal> for c_int {
194    fn from(signal: Signal) -> c_int {
195        let num = signal as libc::c_int;
196        if num >= Signal::Rt0 as libc::c_int {
197            return num - (Signal::Rt0 as libc::c_int) + SIGRTMIN();
198        }
199        num
200    }
201}
202
203impl TryFrom<c_int> for Signal {
204    type Error = Error;
205
206    fn try_from(value: c_int) -> result::Result<Self, Self::Error> {
207        use Signal::*;
208
209        Ok(match value {
210            libc::SIGABRT => Abort,
211            libc::SIGALRM => Alarm,
212            libc::SIGBUS => Bus,
213            libc::SIGCHLD => Child,
214            libc::SIGCONT => Continue,
215            libc::SIGXFSZ => ExceededFileSize,
216            libc::SIGFPE => FloatingPointException,
217            libc::SIGHUP => HangUp,
218            libc::SIGILL => IllegalInstruction,
219            libc::SIGINT => Interrupt,
220            libc::SIGIO => Io,
221            libc::SIGKILL => Kill,
222            libc::SIGPIPE => Pipe,
223            libc::SIGPWR => Power,
224            libc::SIGPROF => Profile,
225            libc::SIGQUIT => Quit,
226            libc::SIGSEGV => SegmentationViolation,
227            libc::SIGSTKFLT => StackFault,
228            libc::SIGSTOP => Stop,
229            libc::SIGSYS => Sys,
230            libc::SIGTRAP => Trap,
231            libc::SIGTERM => Terminate,
232            libc::SIGTTIN => TtyIn,
233            libc::SIGTTOU => TtyOut,
234            libc::SIGTSTP => TtyStop,
235            libc::SIGURG => Urgent,
236            libc::SIGUSR1 => User1,
237            libc::SIGUSR2 => User2,
238            libc::SIGVTALRM => VtAlarm,
239            libc::SIGWINCH => Winch,
240            libc::SIGXCPU => Xcpu,
241            _ => {
242                if value < SIGRTMIN() {
243                    return Err(Error::UnrecognizedSignum(value));
244                }
245                let signal = match value - SIGRTMIN() {
246                    0 => Rt0,
247                    1 => Rt1,
248                    2 => Rt2,
249                    3 => Rt3,
250                    4 => Rt4,
251                    5 => Rt5,
252                    6 => Rt6,
253                    7 => Rt7,
254                    8 => Rt8,
255                    9 => Rt9,
256                    10 => Rt10,
257                    11 => Rt11,
258                    12 => Rt12,
259                    13 => Rt13,
260                    14 => Rt14,
261                    15 => Rt15,
262                    16 => Rt16,
263                    17 => Rt17,
264                    18 => Rt18,
265                    19 => Rt19,
266                    20 => Rt20,
267                    21 => Rt21,
268                    22 => Rt22,
269                    23 => Rt23,
270                    24 => Rt24,
271                    25 => Rt25,
272                    26 => Rt26,
273                    27 => Rt27,
274                    28 => Rt28,
275                    29 => Rt29,
276                    30 => Rt30,
277                    31 => Rt31,
278                    _ => {
279                        return Err(Error::UnrecognizedSignum(value));
280                    }
281                };
282                if value > SIGRTMAX() {
283                    return Err(Error::RtSignumGreaterThanMax(signal));
284                }
285                signal
286            }
287        })
288    }
289}
290
291pub type SignalResult<T> = result::Result<T, Error>;
292
293#[link(name = "c")]
294extern "C" {
295    fn __libc_current_sigrtmin() -> c_int;
296    fn __libc_current_sigrtmax() -> c_int;
297}
298
299/// Returns the minimum (inclusive) real-time signal number.
300#[allow(non_snake_case)]
301pub fn SIGRTMIN() -> c_int {
302    // SAFETY: trivially safe
303    unsafe { __libc_current_sigrtmin() }
304}
305
306/// Returns the maximum (inclusive) real-time signal number.
307#[allow(non_snake_case)]
308pub fn SIGRTMAX() -> c_int {
309    // SAFETY: trivially safe
310    unsafe { __libc_current_sigrtmax() }
311}
312
313fn valid_rt_signal_num(num: c_int) -> bool {
314    num >= SIGRTMIN() && num <= SIGRTMAX()
315}
316
317/// Registers `handler` as the signal handler of signum `num`.
318///
319/// # Safety
320///
321/// This is considered unsafe because the given handler will be called asynchronously, interrupting
322/// whatever the thread was doing and therefore must only do async-signal-safe operations.
323pub unsafe fn register_signal_handler(num: c_int, handler: extern "C" fn(c_int)) -> Result<()> {
324    let mut sigact: sigaction = mem::zeroed();
325    sigact.sa_flags = SA_RESTART;
326    sigact.sa_sigaction = handler as *const () as usize;
327
328    let ret = sigaction(num, &sigact, null_mut());
329    if ret < 0 {
330        return errno_result();
331    }
332
333    Ok(())
334}
335
336/// Resets the signal handler of signum `num` back to the default.
337pub fn clear_signal_handler(num: c_int) -> Result<()> {
338    // SAFETY:
339    // Safe because sigaction is owned and expected to be initialized ot zeros.
340    let mut sigact: sigaction = unsafe { mem::zeroed() };
341    sigact.sa_flags = SA_RESTART;
342    sigact.sa_sigaction = SIG_DFL;
343
344    // SAFETY:
345    // Safe because sigact is owned, and this is restoring the default signal handler.
346    let ret = unsafe { sigaction(num, &sigact, null_mut()) };
347    if ret < 0 {
348        return errno_result();
349    }
350
351    Ok(())
352}
353
354/// Registers `handler` as the signal handler for the real-time signal with signum `num`.
355///
356/// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range.
357///
358/// # Safety
359///
360/// This is considered unsafe because the given handler will be called asynchronously, interrupting
361/// whatever the thread was doing and therefore must only do async-signal-safe operations.
362pub unsafe fn register_rt_signal_handler(num: c_int, handler: extern "C" fn(c_int)) -> Result<()> {
363    if !valid_rt_signal_num(num) {
364        return Err(ErrnoError::new(EINVAL));
365    }
366
367    register_signal_handler(num, handler)
368}
369
370/// Creates `sigset` from an array of signal numbers.
371///
372/// This is a helper function used when we want to manipulate signals.
373pub fn create_sigset(signals: &[c_int]) -> Result<sigset_t> {
374    // SAFETY:
375    // sigset will actually be initialized by sigemptyset below.
376    let mut sigset: sigset_t = unsafe { mem::zeroed() };
377
378    // SAFETY:
379    // Safe - return value is checked.
380    let ret = unsafe { sigemptyset(&mut sigset) };
381    if ret < 0 {
382        return errno_result();
383    }
384
385    for signal in signals {
386        // SAFETY:
387        // Safe - return value is checked.
388        let ret = unsafe { sigaddset(&mut sigset, *signal) };
389        if ret < 0 {
390            return errno_result();
391        }
392    }
393
394    Ok(sigset)
395}
396
397/// Wait for signal before continuing. The signal number of the consumed signal is returned on
398/// success. EAGAIN means the timeout was reached.
399pub fn wait_for_signal(signals: &[c_int], timeout: Option<Duration>) -> Result<c_int> {
400    let sigset = create_sigset(signals)?;
401
402    match timeout {
403        Some(timeout) => {
404            let ts = duration_to_timespec(timeout);
405            // SAFETY:
406            // Safe - return value is checked.
407            let ret = handle_eintr_errno!(unsafe { sigtimedwait(&sigset, null_mut(), &ts) });
408            if ret < 0 {
409                errno_result()
410            } else {
411                Ok(ret)
412            }
413        }
414        None => {
415            let mut ret: c_int = 0;
416            // SAFETY: Safe because args are valid and the return value is checked.
417            let err = handle_eintr_rc!(unsafe { sigwait(&sigset, &mut ret as *mut c_int) });
418            if err != 0 {
419                Err(ErrnoError::new(err))
420            } else {
421                Ok(ret)
422            }
423        }
424    }
425}
426
427/// Retrieves the signal mask of the current thread as a vector of c_ints.
428pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
429    let mut mask = Vec::new();
430
431    // SAFETY:
432    // Safe - return values are checked.
433    unsafe {
434        let mut old_sigset: sigset_t = mem::zeroed();
435        let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t);
436        if ret < 0 {
437            return Err(Error::RetrieveSignalMask(ret));
438        }
439
440        for num in 0..=SIGRTMAX() {
441            if sigismember(&old_sigset, num) > 0 {
442                mask.push(num);
443            }
444        }
445    }
446
447    Ok(mask)
448}
449
450/// Masks given signal.
451///
452/// If signal is already blocked the call will fail with Error::SignalAlreadyBlocked
453/// result.
454pub fn block_signal(num: c_int) -> SignalResult<()> {
455    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
456
457    // SAFETY:
458    // Safe - return values are checked.
459    unsafe {
460        let mut old_sigset: sigset_t = mem::zeroed();
461        let ret = pthread_sigmask(SIG_BLOCK, &sigset, &mut old_sigset as *mut sigset_t);
462        if ret < 0 {
463            return Err(Error::BlockSignal(ErrnoError::last()));
464        }
465        let ret = sigismember(&old_sigset, num);
466        match ret.cmp(&0) {
467            Ordering::Less => {
468                return Err(Error::CompareBlockedSignals(ErrnoError::last()));
469            }
470            Ordering::Greater => {
471                return Err(Error::SignalAlreadyBlocked(num));
472            }
473            _ => (),
474        };
475    }
476    Ok(())
477}
478
479/// Unmasks given signal.
480pub fn unblock_signal(num: c_int) -> SignalResult<()> {
481    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
482
483    // SAFETY:
484    // Safe - return value is checked.
485    let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
486    if ret < 0 {
487        return Err(Error::UnblockSignal(ErrnoError::last()));
488    }
489    Ok(())
490}
491
492/// Clears pending signal.
493pub fn clear_signal(num: c_int) -> SignalResult<()> {
494    let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
495
496    while {
497        // SAFETY:
498        // This is safe as we are rigorously checking return values
499        // of libc calls.
500        unsafe {
501            let mut siginfo: siginfo_t = mem::zeroed();
502            let ts = timespec {
503                tv_sec: 0,
504                tv_nsec: 0,
505            };
506            // Attempt to consume one instance of pending signal. If signal
507            // is not pending, the call will fail with EAGAIN or EINTR.
508            let ret = sigtimedwait(&sigset, &mut siginfo, &ts);
509            if ret < 0 {
510                let e = ErrnoError::last();
511                match e.errno() {
512                    EAGAIN | EINTR => {}
513                    _ => {
514                        return Err(Error::ClearWaitPending(ErrnoError::last()));
515                    }
516                }
517            }
518
519            // This sigset will be actually filled with `sigpending` call.
520            let mut chkset: sigset_t = mem::zeroed();
521            // See if more instances of the signal are pending.
522            let ret = sigpending(&mut chkset);
523            if ret < 0 {
524                return Err(Error::ClearGetPending(ErrnoError::last()));
525            }
526
527            let ret = sigismember(&chkset, num);
528            if ret < 0 {
529                return Err(Error::ClearCheckPending(ErrnoError::last()));
530            }
531
532            // This is do-while loop condition.
533            ret != 0
534        }
535    } {}
536
537    Ok(())
538}
539
540/// # Safety
541/// This is marked unsafe because it allows signals to be sent to arbitrary PIDs. Sending some
542/// signals may lead to undefined behavior. Also, the return codes of the child processes need to be
543/// reaped to avoid leaking zombie processes.
544pub unsafe fn kill(pid: Pid, signum: c_int) -> Result<()> {
545    let ret = libc::kill(pid, signum);
546
547    if ret != 0 {
548        errno_result()
549    } else {
550        Ok(())
551    }
552}
553
554/// Trait for threads that can be signalled via `pthread_kill`.
555///
556/// Note that this is only useful for signals between SIGRTMIN and SIGRTMAX because these are
557/// guaranteed to not be used by the C runtime.
558///
559/// # Safety
560/// This is marked unsafe because the implementation of this trait must guarantee that the returned
561/// pthread_t is valid and has a lifetime at least that of the trait object.
562pub unsafe trait Killable {
563    fn pthread_handle(&self) -> pthread_t;
564
565    /// Sends the signal `num` to this killable thread.
566    ///
567    /// The value of `num` must be within [`SIGRTMIN`, `SIGRTMAX`] range.
568    fn kill(&self, num: c_int) -> Result<()> {
569        if !valid_rt_signal_num(num) {
570            return Err(ErrnoError::new(EINVAL));
571        }
572
573        // SAFETY:
574        // Safe because we ensure we are using a valid pthread handle, a valid signal number, and
575        // check the return result.
576        let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
577        if ret < 0 {
578            return errno_result();
579        }
580        Ok(())
581    }
582}
583
584// SAFETY:
585// Safe because we fulfill our contract of returning a genuine pthread handle.
586unsafe impl<T> Killable for JoinHandle<T> {
587    fn pthread_handle(&self) -> pthread_t {
588        self.as_pthread_t() as _
589    }
590}
591
592// Represents a temporarily blocked signal. It will unblock the signal when dropped.
593pub struct BlockedSignal {
594    signal_num: c_int,
595}
596
597impl BlockedSignal {
598    // Returns a `BlockedSignal` if the specified signal can be blocked, otherwise None.
599    pub fn new(signal_num: c_int) -> Option<BlockedSignal> {
600        if block_signal(signal_num).is_ok() {
601            Some(BlockedSignal { signal_num })
602        } else {
603            None
604        }
605    }
606}
607
608impl Drop for BlockedSignal {
609    fn drop(&mut self) {
610        unblock_signal(self.signal_num).expect("failed to restore signal mask");
611    }
612}