starry-signal 0.8.2

Signal management library for Starry OS
use core::{fmt, mem};

use derive_more::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not};
use linux_raw_sys::general::{SI_KERNEL, SS_DISABLE, SS_ONSTACK, kernel_sigset_t, siginfo_t};

/// SI_TIMER: signal generated by expiration of a POSIX timer.
/// Linux defines this as -2 in include/uapi/asm-generic/siginfo.h.
pub const SI_TIMER: i32 = -2;
use strum::{EnumIter, FromRepr, IntoEnumIterator};

use crate::DefaultSignalAction;

/// Signal number.
#[repr(u8)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, FromRepr, EnumIter)]
pub enum Signo {
    SIGHUP    = 1,
    SIGINT    = 2,
    SIGQUIT   = 3,
    SIGILL    = 4,
    SIGTRAP   = 5,
    SIGABRT   = 6,
    SIGBUS    = 7,
    SIGFPE    = 8,
    SIGKILL   = 9,
    SIGUSR1   = 10,
    SIGSEGV   = 11,
    SIGUSR2   = 12,
    SIGPIPE   = 13,
    SIGALRM   = 14,
    SIGTERM   = 15,
    SIGSTKFLT = 16,
    SIGCHLD   = 17,
    SIGCONT   = 18,
    SIGSTOP   = 19,
    SIGTSTP   = 20,
    SIGTTIN   = 21,
    SIGTTOU   = 22,
    SIGURG    = 23,
    SIGXCPU   = 24,
    SIGXFSZ   = 25,
    SIGVTALRM = 26,
    SIGPROF   = 27,
    SIGWINCH  = 28,
    SIGIO     = 29,
    SIGPWR    = 30,
    SIGSYS    = 31,
    SIGRTMIN  = 32,
    SIGRT1    = 33,
    SIGRT2    = 34,
    SIGRT3    = 35,
    SIGRT4    = 36,
    SIGRT5    = 37,
    SIGRT6    = 38,
    SIGRT7    = 39,
    SIGRT8    = 40,
    SIGRT9    = 41,
    SIGRT10   = 42,
    SIGRT11   = 43,
    SIGRT12   = 44,
    SIGRT13   = 45,
    SIGRT14   = 46,
    SIGRT15   = 47,
    SIGRT16   = 48,
    SIGRT17   = 49,
    SIGRT18   = 50,
    SIGRT19   = 51,
    SIGRT20   = 52,
    SIGRT21   = 53,
    SIGRT22   = 54,
    SIGRT23   = 55,
    SIGRT24   = 56,
    SIGRT25   = 57,
    SIGRT26   = 58,
    SIGRT27   = 59,
    SIGRT28   = 60,
    SIGRT29   = 61,
    SIGRT30   = 62,
    SIGRT31   = 63,
    SIGRT32   = 64,
}

impl Signo {
    pub fn is_realtime(&self) -> bool {
        *self >= Signo::SIGRTMIN
    }

    pub fn default_action(&self) -> DefaultSignalAction {
        match self {
            Signo::SIGHUP => DefaultSignalAction::Terminate,
            Signo::SIGINT => DefaultSignalAction::Terminate,
            Signo::SIGQUIT => DefaultSignalAction::CoreDump,
            Signo::SIGILL => DefaultSignalAction::CoreDump,
            Signo::SIGTRAP => DefaultSignalAction::CoreDump,
            Signo::SIGABRT => DefaultSignalAction::CoreDump,
            Signo::SIGBUS => DefaultSignalAction::CoreDump,
            Signo::SIGFPE => DefaultSignalAction::CoreDump,
            Signo::SIGKILL => DefaultSignalAction::Terminate,
            Signo::SIGUSR1 => DefaultSignalAction::Terminate,
            Signo::SIGSEGV => DefaultSignalAction::CoreDump,
            Signo::SIGUSR2 => DefaultSignalAction::Terminate,
            Signo::SIGPIPE => DefaultSignalAction::Terminate,
            Signo::SIGALRM => DefaultSignalAction::Terminate,
            Signo::SIGTERM => DefaultSignalAction::Terminate,
            Signo::SIGSTKFLT => DefaultSignalAction::Terminate,
            Signo::SIGCHLD => DefaultSignalAction::Ignore,
            Signo::SIGCONT => DefaultSignalAction::Continue,
            Signo::SIGSTOP => DefaultSignalAction::Stop,
            Signo::SIGTSTP => DefaultSignalAction::Stop,
            Signo::SIGTTIN => DefaultSignalAction::Stop,
            Signo::SIGTTOU => DefaultSignalAction::Stop,
            Signo::SIGURG => DefaultSignalAction::Ignore,
            Signo::SIGXCPU => DefaultSignalAction::CoreDump,
            Signo::SIGXFSZ => DefaultSignalAction::CoreDump,
            Signo::SIGVTALRM => DefaultSignalAction::Terminate,
            Signo::SIGPROF => DefaultSignalAction::Terminate,
            Signo::SIGWINCH => DefaultSignalAction::Ignore,
            Signo::SIGIO => DefaultSignalAction::Terminate,
            Signo::SIGPWR => DefaultSignalAction::Terminate,
            Signo::SIGSYS => DefaultSignalAction::CoreDump,
            _ => DefaultSignalAction::Ignore,
        }
    }
}

