Vendor dependencies
Let's see how I like this workflow.
This commit is contained in:
parent
34d1830413
commit
9c435dc440
7500 changed files with 1665121 additions and 99 deletions
261
vendor/signal-hook/src/low_level/siginfo.rs
vendored
Normal file
261
vendor/signal-hook/src/low_level/siginfo.rs
vendored
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
//! Extracting more information from the C [`siginfo_t`] structure.
|
||||
//!
|
||||
//! See [`Origin`].
|
||||
|
||||
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||
|
||||
use libc::{c_int, pid_t, siginfo_t, uid_t};
|
||||
|
||||
use crate::low_level;
|
||||
|
||||
// Careful: make sure the signature and the constants match the C source
|
||||
extern "C" {
|
||||
fn sighook_signal_cause(info: &siginfo_t) -> ICause;
|
||||
fn sighook_signal_pid(info: &siginfo_t) -> pid_t;
|
||||
fn sighook_signal_uid(info: &siginfo_t) -> uid_t;
|
||||
}
|
||||
|
||||
// Warning: must be in sync with the C code
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
#[repr(u8)]
|
||||
// For some reason, the fact it comes from the C makes rustc emit warning that *some* of these are
|
||||
// not constructed. No idea why only some of them.
|
||||
#[allow(dead_code)]
|
||||
enum ICause {
|
||||
Unknown = 0,
|
||||
Kernel = 1,
|
||||
User = 2,
|
||||
TKill = 3,
|
||||
Queue = 4,
|
||||
MesgQ = 5,
|
||||
Exited = 6,
|
||||
Killed = 7,
|
||||
Dumped = 8,
|
||||
Trapped = 9,
|
||||
Stopped = 10,
|
||||
Continued = 11,
|
||||
}
|
||||
|
||||
impl ICause {
|
||||
// The MacOs doesn't use the SI_* constants and leaves si_code at 0. But it doesn't use an
|
||||
// union, it has a good-behaved struct with fields and therefore we *can* read the values,
|
||||
// even though they'd contain nonsense (zeroes). We wipe that out later.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn has_process(self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn has_process(self) -> bool {
|
||||
use ICause::*;
|
||||
match self {
|
||||
Unknown | Kernel => false,
|
||||
User | TKill | Queue | MesgQ | Exited | Killed | Dumped | Trapped | Stopped
|
||||
| Continued => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about process, as presented in the signal metadata.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub struct Process {
|
||||
/// The process ID.
|
||||
pub pid: pid_t,
|
||||
|
||||
/// The user owning the process.
|
||||
pub uid: uid_t,
|
||||
}
|
||||
|
||||
impl Process {
|
||||
/**
|
||||
* Extract the process information.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* The `info` must have a `si_code` corresponding to some situation that has the `si_pid`
|
||||
* and `si_uid` filled in.
|
||||
*/
|
||||
unsafe fn extract(info: &siginfo_t) -> Self {
|
||||
Self {
|
||||
pid: sighook_signal_pid(info),
|
||||
uid: sighook_signal_uid(info),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The means by which a signal was sent by other process.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Sent {
|
||||
/// The `kill` call.
|
||||
User,
|
||||
|
||||
/// The `tkill` call.
|
||||
///
|
||||
/// This is likely linux specific.
|
||||
TKill,
|
||||
|
||||
/// `sigqueue`.
|
||||
Queue,
|
||||
|
||||
/// `mq_notify`.
|
||||
MesgQ,
|
||||
}
|
||||
|
||||
/// A child changed its state.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Chld {
|
||||
/// The child exited normally.
|
||||
Exited,
|
||||
|
||||
/// It got killed by a signal.
|
||||
Killed,
|
||||
|
||||
/// It got killed by a signal and dumped core.
|
||||
Dumped,
|
||||
|
||||
/// The child was trapped by a `SIGTRAP` signal.
|
||||
Trapped,
|
||||
|
||||
/// The child got stopped.
|
||||
Stopped,
|
||||
|
||||
/// The child continued (after being stopped).
|
||||
Continued,
|
||||
}
|
||||
|
||||
/// What caused a signal.
|
||||
///
|
||||
/// This is a best-effort (and possibly incomplete) representation of the C `siginfo_t::si_code`.
|
||||
/// It may differ between OSes and may be extended in future versions.
|
||||
///
|
||||
/// Note that this doesn't contain all the „fault“ signals (`SIGILL`, `SIGSEGV` and similar).
|
||||
/// There's no reasonable way to use the exfiltrators with them, since the handler either needs to
|
||||
/// terminate the process or somehow recover from the situation. Things based on exfiltrators do
|
||||
/// neither, which would cause an UB and therefore these values just don't make sense.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Cause {
|
||||
/// The cause is unknown.
|
||||
///
|
||||
/// Some systems don't fill this in. Some systems have values we don't understand. Some signals
|
||||
/// don't have specific reasons to come to being.
|
||||
Unknown,
|
||||
|
||||
/// Sent by the kernel.
|
||||
///
|
||||
/// This probably exists only on Linux.
|
||||
Kernel,
|
||||
|
||||
/// The signal was sent by other process.
|
||||
Sent(Sent),
|
||||
|
||||
/// A `SIGCHLD`, caused by a child process changing state.
|
||||
Chld(Chld),
|
||||
}
|
||||
|
||||
impl From<ICause> for Cause {
|
||||
fn from(c: ICause) -> Cause {
|
||||
match c {
|
||||
ICause::Kernel => Cause::Kernel,
|
||||
ICause::User => Cause::Sent(Sent::User),
|
||||
ICause::TKill => Cause::Sent(Sent::TKill),
|
||||
ICause::Queue => Cause::Sent(Sent::Queue),
|
||||
ICause::MesgQ => Cause::Sent(Sent::MesgQ),
|
||||
ICause::Exited => Cause::Chld(Chld::Exited),
|
||||
ICause::Killed => Cause::Chld(Chld::Killed),
|
||||
ICause::Dumped => Cause::Chld(Chld::Dumped),
|
||||
ICause::Trapped => Cause::Chld(Chld::Trapped),
|
||||
ICause::Stopped => Cause::Chld(Chld::Stopped),
|
||||
ICause::Continued => Cause::Chld(Chld::Continued),
|
||||
// Unknown and possibly others if the underlying lib is updated
|
||||
_ => Cause::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a signal and its origin.
|
||||
///
|
||||
/// This is produced by the [`WithOrigin`] exfiltrator (or can be [extracted][Origin::extract] from
|
||||
/// `siginfo_t` by hand).
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub struct Origin {
|
||||
/// The signal that happened.
|
||||
pub signal: c_int,
|
||||
|
||||
/// Information about the process that caused the signal.
|
||||
///
|
||||
/// Note that not all signals are caused by a specific process or have the information
|
||||
/// available („fault“ signals like `SIGBUS` don't have, any signal may be sent by the kernel
|
||||
/// instead of a specific process).
|
||||
///
|
||||
/// This is filled in whenever available. For most signals, this is the process that sent the
|
||||
/// signal (by `kill` or similar), for `SIGCHLD` it is the child that caused the signal.
|
||||
pub process: Option<Process>,
|
||||
|
||||
/// How the signal happened.
|
||||
///
|
||||
/// This is a best-effort value. In particular, some systems may have causes not known to this
|
||||
/// library. Some other systems (MacOS) does not fill the value in so there's no way to know.
|
||||
/// In all these cases, this will contain [`Cause::Unknown`].
|
||||
///
|
||||
/// Some values are platform specific and not available on other systems.
|
||||
///
|
||||
/// Future versions may enrich the enum by further values.
|
||||
pub cause: Cause,
|
||||
}
|
||||
|
||||
impl Debug for Origin {
|
||||
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
|
||||
fn named_signal(sig: c_int) -> String {
|
||||
low_level::signal_name(sig)
|
||||
.map(|n| format!("{} ({})", n, sig))
|
||||
.unwrap_or_else(|| sig.to_string())
|
||||
}
|
||||
fmt.debug_struct("Origin")
|
||||
.field("signal", &named_signal(self.signal))
|
||||
.field("process", &self.process)
|
||||
.field("cause", &self.cause)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Origin {
|
||||
/// Extracts the Origin from a raw `siginfo_t` structure.
|
||||
///
|
||||
/// This function is async-signal-safe, can be called inside a signal handler.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// On systems where the structure is backed by an union on the C side, this requires the
|
||||
/// `si_code` and `si_signo` fields must be set properly according to what fields are
|
||||
/// available.
|
||||
///
|
||||
/// The value passed by kernel satisfies this, care must be taken only when constructed
|
||||
/// manually.
|
||||
pub unsafe fn extract(info: &siginfo_t) -> Self {
|
||||
let cause = sighook_signal_cause(info);
|
||||
let process = if cause.has_process() {
|
||||
let process = Process::extract(info);
|
||||
// On macos we don't have the si_code to go by, but we can go by the values being
|
||||
// empty there.
|
||||
if cfg!(target_os = "macos") && process.pid == 0 && process.uid == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(process)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let signal = info.si_signo;
|
||||
Origin {
|
||||
cause: cause.into(),
|
||||
signal,
|
||||
process,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue