mirror of
https://github.com/openharmony/third_party_rust_signal-hook.git
synced 2026-07-01 20:44:18 -04:00
Add the signal-hook-sys crate
For C code that extracts stuff. Because the libc crate is not really very helpful there: * The SI_* constants are missing. * The si_pid/si_uid methods are available only on few selected targets, not everywhere.
This commit is contained in:
@@ -21,7 +21,10 @@ jobs:
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
# Introduction of self: Arc<..>, needed for the iterator module
|
||||
- 1.36.0
|
||||
# Introduction of non_exhaustive, used at certain exfiltrators
|
||||
- 1.40.0
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -170,7 +173,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v1
|
||||
|
||||
- name: Run clippy linter
|
||||
run: cargo clippy --all --tests -- -D clippy::all -D warnings
|
||||
run: cargo clippy --all --all-features --tests -- -D clippy::all -D warnings
|
||||
|
||||
# There's bunch of platforms that have some weird quirks (or we don't know
|
||||
# that they don't). While fully compiling and testing on them is a bit of a
|
||||
@@ -210,7 +213,7 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v1
|
||||
|
||||
- name: Run the check
|
||||
run: cargo check --all --tests --target=${{ matrix.target }}
|
||||
run: cargo check --all --all-features --tests --target=${{ matrix.target }}
|
||||
|
||||
# Check some either weirder platforms, but these support only the base crate,
|
||||
# not all the fancy async ones.
|
||||
@@ -242,4 +245,4 @@ jobs:
|
||||
uses: Swatinem/rust-cache@v1
|
||||
|
||||
- name: Run the check
|
||||
run: cargo check --tests --target=${{ matrix.target }}
|
||||
run: cargo check --tests --all-features --target=${{ matrix.target }}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
tags
|
||||
.ccls-cache
|
||||
|
||||
Generated
+9
@@ -908,6 +908,7 @@ dependencies = [
|
||||
"libc",
|
||||
"serial_test",
|
||||
"signal-hook-registry 1.2.2",
|
||||
"signal-hook-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -950,6 +951,14 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-sys"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-tokio"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -19,6 +19,7 @@ maintenance = { status = "actively-developed" }
|
||||
[features]
|
||||
default = ["iterator"]
|
||||
iterator = []
|
||||
extended-siginfo = ["iterator", "signal-hook-sys"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
@@ -27,11 +28,13 @@ members = [
|
||||
"signal-hook-tokio",
|
||||
"signal-hook-mio",
|
||||
"signal-hook-async-std",
|
||||
"signal-hook-sys",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
libc = "~0.2"
|
||||
signal-hook-registry = { version = "~1.2", path = "signal-hook-registry" }
|
||||
signal-hook-sys = { version = "~0.1", path = "signal-hook-sys", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serial_test = "~0.5"
|
||||
|
||||
+4
-2
@@ -8,16 +8,18 @@
|
||||
set -ex
|
||||
|
||||
rm -f Cargo.lock
|
||||
cargo build --all --exclude signal-hook-async-std
|
||||
cargo build --all --exclude signal-hook-async-std --exclude signal-hook-sys
|
||||
|
||||
if [ "$RUST_VERSION" = 1.36.0 ] ; then
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$OS" = "windows-latest" ] ; then
|
||||
if [ "$OS" = "windows-latest" ] || [ "$RUST_VERSION" = 1.40.0 ]; then
|
||||
# The async support crates rely on the iterator module
|
||||
# which isn't available for windows. So exclude them
|
||||
# from the build.
|
||||
|
||||
# Also, some dependencies of the runtimes don't like 1.40.
|
||||
EXCLUDE_FROM_BUILD="--exclude signal-hook-mio --exclude signal-hook-tokio --exclude signal-hook-async-std"
|
||||
else
|
||||
EXCLUDE_FROM_BUILD=""
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "signal-hook-sys"
|
||||
version = "0.1.0"
|
||||
authors = ["Michal 'vorner' Vaner <vorner@vorner.cz>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = "~0.2"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "~1"
|
||||
@@ -0,0 +1,5 @@
|
||||
use cc::Build;
|
||||
|
||||
fn main() {
|
||||
Build::new().file("src/extract.c").compile("extract");
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct Const {
|
||||
int native;
|
||||
// The signal this applies to, or -1 if it applies to anything.
|
||||
int signal;
|
||||
uint8_t translated;
|
||||
};
|
||||
|
||||
// Warning: must be in sync with the rust source code
|
||||
struct Const consts[] = {
|
||||
#ifdef SI_KERNEL
|
||||
{ SI_KERNEL, -1, 1 },
|
||||
#endif
|
||||
{ SI_USER, -1, 2 },
|
||||
#ifdef SI_TKILL
|
||||
{ SI_TKILL, -1, 3 },
|
||||
#endif
|
||||
{ SI_QUEUE, -1, 4 },
|
||||
{ SI_MESGQ, -1, 5 },
|
||||
{ CLD_EXITED, SIGCHLD, 6 },
|
||||
{ CLD_KILLED, SIGCHLD, 7 },
|
||||
{ CLD_DUMPED, SIGCHLD, 8 },
|
||||
{ CLD_TRAPPED, SIGCHLD, 9 },
|
||||
{ CLD_STOPPED, SIGCHLD, 10 },
|
||||
{ CLD_CONTINUED, SIGCHLD, 11 },
|
||||
};
|
||||
|
||||
uint8_t sighook_signal_cause(const siginfo_t *info) {
|
||||
const size_t const_len = sizeof consts / sizeof *consts;
|
||||
size_t i;
|
||||
for (i = 0; i < const_len; i ++) {
|
||||
if (
|
||||
consts[i].native == info->si_code &&
|
||||
(consts[i].signal == -1 || consts[i].signal == info->si_signo)
|
||||
) {
|
||||
return consts[i].translated;
|
||||
}
|
||||
}
|
||||
return 0; // The "Unknown" variant
|
||||
}
|
||||
|
||||
pid_t sighook_signal_pid(const siginfo_t *info) {
|
||||
return info->si_pid;
|
||||
}
|
||||
|
||||
uid_t sighook_signal_uid(const siginfo_t *info) {
|
||||
return info->si_uid;
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
//! Low-level internals of [`signal-hook`](https://docs.rs/signal-hook).
|
||||
//!
|
||||
//! This crate contains some internal APIs, split off to a separate crate for technical reasons. Do
|
||||
//! not use directly. There are no stability guarantees, no documentation and you should use
|
||||
//! `signal-hook` directly.
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod internal {
|
||||
use libc::{pid_t, siginfo_t, uid_t};
|
||||
|
||||
// Careful: make sure the signature and the constants match the C source
|
||||
extern "C" {
|
||||
fn sighook_signal_cause(info: &siginfo_t) -> Cause;
|
||||
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)]
|
||||
pub enum Cause {
|
||||
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 Cause {
|
||||
// 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 Cause::*;
|
||||
match self {
|
||||
Unknown | Kernel => false,
|
||||
User | TKill | Queue | MesgQ | Exited | Killed | Dumped | Trapped | Stopped
|
||||
| Continued => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub struct Process {
|
||||
pub pid: pid_t,
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn to_u64(self) -> u64 {
|
||||
let pid = self.pid as u32; // With overflow for negative ones
|
||||
let uid = self.uid as u32;
|
||||
((pid as u64) << 32) | (uid as u64)
|
||||
}
|
||||
|
||||
pub const fn from_u64(encoded: u64) -> Self {
|
||||
let pid = ((encoded >> 32) as u32) as _;
|
||||
let uid = (encoded as u32) as _;
|
||||
Self { pid, uid }
|
||||
}
|
||||
|
||||
pub const EMPTY: Self = Self { pid: -1, uid: 0 };
|
||||
|
||||
pub const NO_PROCESS: Self = Self { pid: -1, uid: 1 };
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub struct SigInfo {
|
||||
pub cause: Cause,
|
||||
pub process: Option<Process>,
|
||||
}
|
||||
|
||||
impl SigInfo {
|
||||
// Note: shall be async-signal-safe
|
||||
pub fn extract(info: &siginfo_t) -> Self {
|
||||
let cause = unsafe { sighook_signal_cause(info) };
|
||||
let process = if cause.has_process() {
|
||||
let process = unsafe { 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
|
||||
};
|
||||
Self { cause, process }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@
|
||||
//! Currently, the trait is sealed and all methods hidden. This is likely temporary, until some
|
||||
//! experience with them is gained.
|
||||
|
||||
#[cfg(feature = "extended-siginfo")]
|
||||
pub mod origin;
|
||||
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use libc::{c_int, siginfo_t};
|
||||
@@ -0,0 +1,123 @@
|
||||
//! An exfiltrator providing the process that caused the signal.
|
||||
//!
|
||||
//! The [`WithOrigin`] is an [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that
|
||||
//! provides the information about sending process in addition to the signal number, through the
|
||||
//! [`Origin`] type.
|
||||
//!
|
||||
//! See the [`WithOrigin`] example.
|
||||
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use libc::{c_int, pid_t, siginfo_t, uid_t};
|
||||
use signal_hook_sys::internal::{Process as IProcess, SigInfo};
|
||||
|
||||
use super::sealed::Exfiltrator;
|
||||
|
||||
/// 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 From<IProcess> for Process {
|
||||
fn from(p: IProcess) -> Self {
|
||||
Self {
|
||||
pid: p.pid,
|
||||
uid: p.uid,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about a signal and its origin.
|
||||
///
|
||||
/// This is produced by the [`WithOrigin`] exfiltrator. See the example there.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
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>,
|
||||
// TODO: Figure out a better encoding somehow and expose other info, including the Cause
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
pub struct OriginStorage(AtomicU64);
|
||||
|
||||
impl Default for OriginStorage {
|
||||
fn default() -> Self {
|
||||
Self(AtomicU64::new(IProcess::EMPTY.to_u64()))
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that produces [`Origin`] of
|
||||
/// signals.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use signal_hook::SIGUSR1;
|
||||
/// # use signal_hook::iterator::SignalsInfo;
|
||||
/// # use signal_hook::iterator::exfiltrator::origin::WithOrigin;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), std::io::Error> {
|
||||
/// // Subscribe to SIGUSR1, with information about the process.
|
||||
/// let signals = SignalsInfo::<WithOrigin>::new(&[SIGUSR1])?;
|
||||
///
|
||||
/// // Send a signal to ourselves.
|
||||
/// let my_pid = unsafe { libc::getpid() };
|
||||
/// unsafe { libc::kill(my_pid, SIGUSR1) };
|
||||
///
|
||||
/// // Grab the signal and look into the details.
|
||||
/// let received = signals.forever().next().unwrap();
|
||||
///
|
||||
/// assert_eq!(SIGUSR1, received.signal);
|
||||
/// assert_eq!(my_pid, received.process.unwrap().pid);
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct WithOrigin;
|
||||
|
||||
unsafe impl Exfiltrator for WithOrigin {
|
||||
type Storage = OriginStorage;
|
||||
type Output = Origin;
|
||||
fn supports_signal(&self, _: c_int) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn store(&self, slot: &OriginStorage, _: c_int, info: &siginfo_t) {
|
||||
let value = SigInfo::extract(info)
|
||||
.process
|
||||
.unwrap_or(IProcess::NO_PROCESS)
|
||||
.to_u64();
|
||||
slot.0.store(value, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn load(&self, slot: &OriginStorage, signal: c_int) -> Option<Origin> {
|
||||
let value = slot.0.swap(IProcess::EMPTY.to_u64(), Ordering::SeqCst);
|
||||
match IProcess::from_u64(value) {
|
||||
IProcess::EMPTY => None,
|
||||
IProcess::NO_PROCESS => Some(Origin {
|
||||
signal,
|
||||
process: None,
|
||||
}),
|
||||
process => Some(Origin {
|
||||
signal,
|
||||
process: Some(process.into()),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -4,7 +4,7 @@
|
||||
//! the [`SignalsInfo`] structure which is able to iterate over the
|
||||
//! incoming signals. The structure is parametrized by an
|
||||
//! [`Exfiltrator`][self::exfiltrator::Exfiltrator], which specifies what information is returned
|
||||
//! for each delivered signal.
|
||||
//! for each delivered signal. Note that some exfiltrators are behind a feature flag.
|
||||
//!
|
||||
//! The [`Signals`] is a type alias for the common case when it is enough to get the signal number.
|
||||
//!
|
||||
@@ -89,6 +89,12 @@ use self::exfiltrator::{Exfiltrator, SignalOnly};
|
||||
/// The controller handle can be shared between as many threads as you like using its
|
||||
/// [`clone`][Handle::clone] method.
|
||||
///
|
||||
/// # Exfiltrators
|
||||
///
|
||||
/// The [`SignalOnly]` provides only the signal number. There are further exfiltrators available in
|
||||
/// the [`exfiltrator`] module. Note that some of them are behind feature flags that need to be
|
||||
/// enabled.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
|
||||
Reference in New Issue
Block a user