/// Signal set. Compatible with `struct sigset_t` in libc.
#[derive(Default, Clone, Copy, Not, BitOr, BitOrAssign, BitAnd, BitAndAssign)]
#[repr(transparent)]
pub struct SignalSet(u64);

impl SignalSet {
    fn signo_bit(signo: Signo) -> u64 {
        1 << (signo as u8 - 1)
    }

    /// Adds a signal to the set.
    pub fn add(&mut self, signal: Signo) -> bool {
        let bit = Self::signo_bit(signal);
        if self.0 & bit != 0 {
            return false;
        }
        self.0 |= bit;
        true
    }

    /// Removes a signal from the set.
    pub fn remove(&mut self, signal: Signo) -> bool {
        let bit = Self::signo_bit(signal);
        if self.0 & bit == 0 {
            return false;
        }
        self.0 &= !bit;
        true
    }

    /// Checks if the set contains a signal.
    pub fn has(&self, signal: Signo) -> bool {
        (self.0 & Self::signo_bit(signal)) != 0
    }

    /// Returns `true` if the set is empty.
    pub fn is_empty(&self) -> bool {
        self.0 == 0
    }

    /// Dequeues the a signal in `mask` from this set, if any.
    pub fn dequeue(&mut self, mask: &SignalSet) -> Option<Signo> {
        let bits = self.0 & mask.0;
        if bits == 0 {
            None
        } else {
            let signal = bits.trailing_zeros();
            self.0 &= !(1 << signal);
            Signo::from_repr((signal + 1) as u8)
        }
    }
}

impl From<SignalSet> for kernel_sigset_t {
    fn from(value: SignalSet) -> Self {
        // SAFETY: `kernel_sigset_t` always has the same layout as `[c_ulong; 1]`.
        unsafe { mem::transmute::<u64, kernel_sigset_t>(value.0) }
    }
}

impl From<kernel_sigset_t> for SignalSet {
    fn from(value: kernel_sigset_t) -> Self {
        // SAFETY: `kernel_sigset_t` always has the same layout as `[c_ulong; 1]`.
        Self(unsafe { mem::transmute::<kernel_sigset_t, u64>(value) })
    }
}

impl fmt::Debug for SignalSet {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut debug = f.debug_set();
        for signo in Signo::iter() {
            if self.has(signo) {
                debug.entry(&signo);
            }
        }
        debug.finish()
    }
}

/// Signal information. Compatible with `struct siginfo` in libc.
#[derive(Clone)]
#[repr(transparent)]
pub struct SignalInfo(pub siginfo_t);

impl SignalInfo {
    pub fn new_kernel(signo: Signo) -> Self {
        // FIXME: Zeroable
        let mut result: Self = unsafe { mem::zeroed() };
        result.set_signo(signo);
        result.set_code(SI_KERNEL as _);
        result
    }

    pub fn new_user(signo: Signo, code: i32, pid: u32, uid: u32) -> Self {
        // FIXME: Zeroable
        let mut result: Self = unsafe { mem::zeroed() };
        result.set_signo(signo);
        result.set_code(code);
        result
            .0
            .__bindgen_anon_1
            .__bindgen_anon_1
            ._sifields
            ._sigchld
            ._pid = pid as _;
        result
            .0
            .__bindgen_anon_1
            .__bindgen_anon_1
            ._sifields
            ._sigchld
            ._uid = uid as _;
        result
    }

    pub fn pid(&self) -> u32 {
        // SAFETY: _sigchld._pid and _kill._pid share the same offset in
        // the sifields union.
        unsafe {
            self.0
                .__bindgen_anon_1
                .__bindgen_anon_1
                ._sifields
                ._sigchld
                ._pid as u32
        }
    }

