char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somrearrange libs into lib and extensions into extsf4a0f5b

main
4.3 KiB143 linesraw
1use std::io;
2use std::os::unix::process::ExitStatusExt;
3use std::process::ExitStatus;
4use std::sync::OnceLock;
5
6const SIGNALS: [(&str, libc::c_int); 16] = [
7    ("SIGHUP", libc::SIGHUP),
8    ("SIGINT", libc::SIGINT),
9    ("SIGQUIT", libc::SIGQUIT),
10    ("SIGABRT", libc::SIGABRT),
11    ("SIGKILL", libc::SIGKILL),
12    ("SIGUSR1", libc::SIGUSR1),
13    ("SIGUSR2", libc::SIGUSR2),
14    ("SIGPIPE", libc::SIGPIPE),
15    ("SIGALRM", libc::SIGALRM),
16    ("SIGTERM", libc::SIGTERM),
17    ("SIGCONT", libc::SIGCONT),
18    ("SIGSTOP", libc::SIGSTOP),
19    ("SIGTSTP", libc::SIGTSTP),
20    ("SIGTTIN", libc::SIGTTIN),
21    ("SIGTTOU", libc::SIGTTOU),
22    ("SIGWINCH", libc::SIGWINCH),
23];
24
25pub(crate) fn signal_number(name: &str) -> io::Result<libc::c_int> {
26    SIGNALS
27        .iter()
28        .find(|(text, _)| *text == name)
29        .map(|(_, number)| *number)
30        .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "Unsupported subprocess signal"))
31}
32
33/// Whether this names a real (if not necessarily listenable) signal.
34pub fn known_signal(name: &str) -> bool {
35    SIGNALS.iter().any(|(text, _)| *text == name)
36}
37
38pub fn validate_signal(name: &str) -> io::Result<()> {
39    signal_number(name).map(drop)
40}
41
42pub fn exit_signal(status: &ExitStatus) -> Option<String> {
43    let number = status.signal()?;
44    Some(
45        SIGNALS
46            .iter()
47            .find(|(_, value)| *value == number)
48            .map_or_else(|| format!("SIG{number}"), |(name, _)| (*name).into()),
49    )
50}
51
52/// A listenable signal's inherited disposition, snapshotted once at
53/// process start. Every fate decision consults the snapshot instead of a
54/// live query, which later installs (supervisor, feeds, owners) would
55/// otherwise corrupt.
56#[derive(Clone, Copy, PartialEq)]
57pub enum Prior {
58    Default,
59    Ignored,
60    Owned,
61}
62
63/// The signals whose startup disposition the snapshot records: every
64/// signal listener feeds can bind plus the supervisor's own two.
65const SNAPSHOT_SIGNALS: [libc::c_int; 6] = [
66    libc::SIGINT,
67    libc::SIGTERM,
68    libc::SIGHUP,
69    libc::SIGUSR1,
70    libc::SIGUSR2,
71    libc::SIGWINCH,
72];
73
74static ORIGINALS: OnceLock<[(libc::c_int, Prior); 6]> = OnceLock::new();
75
76/// Records each listenable signal's inherited disposition. Call once
77/// at process start, before the supervisor, feeds, or owners install
78/// anything; later installs must not move the baseline.
79pub fn snapshot_originals() {
80    let _ = ORIGINALS.get_or_init(|| {
81        SNAPSHOT_SIGNALS.map(|number| {
82            let mut old: libc::sigaction = unsafe { std::mem::zeroed() };
83            unsafe {
84                libc::sigaction(number, std::ptr::null(), &mut old);
85            }
86            (
87                number,
88                if old.sa_sigaction == libc::SIG_IGN {
89                    Prior::Ignored
90                } else if old.sa_sigaction == libc::SIG_DFL {
91                    Prior::Default
92                } else {
93                    Prior::Owned
94                },
95            )
96        })
97    });
98}
99/// A listenable signal's startup disposition. Falls back to a live
100/// query when the process never snapshotted (in-process tests).
101pub fn original_prior(number: libc::c_int) -> Prior {
102    if let Some(found) = ORIGINALS.get().and_then(|originals| {
103        originals
104            .iter()
105            .find(|(signal, _)| *signal == number)
106            .map(|(_, prior)| *prior)
107    }) {
108        return found;
109    }
110    let mut old: libc::sigaction = unsafe { std::mem::zeroed() };
111    unsafe {
112        libc::sigaction(number, std::ptr::null(), &mut old);
113    }
114    if old.sa_sigaction == libc::SIG_IGN {
115        Prior::Ignored
116    } else if old.sa_sigaction == libc::SIG_DFL {
117        Prior::Default
118    } else {
119        Prior::Owned
120    }
121}
122/// Whether this signal was ignored at process start. Consult the
123/// snapshot, never the live disposition.
124pub fn originally_ignored(number: libc::c_int) -> bool {
125    original_prior(number) == Prior::Ignored
126}
127
128pub(crate) fn send(pid: u32, group: bool, number: libc::c_int) -> std::io::Result<bool> {
129    let target = if group {
130        -(pid as libc::pid_t)
131    } else {
132        pid as libc::pid_t
133    };
134    if unsafe { libc::kill(target, number) } == 0 {
135        return Ok(true);
136    }
137    let error = std::io::Error::last_os_error();
138    if error.raw_os_error() == Some(libc::ESRCH) {
139        Ok(false)
140    } else {
141        Err(error)
142    }
143}