Skip to main content

relibc/header/signal/
mod.rs

1//! `signal.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
4
5use core::{mem, ptr};
6
7use cbitset::BitSet;
8
9#[cfg(target_os = "redox")]
10use crate::platform::types::pthread_attr_t;
11use crate::{
12    error::{Errno, ResultExt},
13    header::{bits_sigset_t::sigset_t, errno, time::timespec},
14    platform::{
15        self, ERRNO, Pal, PalSignal, Sys,
16        types::{c_char, c_int, c_ulonglong, c_void, pid_t, pthread_t, size_t, uid_t},
17    },
18};
19
20pub mod constants;
21pub use self::sys::*;
22pub use constants::*;
23
24use super::{
25    errno::EFAULT,
26    stdio::{fprintf, stderr},
27};
28
29#[cfg(target_os = "linux")]
30#[path = "linux.rs"]
31pub mod sys;
32
33#[cfg(target_os = "redox")]
34#[path = "redox.rs"]
35pub mod sys;
36
37type SigSet = BitSet<[u64; 1]>;
38
39/// cbindgen:ignore
40/// Request for default signal handling.
41pub(crate) const SIG_DFL: usize = 0;
42/// cbindgen:ignore
43/// Request that signal be ignored.
44pub(crate) const SIG_IGN: usize = 1;
45/// cbindgen:ignore
46/// Return value of `signal()` in case of error.
47pub(crate) const SIG_ERR: isize = -1;
48/// cbindgen:ignore
49/// Obsolete in issue 7, removed in issue 8.
50/// Request that signal be held.
51pub(crate) const SIG_HOLD: isize = 2;
52
53/// The resulting set is the union of the current set and the signal set
54/// pointed to by the argument `set`.
55pub const SIG_BLOCK: c_int = 0;
56/// The resulting set is the intersection of the current set and the compliment
57/// of the signal set pointed to by the argument `set`.
58pub const SIG_UNBLOCK: c_int = 1;
59/// The resulting set is the signal set pointed to by the argument `set`.
60pub const SIG_SETMASK: c_int = 2;
61
62/// A queued signal, with an application-defined value, is generated when the
63/// event of interest occurs.
64pub const SIGEV_SIGNAL: c_int = 0;
65/// No asynchronous notification is delivered when the event of interest
66/// occurs.
67pub const SIGEV_NONE: c_int = 1;
68/// A notification function is called to perform notification.
69pub const SIGEV_THREAD: c_int = 2;
70
71/// cbindgen:ignore
72/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
73///
74/// # Implementation
75/// This struct in Rust is missing the `sa_sigaction` field. The stucture in
76/// cbindgen uses a union to combine `sa_handler` and `sa_sigaction`. POSIX
77/// states that both fields shall not be used simultaneously.
78#[repr(C)]
79#[derive(Clone, Debug)]
80pub struct sigaction {
81    /// Pointer to a signal-catching function or one of the `SIG_IGN` or
82    /// `SIG_DFL`.
83    pub sa_handler: Option<extern "C" fn(c_int)>,
84    /// Special flags.
85    pub sa_flags: c_int,
86    /// Non-POSIX, see <https://www.man7.org/linux/man-pages/man2/sigaction.2.html>.
87    ///
88    /// Not intended for application use. A sigaction wrapper function is
89    /// intended to use this to store the location of the trampoline code and
90    /// setting the `SA_RESTORER` flag in `sa_flags`.
91    pub sa_restorer: Option<unsafe extern "C" fn()>,
92    /// Set of signals to be blocked during execution of the signal handling
93    /// function.
94    pub sa_mask: sigset_t,
95}
96
97#[repr(C)]
98#[derive(Clone)]
99pub struct sigaltstack {
100    /// Stack base or pointer.
101    pub ss_sp: *mut c_void,
102    /// Flags,
103    pub ss_flags: c_int,
104    /// Stack size.
105    pub ss_size: size_t,
106}
107
108/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
109#[repr(C)]
110#[derive(Clone)]
111#[cfg(not(target_os = "linux"))]
112pub struct sigevent {
113    /// Signal value.
114    pub sigev_value: sigval,
115    /// Signal number.
116    pub sigev_signo: c_int,
117    /// Notification type.
118    pub sigev_notify: c_int,
119    /// Notification function.
120    pub sigev_notify_function: Option<extern "C" fn(sigval)>,
121    /// Notification attributes.
122    pub sigev_notify_attributes: *mut pthread_attr_t,
123}
124
125/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
126///
127/// # Implementation
128/// Must match with signature from libc.
129/// See <https://docs.rs/libc/0.2.186/src/libc/unix/linux_like/mod.rs.html#300-322>.
130#[repr(C)]
131#[derive(Clone)]
132#[cfg(target_os = "linux")]
133pub struct sigevent {
134    /// Signal value.
135    pub sigev_value: sigval,
136    /// Signal number.
137    pub sigev_signo: c_int,
138    /// Notification type.
139    pub sigev_notify: c_int,
140    // Actually a union.  We only expose sigev_notify_thread_id because it's
141    // the most useful member
142    pub sigev_notify_thread_id: c_int,
143    #[cfg(target_pointer_width = "64")]
144    __unused1: [c_int; 11],
145    #[cfg(target_pointer_width = "32")]
146    __unused1: [c_int; 12],
147}
148
149// FIXME: This struct is wrong on Linux
150#[repr(C)]
151#[derive(Clone, Copy)]
152pub struct siginfo {
153    /// Signal number.
154    pub si_signo: c_int,
155    /// If non-zero, an errno value associated with this signal.
156    pub si_errno: c_int,
157    /// Signal code.
158    pub si_code: c_int,
159    /// Sending process ID.
160    pub si_pid: pid_t,
161    /// Real user ID of sending process.
162    pub si_uid: uid_t,
163    /// Address that caused fault.
164    pub si_addr: *mut c_void,
165    /// Exit value or signal.
166    pub si_status: c_int,
167    /// Signal value.
168    pub si_value: sigval,
169}
170
171/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
172///
173/// Signal value.
174#[derive(Clone, Copy)]
175#[repr(C)]
176pub union sigval {
177    /// Integer signal value.
178    pub sival_int: c_int,
179    /// Pointer signal value.
180    pub sival_ptr: *mut c_void,
181}
182
183/// cbindgen:ignore
184/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
185pub type siginfo_t = siginfo;
186
187/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>
188pub type stack_t = sigaltstack;
189
190//NOTE for the following two functions, to see why they're implemented slightly differently from their intended behavior, read
191//     https://git.musl-libc.org/cgit/musl/commit/?id=583e55122e767b1586286a0d9c35e2a4027998ab
192#[unsafe(no_mangle)]
193unsafe extern "C" fn __sigsetjmp_tail(jb: *mut c_ulonglong, ret: c_int) -> c_int {
194    let set = jb.wrapping_add(9);
195    if ret > 0 {
196        unsafe { sigprocmask(SIG_SETMASK, set, ptr::null_mut()) };
197    } else {
198        unsafe { sigprocmask(SIG_SETMASK, ptr::null_mut(), set) };
199    }
200    ret
201}
202
203/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/kill.html>.
204///
205/// Sends a signal to a process or a group of processes specified by `pid`.
206///
207/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
208/// indicate the error. No signal is sent if failed.
209#[unsafe(no_mangle)]
210pub extern "C" fn kill(pid: pid_t, sig: c_int) -> c_int {
211    Sys::kill(pid, sig).map(|()| 0).or_minus_one_errno()
212}
213
214/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigqueue.html>.
215///
216/// Causes the signal specified by `signo` to be sent with the value specified
217/// by `value` to the process specified by `pid`.
218///
219/// Upon success, the specified signal shall have been queued, and returns `0`.
220/// Upon error, returns `-1` and sets errno to indicate the error.
221#[unsafe(no_mangle)]
222pub extern "C" fn sigqueue(pid: pid_t, signo: c_int, value: sigval) -> c_int {
223    Sys::sigqueue(pid, signo, value)
224        .map(|()| 0)
225        .or_minus_one_errno()
226}
227
228/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/killpg.html>.
229///
230/// Sends the signali specified by `sig` to the process group specified by
231/// `pgrp`.
232///
233/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
234/// indicate the error. No signal is sent if failed.
235#[unsafe(no_mangle)]
236pub extern "C" fn killpg(pgrp: pid_t, sig: c_int) -> c_int {
237    Sys::killpg(pgrp, sig).map(|()| 0).or_minus_one_errno()
238}
239
240/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_kill.html>.
241///
242/// Requests that a signal be delivered to the specified thread. It shall not
243/// be an error is `thread` is a zombie thread.
244///
245/// Upon success, returns `0`. Upon failure, returns an error number and does
246/// not send the signal.
247#[unsafe(no_mangle)]
248pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int {
249    let os_tid = {
250        let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
251        unsafe { pthread.os_tid.get().read() }
252    };
253    crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) })
254}
255
256/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pthread_sigmask.html>.
257///
258/// Examines or changes (or both) the calling thread's signal mask.
259///
260/// Upon success, returns `0`. Upon failure, returns an error number.
261#[unsafe(no_mangle)]
262pub unsafe extern "C" fn pthread_sigmask(
263    how: c_int,
264    set: *const sigset_t,
265    oldset: *mut sigset_t,
266) -> c_int {
267    // On Linux and Redox, pthread_sigmask and sigprocmask are equivalent
268    if unsafe { sigprocmask(how, set, oldset) } == 0 {
269        0
270    } else {
271        //TODO: Fix race
272        platform::ERRNO.get()
273    }
274}
275
276/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/raise.html>.
277///
278/// Sends the signal `sig` to the executing thread or process. If a signal
279/// handler is called, this function shall not return until after the signal
280/// handler does.
281///
282/// Upon success, returns `0`. Upon failure, returns a non-zero value and sets
283/// errno to indicate the error.
284#[unsafe(no_mangle)]
285pub extern "C" fn raise(sig: c_int) -> c_int {
286    Sys::raise(sig).map(|()| 0).or_minus_one_errno()
287}
288
289/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaction.html>.
290///
291/// Allows the calling process to examine and/or specify the action to be
292/// associated with a specific signal.
293///
294/// Upon success, returns `0`. Upon failure, returns `-1`, sets errno to
295/// indicate the error, and no new signal-catching function shall be installed.
296#[unsafe(no_mangle)]
297pub unsafe extern "C" fn sigaction(
298    sig: c_int,
299    act: *const sigaction,
300    oact: *mut sigaction,
301) -> c_int {
302    Sys::sigaction(sig, unsafe { act.as_ref() }, unsafe { oact.as_mut() })
303        .map(|()| 0)
304        .or_minus_one_errno()
305}
306
307/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaddset.html>.
308///
309/// Adds the individual signal specified by `signo` to the signal set pointed
310/// to by `set`.
311///
312/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
313/// indicate the error.
314///
315/// # Safety
316/// The `sigset_t` pointed to by `set` must be initialized by `sigemptyset()`
317/// or `sigfillset()` before calling this function or undefined behaviour will
318/// occur.
319#[unsafe(no_mangle)]
320pub unsafe extern "C" fn sigaddset(set: *mut sigset_t, signo: c_int) -> c_int {
321    if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX)
322    /* TODO */
323    {
324        platform::ERRNO.set(errno::EINVAL);
325        return -1;
326    }
327
328    if let Some(set) = unsafe { (set.cast::<SigSet>()).as_mut() } {
329        set.insert(signo as usize - 1); // 0-indexed usize, please!
330    }
331    0
332}
333
334/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigaltstack.html>.
335///
336/// Allows a process to define and examine the state of an alternate stack for
337/// signal handlers for the current thread.
338///
339/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
340/// indicate the error.
341///
342/// # Safety
343/// Use of this function by library threads that are not bound to
344/// kernel-scheduled entities results in undefined behaviour.
345#[unsafe(no_mangle)]
346pub unsafe extern "C" fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int {
347    unsafe {
348        Sys::sigaltstack(ss.as_ref(), old_ss.as_mut())
349            .map(|()| 0)
350            .or_minus_one_errno()
351    }
352}
353
354/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigdelset.html>.
355///
356/// Deletes the individual signal specified by `signo` to the signal set
357/// pointed to by `set`.
358///
359/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
360/// indicate the error.
361///
362/// # Safety
363/// The `sigset_t` pointed to by `set` must be initialized by `sigemptyset()`
364/// or `sigfillset()` before calling this function or undefined behaviour will
365/// occur.
366#[unsafe(no_mangle)]
367pub unsafe extern "C" fn sigdelset(set: *mut sigset_t, signo: c_int) -> c_int {
368    if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX)
369    /* TODO */
370    {
371        platform::ERRNO.set(errno::EINVAL);
372        return -1;
373    }
374
375    if let Some(set) = unsafe { (set.cast::<SigSet>()).as_mut() } {
376        set.remove(signo as usize - 1); // 0-indexed usize, please!
377    }
378    0
379}
380
381/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigemptyset.html>.
382///
383/// Initializes the signal set pointed to by `set`, such that all signals
384/// defined in POSIX.1-2024 are excluded.
385///
386/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
387/// indicate the error.
388#[unsafe(no_mangle)]
389pub unsafe extern "C" fn sigemptyset(set: *mut sigset_t) -> c_int {
390    if let Some(set) = unsafe { (set.cast::<SigSet>()).as_mut() } {
391        set.clear();
392    }
393    0
394}
395
396/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigfillset.html>.
397///
398/// Initializes the signal set pointed to by `set`, such that all signals
399/// defined in POSIX.1-2024 are included.
400///
401/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
402/// indicate the error.
403#[unsafe(no_mangle)]
404pub unsafe extern "C" fn sigfillset(set: *mut sigset_t) -> c_int {
405    if let Some(set) = unsafe { (set.cast::<SigSet>()).as_mut() } {
406        set.fill(.., true);
407    }
408    0
409}
410
411/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
412///
413/// Adds `sig` to the signal mask of the calling process.
414///
415/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
416/// indicate the error.
417///
418/// # Deprecated
419/// Present in issue 7. Removed in issue 8.
420///
421/// Use of this function is unspecified in a multi-threaded process.
422///
423/// `pthread_sigmask()` or `sigprocmask()` should be used instead.
424///
425/// # Implementation
426/// Calls `sigprocmask()` internally.
427#[deprecated]
428#[unsafe(no_mangle)]
429pub unsafe extern "C" fn sighold(sig: c_int) -> c_int {
430    let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
431    unsafe { sigemptyset(pset.as_mut_ptr()) };
432    let mut set = unsafe { pset.assume_init() };
433    if unsafe { sigaddset(&raw mut set, sig) } < 0 {
434        return -1;
435    }
436    unsafe { sigprocmask(SIG_BLOCK, &raw const set, ptr::null_mut()) }
437}
438
439/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
440///
441/// Sets the disposition of `sig` to `SIG_IGN`.
442///
443/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
444/// indicate the error.
445///
446/// # Deprecated
447/// Present in issue 7. Removed in issue 8.
448///
449/// Use of this function is unspecified in a multi-threaded process.
450///
451/// `sigaction()` should be used instead.
452///
453/// # Implementation
454/// Calls `sigaction()` internally.
455#[deprecated]
456#[expect(clippy::missing_transmute_annotations, reason = "too verbose")]
457#[unsafe(no_mangle)]
458pub extern "C" fn sigignore(sig: c_int) -> c_int {
459    let mut psa = mem::MaybeUninit::<sigaction>::uninit();
460    unsafe { sigemptyset(&raw mut (*psa.as_mut_ptr()).sa_mask) };
461    let mut sa = unsafe { psa.assume_init() };
462    sa.sa_handler = unsafe { mem::transmute(SIG_IGN) };
463    sa.sa_flags = 0;
464    unsafe { sigaction(sig, &raw const sa, ptr::null_mut()) }
465}
466
467/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/siginterrupt.html>.
468///
469/// Changes the restart behaviour when a function is interrupted by the
470/// specified signal.
471///
472/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
473/// indicate the error.
474///
475/// # Deprecated
476/// Marked obsolescent in issue 7. Removed in issue 8.
477///
478/// Should use `sigaction()` with the `SA_RESTART` flag instead.
479///
480/// # Implementation
481/// Internally uses `sigaction()` with the `SA_RESTART` flag.
482#[deprecated]
483#[unsafe(no_mangle)]
484pub extern "C" fn siginterrupt(sig: c_int, flag: c_int) -> c_int {
485    let mut psa = mem::MaybeUninit::<sigaction>::uninit();
486    unsafe { sigaction(sig, ptr::null_mut(), psa.as_mut_ptr()) };
487    let mut sa = unsafe { psa.assume_init() };
488    if flag != 0 {
489        sa.sa_flags &= !SA_RESTART as c_int;
490    } else {
491        sa.sa_flags |= SA_RESTART as c_int;
492    }
493
494    unsafe { sigaction(sig, &raw const sa, ptr::null_mut()) }
495}
496
497/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigismember.html>.
498///
499/// Tests whether the signal specified by `signo` is a member of the set
500/// pointed to by `set`.
501///
502/// Upon success, return `1` if the specified signal is a member of the
503/// specified set, or `0` if it is not. Upon failure, returns `-1` and sets
504/// errno to indicate the error.
505///
506/// # Safety
507/// The `sigset_t` pointed to by `set` must be initialized by `sigemptyset()`
508/// or `sigfillset()` before calling this function or undefined behaviour will
509/// occur.
510#[unsafe(no_mangle)]
511pub unsafe extern "C" fn sigismember(set: *const sigset_t, signo: c_int) -> c_int {
512    if signo <= 0 || signo as usize > NSIG.max(SIGRTMAX)
513    /* TODO */
514    {
515        platform::ERRNO.set(errno::EINVAL);
516        return -1;
517    }
518
519    if let Some(set) = unsafe { (set as *mut SigSet).as_mut() }
520        && set.contains(signo as usize - 1)
521    {
522        return 1;
523    }
524    0
525}
526
527/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/signal.html>.
528///
529/// Chooses one of three ways in which receipt of the signal number `sig` is to
530/// be subsequently handled.
531///
532/// Upon success, returns the value of `func` for the most recent call to
533/// `signal()` for the specified signal `sig`.Upon failure, returns `SIG_ERR`
534/// and a positive value shall be stored in errno.
535#[expect(clippy::missing_transmute_annotations, reason = "too verbose")]
536#[unsafe(no_mangle)]
537pub extern "C" fn signal(
538    sig: c_int,
539    func: Option<extern "C" fn(c_int)>,
540) -> Option<extern "C" fn(c_int)> {
541    let sa = sigaction {
542        sa_handler: func,
543        sa_flags: SA_RESTART as _,
544        sa_restorer: None, // set by platform if applicable
545        sa_mask: sigset_t::default(),
546    };
547    let mut old_sa = mem::MaybeUninit::uninit();
548    if unsafe { sigaction(sig, &raw const sa, old_sa.as_mut_ptr()) } < 0 {
549        return unsafe { mem::transmute(SIG_ERR) };
550    }
551    unsafe { old_sa.assume_init() }.sa_handler
552}
553
554/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
555///
556/// Removes `sig` from the signal mask of the calling process and suspend the
557/// calling process until a signal is received.
558///
559/// Suspends execution of the thread until a signal is received, whereupon it
560/// shall return `-1` and set errno to `EINTR`.
561///
562/// # Deprecated
563/// Present in issue 7. Removed in issue 8.
564///
565/// Use of this function is unspecified in a multi-threaded process.
566///
567/// `sigsuspend()` should be used instead.
568///
569/// # Implementation
570/// Calls `sigsuspend()` internally.
571#[deprecated]
572#[unsafe(no_mangle)]
573pub unsafe extern "C" fn sigpause(sig: c_int) -> c_int {
574    let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
575    unsafe { sigprocmask(0, ptr::null_mut(), pset.as_mut_ptr()) };
576    let mut set = unsafe { pset.assume_init() };
577    if unsafe { sigdelset(&raw mut set, sig) } == -1 {
578        return -1;
579    }
580    unsafe { sigsuspend(&raw const set) }
581}
582
583/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigpending.html>.
584///
585/// Stores, in the location referenced by the `set` argument, the set of
586/// signals that are blocked from delivery to the calling thread and that are
587/// pending on the process or the calling thread.
588///
589/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
590/// indicate the error.
591#[unsafe(no_mangle)]
592pub unsafe extern "C" fn sigpending(set: *mut sigset_t) -> c_int {
593    (|| Sys::sigpending(unsafe { set.as_mut().ok_or(Errno(EFAULT)) }?))()
594        .map(|()| 0)
595        .or_minus_one_errno()
596}
597
598// TODO: Double-check this mask.
599// This prevents the application from blocking the two signals SIGRTMIN - 1 and SIGRTMIN - 2 which
600// are (at least meant to be) used internally for timers and pthread cancellation. On Linux this is
601// 32 and 33 (same as NPTL reserves), whereas this on Redox is 33 and 34 (TODO: could this be
602// changed to 32 and 33 for Redox too, since there's currently no support for "sigqueue" targeting
603// specific threads).
604const RLCT_SIGNAL_MASK: sigset_t = (1 << ((SIGRTMIN - 1) - 1)) | (1 << ((SIGRTMIN - 2) - 1));
605
606/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigprocmask.html>.
607///
608/// Examines or changes (or both) the calling thread's signal mask.
609///
610/// Upon success, returns `0`. Upon failure, returns `-1`, sets errno to
611/// indicate the error, and does not change the signal mask.
612///
613/// Use of this function is unspecified in a multi-threaded process.
614#[unsafe(no_mangle)]
615pub unsafe extern "C" fn sigprocmask(
616    how: c_int,
617    set: *const sigset_t,
618    oset: *mut sigset_t,
619) -> c_int {
620    (|| {
621        let set = unsafe { set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK) };
622        let mut oset = unsafe { oset.as_mut() };
623
624        Sys::sigprocmask(
625            how,
626            set.as_ref(),
627            oset.as_deref_mut(), // as_deref_mut for lifetime reasons
628        )?;
629
630        if let Some(oset) = oset {
631            *oset &= !RLCT_SIGNAL_MASK;
632        }
633
634        Ok(0)
635    })()
636    .or_minus_one_errno()
637}
638
639/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
640///
641/// Removes `sig` from the signal mask of the calling process.
642///
643/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
644/// indicate the error.
645///
646/// # Deprecated
647/// Present in issue 7. Removed in issue 8.
648///
649/// Use of this function is unspecified in a multi-threaded process.
650///
651/// `pthread_sigmask()` or `sigprocmask()` should be used instead.
652///
653/// # Implementation
654/// Calls `sigprocmask()` internally.
655#[deprecated]
656#[unsafe(no_mangle)]
657pub unsafe extern "C" fn sigrelse(sig: c_int) -> c_int {
658    let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
659    unsafe { sigemptyset(pset.as_mut_ptr()) };
660    let mut set = unsafe { pset.assume_init() };
661    if unsafe { sigaddset(&raw mut set, sig) } < 0 {
662        return -1;
663    }
664    unsafe { sigprocmask(SIG_UNBLOCK, &raw const set, ptr::null_mut()) }
665}
666
667/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html>.
668///
669/// Modifies signal dispositions.
670///
671/// Upon success, returns `SIG_HOLD` if the signal had been blocked and the
672/// signal's previous disposition if it had not been blocked. Upon failure,
673/// returns `SIG_ERR` and sets errno to indicate the error.
674///
675/// # Deprecated
676/// Present in issue 7. Removed in issue 8.
677///
678/// Use of this function is unspecified in a multi-threaded process.
679///
680/// `sigaction()` should be used instead.
681///
682/// # Implementation
683/// Calls `sigaction()` internally.
684#[deprecated]
685#[unsafe(no_mangle)]
686pub unsafe extern "C" fn sigset(
687    sig: c_int,
688    func: Option<extern "C" fn(c_int)>,
689) -> Option<extern "C" fn(c_int)> {
690    let mut old_sa = mem::MaybeUninit::uninit();
691    let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
692    let sig_hold: Option<extern "C" fn(c_int)> = unsafe { mem::transmute(SIG_HOLD) };
693    let sig_err: Option<extern "C" fn(c_int)> = unsafe { mem::transmute(SIG_ERR) };
694    unsafe { sigemptyset(pset.as_mut_ptr()) };
695    let mut set = unsafe { pset.assume_init() };
696    if unsafe { sigaddset(&raw mut set, sig) } < 0 {
697        return sig_err;
698    } else {
699        let is_equal = {
700            match (func, sig_hold) {
701                (None, None) => true,
702                (Some(_), None) | (None, Some(_)) => false,
703                (Some(f), Some(sh)) => ptr::fn_addr_eq(f, sh),
704            }
705        };
706        if is_equal {
707            if unsafe { sigaction(sig, ptr::null_mut(), old_sa.as_mut_ptr()) } < 0
708                || unsafe { sigprocmask(SIG_BLOCK, &raw const set, &raw mut set) } < 0
709            {
710                return sig_err;
711            }
712        } else {
713            let mut sa = sigaction {
714                sa_handler: func,
715                sa_flags: c_int::from(0),
716                sa_restorer: None, // set by platform if applicable
717                sa_mask: sigset_t::default(),
718            };
719            unsafe { sigemptyset(&raw mut sa.sa_mask) };
720            if unsafe { sigaction(sig, &raw const sa, old_sa.as_mut_ptr()) } < 0
721                || unsafe { sigprocmask(SIG_UNBLOCK, &raw const set, &raw mut set) } < 0
722            {
723                return sig_err;
724            }
725        }
726    }
727    if unsafe { sigismember(&raw const set, sig) } == 1 {
728        return sig_hold;
729    }
730    unsafe { old_sa.assume_init().sa_handler }
731}
732
733/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigsuspend.html>.
734///
735/// Atomically both replace the current signal mask of the calling thread with
736/// the set of signals pointed to by `sigmask` and suspend the thread until
737/// delivery of a signal whose action is either to execute a signal-catching
738/// function or to terminate the process.
739///
740/// Upon success, suspends thread execution and does not return. If the action
741/// is to execute a signal-catching function, returns `-1` after the
742/// signal-catching function returns, and restores the signal mask back to the
743/// set that existed prior to calling this function, then sets errno to
744/// indicate the error. Upon failure, returns `-1` and sets errno to indicate
745/// the error.
746#[unsafe(no_mangle)]
747pub unsafe extern "C" fn sigsuspend(sigmask: *const sigset_t) -> c_int {
748    Err(Sys::sigsuspend(unsafe { &*sigmask })).or_minus_one_errno()
749}
750
751/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigwait.html>.
752///
753/// Selects a pending signal from `set`, atomically clears it from the system's
754/// set of pending signals, and return that signal number in the location
755/// referenced by `sig`.
756///
757/// Upon success, stores the signal number of the received signal at the
758/// location referenced by `sig` and returns `0`. Upon failure, an error number
759/// is returned to indicate the error.
760#[unsafe(no_mangle)]
761pub unsafe extern "C" fn sigwait(set: *const sigset_t, sig: *mut c_int) -> c_int {
762    let mut pinfo = mem::MaybeUninit::<siginfo_t>::uninit();
763    if unsafe { sigtimedwait(set, pinfo.as_mut_ptr(), ptr::null_mut()) } < 0 {
764        return -1;
765    }
766    let info = unsafe { pinfo.assume_init() };
767    unsafe { (*sig) = info.si_signo };
768    0
769}
770
771/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigtimedwait.html>.
772///
773/// Equivalent to `sigwaitinfo()` except that if none of the signals specified
774/// by `set` are pending, wait for the time interval specified in the
775/// `timespec` structure referenced by `timeout`. Shall return immediately with
776/// an error if `timeout` is zero-valued and none of the signals specified by
777/// `set` are pending.
778///
779/// Upon success, returns the selected signal number. Upon failure, returns
780/// `-1` and sets errno to indicate the error.
781///
782/// # Safety
783/// If `timeout` is the null pointer, behaviour is unspecified.
784#[unsafe(no_mangle)]
785pub unsafe extern "C" fn sigtimedwait(
786    set: *const sigset_t,
787    // s/siginfo_t/siginfo due to https://github.com/mozilla/cbindgen/issues/621
788    sig: *mut siginfo,
789    // POSIX leaves behavior unspecified if this is NULL, but on both Linux and Redox, NULL is used
790    // to differentiate between sigtimedwait and sigwaitinfo internally
791    tp: *const timespec,
792) -> c_int {
793    Sys::sigtimedwait(unsafe { &*set }, unsafe { sig.as_mut() }, unsafe {
794        tp.as_ref()
795    })
796    .or_minus_one_errno()
797}
798
799/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigwaitinfo.html>.
800///
801/// Selects the pending signal from the set specified by `set`.
802///
803/// Upon success, returns the selected signal number. Upon failure, returns
804/// `-1` and sets errno to indicate the error.
805#[unsafe(no_mangle)]
806pub unsafe extern "C" fn sigwaitinfo(set: *const sigset_t, sig: *mut siginfo_t) -> c_int {
807    unsafe { sigtimedwait(set, sig, core::ptr::null()) }
808}
809
810pub(crate) const SIGNAL_STRINGS: [&str; 32] = [
811    "Unknown signal\0",
812    "Hangup\0",
813    "Interrupt\0",
814    "Quit\0",
815    "Illegal instruction\0",
816    "Trace/breakpoint trap\0",
817    "Aborted\0",
818    "Bus error\0",
819    "Arithmetic exception\0",
820    "Killed\0",
821    "User defined signal 1\0",
822    "Segmentation fault\0",
823    "User defined signal 2\0",
824    "Broken pipe\0",
825    "Alarm clock\0",
826    "Terminated\0",
827    "Stack fault\0",
828    "Child process status\0",
829    "Continued\0",
830    "Stopped (signal)\0",
831    "Stopped\0",
832    "Stopped (tty input)\0",
833    "Stopped (tty output)\0",
834    "Urgent I/O condition\0",
835    "CPU time limit exceeded\0",
836    "File size limit exceeded\0",
837    "Virtual timer expired\0",
838    "Profiling timer expired\0",
839    "Window changed\0",
840    "I/O possible\0",
841    "Power failure\0",
842    "Bad system call\0",
843];
844
845/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/psignal.html>.
846///
847/// Writes a language-dependent message associated with a signal number to the
848/// standard error stream.
849#[unsafe(no_mangle)]
850pub unsafe extern "C" fn psignal(sig: c_int, prefix: *const c_char) {
851    let c_description = usize::try_from(sig)
852        .ok()
853        .and_then(|idx| SIGNAL_STRINGS.get(idx))
854        .unwrap_or(&SIGNAL_STRINGS[0])
855        .as_ptr();
856    // fprintf can affect errno, so we save errno and restore it
857    let old_errno = ERRNO.get();
858    // POSIX says that "prefix" shall be written if it isn't null or an empty string.
859    // Otherwise, only the signal description should be written
860    if prefix.is_null() {
861        unsafe {
862            fprintf(stderr, c"%s\n".as_ptr(), c_description);
863        }
864    } else {
865        unsafe {
866            fprintf(stderr, c"%s: %s\n".as_ptr(), prefix, c_description);
867        }
868    }
869    ERRNO.set(old_errno);
870}
871
872/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/psiginfo.html>.
873///
874/// Writes a language-dependent message associated with a signal number to the
875/// standard error stream.
876///
877/// # Implementation
878/// Calls `psignal()` internally.
879#[unsafe(no_mangle)]
880pub unsafe extern "C" fn psiginfo(info: *const siginfo_t, prefix: *const c_char) {
881    unsafe {
882        psignal((*info).si_signo, prefix);
883    }
884}