    pub fn uid(&self) -> u32 {
        // SAFETY: _sigchld._uid and _kill._uid share the same offset in
        // the sifields union.
        unsafe {
            self.0
                .__bindgen_anon_1
                .__bindgen_anon_1
                ._sifields
                ._sigchld
                ._uid
        }
    }

    /// Construct a SignalInfo for a child process state change (SIGCHLD).
    ///
    /// - `child_pid`: PID of the child process
    /// - `child_uid`: UID of the child process
    /// - `code`: one of `CLD_EXITED`, `CLD_KILLED`, `CLD_DUMPED`, etc.
    /// - `status`: for `CLD_EXITED` the raw exit value (0–255); for
    ///   `CLD_KILLED`/`CLD_DUMPED` the signal number that killed the process.
    pub fn new_sigchld(child_pid: u32, child_uid: u32, code: i32, status: i32) -> Self {
        let mut result: Self = unsafe { mem::zeroed() };
        result.set_signo(Signo::SIGCHLD);
        result.set_code(code);
        // SAFETY: The siginfo union is zeroed above. We access the _sigchld
        // variant which is valid for SIGCHLD signals.
        unsafe {
            let fields = &mut result
                .0
                .__bindgen_anon_1
                .__bindgen_anon_1
                ._sifields
                ._sigchld;
            fields._pid = child_pid as _;
            fields._uid = child_uid as _;
            fields._status = status;
        }
        result
    }

    /// Construct a SignalInfo for a POSIX timer expiry.
    ///
    /// Sets si_code = SI_TIMER and si_value = sigev_value (the value the user
    /// passed to timer_create). The si_value is stored in the _timer._sigval
    /// field of the siginfo_t union.
    pub fn new_timer(signo: Signo, sigev_value: i64) -> Self {
        // FIXME: Zeroable
        let mut result: Self = unsafe { mem::zeroed() };
        result.set_signo(signo);
        result.set_code(SI_TIMER);
        // SAFETY: _sifields._timer._sigval is the sigev_value union in the
        // Linux siginfo_t layout generated by bindgen. We store the value as
        // sival_ptr (pointer-sized) to preserve all 64 bits on both 32-bit
        // and 64-bit architectures.
        result
            .0
            .__bindgen_anon_1
            .__bindgen_anon_1
            ._sifields
            ._timer
            ._sigval
            .sival_ptr = sigev_value as *mut core::ffi::c_void;
        result
    }

    pub fn signo(&self) -> Signo {
        unsafe { Signo::from_repr(self.0.__bindgen_anon_1.__bindgen_anon_1.si_signo as _).unwrap() }
    }

    pub fn set_signo(&mut self, signo: Signo) {
        self.0.__bindgen_anon_1.__bindgen_anon_1.si_signo = signo as _;
    }

    pub fn code(&self) -> i32 {
        unsafe { self.0.__bindgen_anon_1.__bindgen_anon_1.si_code }
    }

    pub fn set_code(&mut self, code: i32) {
        self.0.__bindgen_anon_1.__bindgen_anon_1.si_code = code;
    }

    pub fn errno(&self) -> i32 {
        // SAFETY: The union layout matches Linux's siginfo_t definition. bindgen keeps
        // this layout, so it is safe to read the errno field through the
        // anonymous union.
        unsafe { self.0.__bindgen_anon_1.__bindgen_anon_1.si_errno }
    }
}

unsafe impl Send for SignalInfo {}
unsafe impl Sync for SignalInfo {}

impl fmt::Debug for SignalInfo {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SignalInfo")
            .field("signo", &self.signo())
            .field("code", &self.code())
            .finish()
    }
}

/// Signal stack. Compatible with `struct sigaltstack` in libc.
#[repr(C)]
#[derive(Clone)]
pub struct SignalStack {
    pub sp: usize,
    pub flags: u32,
    pub size: usize,
}

impl Default for SignalStack {
    fn default() -> Self {
        Self {
            sp: 0,
            flags: SS_DISABLE,
            size: 0,
        }
    }
}

impl SignalStack {
    /// Checks if signal stack is disabled.
    pub fn disabled(&self) -> bool {
        self.flags & SS_DISABLE != 0
    }

    /// Returns this stack as reported while the thread is running on it.
    pub fn on_stack(mut self) -> Self {
        if !self.disabled() {
            self.flags |= SS_ONSTACK;
        }
        self
    }

    /// Removes kernel-owned state bits before storing a user-provided stack.
    pub fn without_runtime_flags(mut self) -> Self {
        self.flags &= !SS_ONSTACK;
        self
    }
}