signal-hook-0.3.17/.cargo_vcs_info.json0000644000000001360000000000100133370ustar { "git": { "sha1": "7dfed903c065962164699c0c015c36f838135fa6" }, "path_in_vcs": "" }signal-hook-0.3.17/.github/actions-rs/grcov.yml000064400000000000000000000001551046102023000174150ustar 00000000000000branch: true ignore-not-existing: true llvm: true filter: covered output-type: lcov output-path: ./lcov.info signal-hook-0.3.17/.github/codecov.yml000064400000000000000000000002711046102023000156340ustar 00000000000000comment: layout: "diff, flags, files" require_changes: true coverage: status: project: default: # Don't fail on coverage, only show informational: true signal-hook-0.3.17/.github/workflows/audit.yaml000064400000000000000000000005341046102023000175200ustar 00000000000000name: Security audit on: pull_request: push: branches: - master schedule: - cron: '0 0 * * 0' jobs: security_audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 with: token: ${{ secrets.GITHUB_TOKEN }} signal-hook-0.3.17/.github/workflows/coverage.yaml000064400000000000000000000027441046102023000202120ustar 00000000000000name: Coverage on: push: branches: - master pull_request: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full jobs: coverage: name: Coverage runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: nightly profile: minimal default: true - name: Pre-installing grcov uses: actions-rs/install@v0.1 with: crate: grcov use-tool-cache: true - name: Restore cache uses: Swatinem/rust-cache@v1 - name: Run tests run: cargo test --all --all-features env: CARGO_INCREMENTAL: '0' RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' # This seems not to work, the C code can't seem to find the gcov functions. By not providing this, we probably give up coverage from these :-( #CFLAGS: --coverage #CXXFLAGS: --coverage - name: Generate coverage uses: actions-rs/grcov@v0.1 - name: Upload to codecov.io uses: codecov/codecov-action@5a8bb4701eca7ba3673f21664b887f652c58d0a3 with: token: ${{ secrets.CODECOV_TOKEN }} signal-hook-0.3.17/.github/workflows/test.yaml000064400000000000000000000137301046102023000173730ustar 00000000000000name: test on: push: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full jobs: test: name: Build & test strategy: fail-fast: false matrix: os: - ubuntu-latest - macos-latest - windows-latest rust: - 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 }} steps: - name: checkout uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} default: true profile: minimal - name: Restore cache uses: Swatinem/rust-cache@v1 - name: Build & test env: RUST_VERSION: ${{ matrix.rust }} OS: ${{ matrix.os }} run: ./ci-check.sh rustfmt: name: Check formatting runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable default: true components: rustfmt - run: cargo fmt --all -- --check links: name: Check documentation links runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable default: true - name: Restore cache uses: Swatinem/rust-cache@v1 - name: Install cargo-deadlinks uses: actions-rs/install@v0.1 with: crate: cargo-deadlinks use-tool-cache: true - name: Check links run: | for package in $(cargo metadata --no-deps --format-version=1 | jq -r '.packages[] | .name'); do cargo rustdoc -p "$package" --all-features -- -D warnings dname=$(echo "$package" | tr '-' '_') cargo deadlinks --dir "target/doc/$dname" --check-http --ignore-fragments done ancient-registry: name: Check compilation of signal-hook-registry on 1.26.0 strategy: matrix: os: - ubuntu-latest - macos-latest - windows-latest runs-on: ${{ matrix.os }} steps: - name: checkout uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: 1.26.0 default: true profile: minimal - name: Restore cache uses: Swatinem/rust-cache@v1 - name: Check compilation run: | rm Cargo.toml cd signal-hook-registry sed -i -e '/signal-hook =/d' Cargo.toml cargo check ancient: name: Check compilation on 1.31.0 strategy: matrix: os: - ubuntu-latest - macos-latest - windows-latest runs-on: ${{ matrix.os }} steps: - name: checkout uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: 1.31.0 default: true profile: minimal - name: Restore cache uses: Swatinem/rust-cache@v1 - name: Check compilation run: | rm Cargo.lock cargo update cargo check --no-default-features clippy: name: Clippy lints runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal default: true components: clippy - name: Restore cache uses: Swatinem/rust-cache@v1 - name: Run clippy linter 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 # challenge, running cargo check on them should be easy enough and should # catch at least some problems (like different sizes of types). weird_platforms: name: Check weird platforms strategy: fail-fast: false matrix: target: - x86_64-unknown-linux-musl - x86_64-pc-solaris - x86_64-linux-android - aarch64-linux-android - arm-linux-androideabi - mips-unknown-linux-musl - x86_64-unknown-netbsd - x86_64-unknown-freebsd extra-args: - "--all --all-features" include: # - wasm32-wasi (not yet? Or not ever?) # - x86_64-unknown-redox (Is that platform even usable on stable?) - { target: x86_64-fuchsia, extra-args: "--all-features" } - { target: asmjs-unknown-emscripten, extra-args: "--all-features" } # Seems we have some trouble with properly running C compiler on these. Might as well be problem with cross-compilation setup, but who knows # So we avoid the exfiltrator part/signal-hook-sys :-( - { target: x86_64-apple-darwin, extra-args: "--features=iterator --all --exclude signal-hook-sys" } - { target: x86_64-apple-ios, extra-args: "--features=iterator --all --exclude signal-hook-sys" } runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: toolchain: stable profile: minimal default: true target: ${{ matrix.target }} - name: Restore cache uses: Swatinem/rust-cache@v1 - name: Run the check uses: actions-rs/cargo@v1 with: command: check args: ${{ matrix.extra-args }} --tests --target=${{ matrix.target }} use-cross: true signal-hook-0.3.17/.gitignore000064400000000000000000000000441046102023000141150ustar 00000000000000/target **/*.rs.bk tags .ccls-cache signal-hook-0.3.17/CHANGELOG.md000064400000000000000000000136501046102023000137450ustar 00000000000000# 0.3.17 * Fix race condition leading into a panic in SignalsInfo::forever (#148). # 0.3.16 * Fix compilation on OpenBSD (#147). # 0.3.15 # signal-hook-registry-1.4.1 * AIX support (experimental/not guaranteed to work). # 0.3.14 * Added the SIGINFO signal (where available). # signal-hook-mio-0.2.3 * Support for mio 0.8 # signal-hook-async-std-0.2.2 # signal-hook-tokio-0.3.1 * Fix support for SignalsInfo with non-default info extractors. # 0.3.13 * Add haiku support. # 0.3.12 * Fix accidentally broken windows build. # 0.3.11 * Provide fallback sigaddset, sigemptyset on certain androids, as they are missing them. # 0.3.10 * Doc link fixes. # 0.3.9 * Deliver SIGCHLD even on stop/continue. # 0.3.8 * Fix docs.rs build. # 0.3.7 * Unmask a signal in default emulation if it is termination. # mio-0.2.2 * The same fix, but for the 0.6 support 😇. # mio-0.2.1 * Fix example: handle ErrorKind::Interrupted inside poll. It's very likely to happen, when we are waiting for signals there. # 0.3.6 * Fix the labels on docs.rs :-|. # 0.3.5 * Doc: include the features & these little labels inside docs. # signal-hook-async-std-0.2.1 * Dependency updates ‒ no longer depends on the whole async-std, but only on some smaller dependencies of it (`async-io`, `futures-lite`). This might make it work even outside of async-std context. # signal-hook-tokio-0.3.0 * Support for tokio 1.0. # 0.3.4 * Fix feature dependencies (`iterator` depends on `channel`). # 0.3.3 * `low_level::emulate_default_handler` to emulate whatever default handler would do. * `low_level::signal_name` to look up human readable name. * The `Origin`'s debug output now contains the human readable name of the signal. # 0.3.2 * Allow extracting Origin from the raw `siginfo_t` structure by hand, without needing an iterator. * Folding the signal-hook-sys inline (but still compiling C code only conditionally). * `WithRawSiginfo` extractor (to get hands on the raw `siginfo_t`). * Bugfix: Don't leak on WithOrigin destruction. # 0.3.1 * Use caret dependencies where appropriate (to allow upgrades on signal-hook-registry). # async-std-0.2.0 * No longer depends on `futures`. # 0.3.0 * The `cleanup` module is gone, it was not a good API. Replaced by conditional termination in `flag`. * Some abstractions/patterns are moved to `low_level` submodule, as they are considered building blocks, not for direct use (`register`, `pipe`, `channel`). * The signal constants are moved to a submodule (`consts`), together with few more constants, to not clutter the root. * The forever iterator no longer consumes. # registry-1.3.0 * The `unregister_signal` in is deprecated, without a replacement. # 0.2.2 * Extractor for the origin of a signal (PID, UID, what caused it). * Fixing some doc links on re-exports. # 0.2.1 * Allow turning the iterator module off (the `iterator` feature, part of default features). This would allow compiling the crate on 1.31.0. # 0.2.0 * Bump minimal rustc version to 1.36.0 (signal-hook-registry still builds with 1.26.0). * (Breaking) Support for exfiltrators ‒ ability to return more than just the signal number from the iterator and streams. Nothing more is implemented yet, but the place to put it is reserved in the API. * (Breaking) `pipe::register_raw` now takes ownership and tries to use send first, falls back to `O_NONBLOCK` and `write` on failure. * (Breaking) All async support is pulled out into separate crates, to decouple from the async runtime release cycles on the main `signal-hook` crate. * Inner parts of the `Iterator` are now exposed in `signal_hook::iterator::backend`, to support the async crates. # registry-1.2.2 * Drop dependency on arc-swap (only very small subset used and arc-swap would like to drop that part anyway). # registry-1.2.1 * Abort instead of panicking if the OS gives us NULL as siginfo (which is illegal). Panicking would be UB. # 0.1.16 * Fix possible blocking in signal handler registered by `Signals`. # 0.1.15 * Make `Signals` work in edge-triggered mode in mio too, by always draining everything from the socket. Needed, because mio 0.7 doesn't have level-triggered any more. # 0.1.14 * `mio-0_7-support` feature for use with mio 0.7.0+. * Bump minimal rustc version to 1.31.0 (signal-hook-registry can still build with 1.26.0). # 0.1.13 * Some doc clarifications. # 0.1.12 * `cleanup` module to register resetting signals to default. # registry-1.2.0 * `unregister_signal`, to remove all hooks of one signal. # 0.1.11 * Docs improvements. * Fix registering pipes as well as sockets into the pipe module (#27). # registry-1.1.1 * Update deps. # registry-1.1.0 * Adding Windows support (thanks to @qnighy). # 0.1.10 * Fix busy loop in Iterator::forever when the mio-support feature is enabled (#16). # registry-1.0.1 * Include the registry files in the crates.io tarball. # 0.1.9 # registry-1.0.0 * Split into backend signal-hook-registry and the frontend. The backend is much less likely to have breaking changes so it contains the things that can be in the application just once. # 0.1.8 * The `Signals` iterator can now be closed (from another instance or thread), which can be used to shut down the thread handling signals from the main thread. # 0.1.7 * The `Signals` iterator allows adding signals after creation. * Fixed a bug where `Signals` registrations could be unregirestered too soon if the `Signals` was cloned previously. # 0.1.6 * The internally used ArcSwap thing doesn't block other ArcSwaps now (has independent generation lock). # 0.1.5 * Re-exported signal constants, so users no longer need libc. # 0.1.4 * Compilation fix for android-aarch64 # 0.1.3 * Tokio support. * Mio support. * Dependency updates. # 0.1.2 * Dependency updates. # 0.1.1 * Get rid of `catch_unwind` inside the signal handler. * Link to the nix crate. # 0.1.0 * Initial basic implementation. * Flag helpers. * Pipe helpers. * High-level iterator helper. signal-hook-0.3.17/Cargo.lock0000644000000155210000000000100113160ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "cc" version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "lock_api" version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "parking_lot" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", "windows-sys", ] [[package]] name = "proc-macro-error" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", "syn", "version_check", ] [[package]] name = "proc-macro-error-attr" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ "proc-macro2", "quote", "version_check", ] [[package]] name = "proc-macro2" version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" dependencies = [ "bitflags", ] [[package]] name = "rustversion" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serial_test" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d19dbfb999a147cedbfe82f042eb9555f5b0fa4ef95ee4570b74349103d9c9f4" dependencies = [ "lazy_static", "log", "parking_lot", "serial_test_derive", ] [[package]] name = "serial_test_derive" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb9e2050b2be1d681f8f1c1a528bcfe4e00afa2d8995f713974f5333288659f2" dependencies = [ "proc-macro-error", "proc-macro2", "quote", "rustversion", "syn", ] [[package]] name = "signal-hook" version = "0.3.17" dependencies = [ "cc", "libc", "serial_test", "signal-hook-registry", ] [[package]] name = "signal-hook-registry" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" dependencies = [ "libc", ] [[package]] name = "smallvec" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" [[package]] name = "syn" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" [[package]] name = "version_check" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "windows-sys" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_i686_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_x86_64_gnu" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_msvc" version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" signal-hook-0.3.17/Cargo.toml0000644000000026230000000000100113400ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "signal-hook" version = "0.3.17" authors = [ "Michal 'vorner' Vaner ", "Thomas Himmelstoss ", ] description = "Unix signal handling" documentation = "https://docs.rs/signal-hook" readme = "README.md" keywords = [ "signal", "unix", "daemon", ] license = "Apache-2.0/MIT" repository = "https://github.com/vorner/signal-hook" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.libc] version = "^0.2" [dependencies.signal-hook-registry] version = "^1.4" [dev-dependencies.serial_test] version = "^0.7" [build-dependencies.cc] version = "^1" optional = true [features] channel = [] default = [ "channel", "iterator", ] extended-siginfo = [ "channel", "iterator", "extended-siginfo-raw", ] extended-siginfo-raw = ["cc"] iterator = ["channel"] [badges.maintenance] status = "actively-developed" signal-hook-0.3.17/Cargo.toml.orig000064400000000000000000000021351046102023000150170ustar 00000000000000[package] name = "signal-hook" version = "0.3.17" authors = [ "Michal 'vorner' Vaner ", "Thomas Himmelstoss ", ] description = "Unix signal handling" documentation = "https://docs.rs/signal-hook" repository = "https://github.com/vorner/signal-hook" readme = "README.md" keywords = ["signal", "unix", "daemon"] license = "Apache-2.0/MIT" edition = "2018" [badges] maintenance = { status = "actively-developed" } [features] channel = [] default = ["channel", "iterator"] iterator = ["channel"] # TODO: Unify them on the next breaking release. extended-siginfo = ["channel", "iterator", "extended-siginfo-raw"] extended-siginfo-raw = ["cc"] [workspace] members = [ "./", "signal-hook-registry", "signal-hook-tokio", "signal-hook-mio", "signal-hook-async-std", ] [dependencies] libc = "^0.2" signal-hook-registry = { version = "^1.4", path = "signal-hook-registry" } [dev-dependencies] serial_test = "^0.7" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [build-dependencies] cc = { version = "^1", optional = true } signal-hook-0.3.17/LICENSE-APACHE000064400000000000000000000251371046102023000140630ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. signal-hook-0.3.17/LICENSE-MIT000064400000000000000000000020541046102023000135640ustar 00000000000000Copyright (c) 2017 tokio-jsonrpc developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. signal-hook-0.3.17/README.md000064400000000000000000000056331046102023000134150ustar 00000000000000# Signal-hook [![Actions Status](https://github.com/vorner/signal-hook/workflows/test/badge.svg)](https://github.com/vorner/signal-hook/actions) [![codecov](https://codecov.io/gh/vorner/signal-hook/branch/master/graph/badge.svg?token=3KA3R2D9fV)](https://codecov.io/gh/vorner/signal-hook) [![docs](https://docs.rs/signal-hook/badge.svg)](https://docs.rs/signal-hook) Library for safe and correct Unix signal handling in Rust. Unix signals are inherently hard to handle correctly, for several reasons: * They are a global resource. If a library wants to set its own signal handlers, it risks disturbing some other library. It is possible to chain the previous signal handler, but then it is impossible to remove the old signal handlers from the chains in any practical manner. * They can be called from whatever thread, requiring synchronization. Also, as they can interrupt a thread at any time, making most handling race-prone. * According to the POSIX standard, the set of functions one may call inside a signal handler is limited to very few of them. To highlight, mutexes (or other locking mechanisms) and memory allocation and deallocation are *not* allowed. This library aims to solve some of the problems. It provides a global registry of actions performed on arrival of signals. It is possible to register multiple actions for the same signal and it is possible to remove the actions later on. If there was a previous signal handler when the first action for a signal is registered, it is chained (but the original one can't be removed). Besides the basic registration of an arbitrary action, several helper actions are provided to cover the needs of the most common use cases, available from safe Rust. For further details, see the [documentation](https://docs.rs/signal-hook). ## Example (This likely does a lot more than you need in each individual application, it's more of a show-case of what everything is possible, not of what you need to do each time). ```rust use std::io::Error; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; fn main() -> Result<(), Error> { let term = Arc::new(AtomicBool::new(false)); signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term))?; while !term.load(Ordering::Relaxed) { // Do some time-limited stuff here // (if this could block forever, then there's no guarantee the signal will have any // effect). } Ok(()) } ``` ## License Licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. signal-hook-0.3.17/build.rs000064400000000000000000000003161046102023000135740ustar 00000000000000#[cfg(feature = "extended-siginfo-raw")] fn main() { cc::Build::new() .file("src/low_level/extract.c") .compile("extract"); } #[cfg(not(feature = "extended-siginfo-raw"))] fn main() {} signal-hook-0.3.17/ci-check.sh000075500000000000000000000017001046102023000141320ustar 00000000000000#!/bin/sh # We try to support some older versions of rustc. However, the support is # tiered a bit. Our dev-dependencies do *not* guarantee that old minimal # version. So we don't do tests on the older ones. Also, the # signal-hook-registry supports older rustc than we signal-hook. set -ex rm -f Cargo.lock cargo build --all --exclude signal-hook-async-std --exclude signal-hook-tokio if [ "$RUST_VERSION" = 1.36.0 ] ; then exit fi 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="" fi export RUSTFLAGS="-D warnings" cargo test --all --all-features $EXCLUDE_FROM_BUILD cargo test --all $EXCLUDE_FROM_BUILD signal-hook-0.3.17/examples/print.rs000064400000000000000000000015231046102023000154500ustar 00000000000000use libc::c_int; use signal_hook::consts::signal::*; use signal_hook::low_level; use std::io::Error; #[cfg(feature = "extended-siginfo")] type Signals = signal_hook::iterator::SignalsInfo; #[cfg(not(feature = "extended-siginfo"))] use signal_hook::iterator::Signals; fn main() -> Result<(), Error> { const SIGNALS: &[c_int] = &[ SIGTERM, SIGQUIT, SIGINT, SIGTSTP, SIGWINCH, SIGHUP, SIGCHLD, SIGCONT, ]; let mut sigs = Signals::new(SIGNALS)?; for signal in &mut sigs { eprintln!("Received signal {:?}", signal); #[cfg(feature = "extended-siginfo")] let signal = signal.signal; // After printing it, do whatever the signal was supposed to do in the first place low_level::emulate_default_handler(signal)?; } Ok(()) } signal-hook-0.3.17/rustfmt.toml000064400000000000000000000000001046102023000145160ustar 00000000000000signal-hook-0.3.17/src/flag.rs000064400000000000000000000252011046102023000141750ustar 00000000000000//! Module for actions setting flags. //! //! This contains helper functions to set flags whenever a signal happens. The flags are atomic //! bools or numbers and the library manipulates them with the `SeqCst` ordering, in case someone //! cares about relative order to some *other* atomic variables. If you don't care about the //! relative order, you are free to use `Ordering::Relaxed` when reading and resetting the flags. //! //! # When to use //! //! The flags in this module allow for polling if a signal arrived since the previous poll. The do //! not allow blocking until something arrives. //! //! Therefore, the natural way to use them is in applications that have some kind of iterative work //! with both some upper and lower time limit on one iteration. If one iteration could block for //! arbitrary time, the handling of the signal would be postponed for a long time. If the iteration //! didn't block at all, the checking for the signal would turn into a busy-loop. //! //! If what you need is blocking until a signal comes, you might find better tools in the //! [`pipe`][crate::low_level::pipe] and [`iterator`][crate::iterator] modules. //! //! # Examples //! //! Doing something until terminated. This also knows by which signal it was terminated. In case //! multiple termination signals arrive before it is handled, it recognizes the last one. //! //! ```rust //! use std::io::Error; //! use std::sync::Arc; //! use std::sync::atomic::{AtomicUsize, Ordering}; //! //! use signal_hook::consts::signal::*; //! use signal_hook::flag as signal_flag; //! //! fn main() -> Result<(), Error> { //! let term = Arc::new(AtomicUsize::new(0)); //! const SIGTERM_U: usize = SIGTERM as usize; //! const SIGINT_U: usize = SIGINT as usize; //! # #[cfg(not(windows))] //! const SIGQUIT_U: usize = SIGQUIT as usize; //! signal_flag::register_usize(SIGTERM, Arc::clone(&term), SIGTERM_U)?; //! signal_flag::register_usize(SIGINT, Arc::clone(&term), SIGINT_U)?; //! # #[cfg(not(windows))] //! signal_flag::register_usize(SIGQUIT, Arc::clone(&term), SIGQUIT_U)?; //! //! # // Hack to terminate the example when run as a doc-test. //! # term.store(SIGTERM_U, Ordering::Relaxed); //! loop { //! match term.load(Ordering::Relaxed) { //! 0 => { //! // Do some useful stuff here //! } //! SIGTERM_U => { //! eprintln!("Terminating on the TERM signal"); //! break; //! } //! SIGINT_U => { //! eprintln!("Terminating on the INT signal"); //! break; //! } //! # #[cfg(not(windows))] //! SIGQUIT_U => { //! eprintln!("Terminating on the QUIT signal"); //! break; //! } //! _ => unreachable!(), //! } //! } //! //! Ok(()) //! } //! ``` //! //! Sending a signal to self and seeing it arrived (not of a practical usage on itself): //! //! ```rust //! use std::io::Error; //! use std::sync::Arc; //! use std::sync::atomic::{AtomicBool, Ordering}; //! use std::thread; //! use std::time::Duration; //! //! use signal_hook::consts::signal::*; //! use signal_hook::low_level::raise; //! //! fn main() -> Result<(), Error> { //! let got = Arc::new(AtomicBool::new(false)); //! # #[cfg(not(windows))] //! signal_hook::flag::register(SIGUSR1, Arc::clone(&got))?; //! # #[cfg(windows)] //! # signal_hook::flag::register(SIGTERM, Arc::clone(&got))?; //! # #[cfg(not(windows))] //! raise(SIGUSR1).unwrap(); //! # #[cfg(windows)] //! # raise(SIGTERM).unwrap(); //! // A sleep here, because it could run the signal handler in another thread and we may not //! // see the flag right away. This is still a hack and not guaranteed to work, it is just an //! // example! //! thread::sleep(Duration::from_secs(1)); //! assert!(got.load(Ordering::Relaxed)); //! Ok(()) //! } //! ``` //! //! Reloading a configuration on `SIGHUP` (which is a common behaviour of many UNIX daemons, //! together with reopening the log file). //! //! ```rust //! use std::io::Error; //! use std::sync::Arc; //! use std::sync::atomic::{AtomicBool, Ordering}; //! //! use signal_hook::consts::signal::*; //! use signal_hook::flag as signal_flag; //! //! fn main() -> Result<(), Error> { //! // We start with true, to load the configuration in the very first iteration too. //! let reload = Arc::new(AtomicBool::new(true)); //! let term = Arc::new(AtomicBool::new(false)); //! # #[cfg(not(windows))] //! signal_flag::register(SIGHUP, Arc::clone(&reload))?; //! signal_flag::register(SIGINT, Arc::clone(&term))?; //! signal_flag::register(SIGTERM, Arc::clone(&term))?; //! # #[cfg(not(windows))] //! signal_flag::register(SIGQUIT, Arc::clone(&term))?; //! while !term.load(Ordering::Relaxed) { //! // Using swap here, not load, to reset it back to false once it is reloaded. //! if reload.swap(false, Ordering::Relaxed) { //! // Reload the config here //! # //! # // Hiden hack to make the example terminate when run as doc-test. Not part of the //! # // real code. //! # term.store(true, Ordering::Relaxed); //! } //! // Serve one request //! } //! Ok(()) //! } //! ``` use std::io::Error; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; use libc::{c_int, EINVAL}; use crate::{low_level, SigId}; /// Registers an action to set the flag to `true` whenever the given signal arrives. /// /// # Panics /// /// If the signal is one of the forbidden. pub fn register(signal: c_int, flag: Arc) -> Result { // We use SeqCst for two reasons: // * Signals should not come very often, so the performance does not really matter. // * We promise the order of actions, but setting different atomics with Relaxed or similar // would not guarantee the effective order. unsafe { low_level::register(signal, move || flag.store(true, Ordering::SeqCst)) } } /// Registers an action to set the flag to the given value whenever the signal arrives. pub fn register_usize(signal: c_int, flag: Arc, value: usize) -> Result { unsafe { low_level::register(signal, move || flag.store(value, Ordering::SeqCst)) } } /// Terminate the application on a signal if the given condition is true. /// /// This can be used for different use cases. One of them (with the condition being always true) is /// just unconditionally terminate on the given signal. /// /// Another is being able to turn on and off the behaviour by the shared flag. /// /// The last one is handling double CTRL+C ‒ if the user presses CTRL+C, we would like to start a /// graceful shutdown. But if anything ever gets stuck in the shutdown, second CTRL+C (or other /// such termination signal) should terminate the application without further delay. /// /// To do that, one can combine this with [`register`]. On the first run, the flag is `false` and /// this doesn't terminate. But then the flag is set to true during the first run and „arms“ the /// shutdown on the second run. Note that it matters in which order the actions are registered (the /// shutdown must go first). And yes, this also allows asking the user „Do you want to terminate“ /// and disarming the abrupt shutdown if the user answers „No“. /// /// # Panics /// /// If the signal is one of the forbidden. pub fn register_conditional_shutdown( signal: c_int, status: c_int, condition: Arc, ) -> Result { let action = move || { if condition.load(Ordering::SeqCst) { low_level::exit(status); } }; unsafe { low_level::register(signal, action) } } /// Conditionally runs an emulation of the default action on the given signal. /// /// If the provided condition is true at the time of invoking the signal handler, the equivalent of /// the default action of the given signal is run. It is a bit similar to /// [`register_conditional_shutdown`], except that it doesn't terminate for non-termination /// signals, it runs their default handler. /// /// # Panics /// /// If the signal is one of the forbidden /// /// # Errors /// /// Similarly to the [`emulate_default_handler`][low_level::emulate_default_handler] function, this /// one looks the signal up in a table. If it is unknown, an error is returned. /// /// Additionally to that, any errors that can be caused by a registration of a handler can happen /// too. pub fn register_conditional_default( signal: c_int, condition: Arc, ) -> Result { // Verify we know about this particular signal. low_level::signal_name(signal).ok_or_else(|| Error::from_raw_os_error(EINVAL))?; let action = move || { if condition.load(Ordering::SeqCst) { let _ = low_level::emulate_default_handler(signal); } }; unsafe { low_level::register(signal, action) } } #[cfg(test)] mod tests { use std::sync::atomic; use std::time::{Duration, Instant}; use super::*; use crate::consts::signal::*; fn self_signal() { #[cfg(not(windows))] const SIG: c_int = SIGUSR1; #[cfg(windows)] const SIG: c_int = SIGTERM; crate::low_level::raise(SIG).unwrap(); } fn wait_flag(flag: &AtomicBool) -> bool { let start = Instant::now(); while !flag.load(Ordering::Relaxed) { // Replaced by hint::spin_loop, but we want to support older compiler #[allow(deprecated)] atomic::spin_loop_hint(); if Instant::now() - start > Duration::from_secs(1) { // We reached a timeout and nothing happened yet. // In theory, using timeouts for thread-synchronization tests is wrong, but a // second should be enough in practice. return false; } } true } #[test] fn register_unregister() { // When we register the action, it is active. let flag = Arc::new(AtomicBool::new(false)); #[cfg(not(windows))] let signal = register(SIGUSR1, Arc::clone(&flag)).unwrap(); #[cfg(windows)] let signal = register(crate::SIGTERM, Arc::clone(&flag)).unwrap(); self_signal(); assert!(wait_flag(&flag)); // But stops working after it is unregistered. assert!(crate::low_level::unregister(signal)); flag.store(false, Ordering::Relaxed); self_signal(); assert!(!wait_flag(&flag)); // And the unregistration actually dropped its copy of the Arc assert_eq!(1, Arc::strong_count(&flag)); } // The shutdown is tested in tests/shutdown.rs } signal-hook-0.3.17/src/iterator/backend.rs000064400000000000000000000417771046102023000165240ustar 00000000000000//! A backend module for implementing the iterator like //! [`iterator`][crate::iterator] module and the asynchronous //! adapter crates. //! //! This module contains generic types which abstract over the concrete //! IO type for the self-pipe. The motivation for having this abstraction //! are the adapter crates for different asynchronous runtimes. The runtimes //! provide their own wrappers for [`std::os::unix::net::UnixStream`] //! which should be used as the internal self pipe. But large parts of the //! remaining functionality doesn't depend directly onto the IO type and can //! be reused. //! //! See also the [`SignalDelivery::with_pipe`] method for more information //! about requirements the IO types have to fulfill. //! //! As a regular user you shouldn't need to use the types in this module. //! Use the [`Signals`][crate::iterator::Signals] struct or one of the types //! contained in the adapter libraries instead. use std::borrow::{Borrow, BorrowMut}; use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::io::Error; use std::mem::MaybeUninit; use std::os::unix::io::AsRawFd; use std::ptr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use libc::{self, c_int}; use super::exfiltrator::Exfiltrator; use crate::low_level::pipe::{self, WakeMethod}; use crate::SigId; /// Maximal signal number we support. const MAX_SIGNUM: usize = 128; trait SelfPipeWrite: Debug + Send + Sync { fn wake_readers(&self); } impl SelfPipeWrite for W { fn wake_readers(&self) { pipe::wake(self.as_raw_fd(), WakeMethod::Send); } } #[derive(Debug)] struct DeliveryState { closed: AtomicBool, registered_signal_ids: Mutex>>, } impl DeliveryState { fn new() -> Self { let ids = (0..MAX_SIGNUM).map(|_| None).collect(); Self { closed: AtomicBool::new(false), registered_signal_ids: Mutex::new(ids), } } } impl Drop for DeliveryState { fn drop(&mut self) { let lock = self.registered_signal_ids.lock().unwrap(); for id in lock.iter().filter_map(|s| *s) { crate::low_level::unregister(id); } } } struct PendingSignals { exfiltrator: E, slots: [E::Storage; MAX_SIGNUM], } impl PendingSignals { fn new(exfiltrator: E) -> Self { // Unfortunately, Default is not implemented for long arrays :-( // // Note that if the default impl panics, the already existing instances are leaked. let mut slots = MaybeUninit::<[E::Storage; MAX_SIGNUM]>::uninit(); for i in 0..MAX_SIGNUM { unsafe { let slot: *mut E::Storage = slots.as_mut_ptr() as *mut _; let slot = slot.add(i); ptr::write(slot, E::Storage::default()); } } Self { exfiltrator, slots: unsafe { slots.assume_init() }, } } } /// An internal trait to hide adding new signals into a Handle behind a dynamic dispatch. trait AddSignal: Debug + Send + Sync { fn add_signal( self: Arc, write: Arc, signal: c_int, ) -> Result; } // Implemented manually because 1.36.0 doesn't yet support Debug for [X; BIG_NUMBER]. impl Debug for PendingSignals { fn fmt(&self, fmt: &mut Formatter) -> FmtResult { fmt.debug_struct("PendingSignals") .field("exfiltrator", &self.exfiltrator) // While the array does not, the slice does implement Debug .field("slots", &&self.slots[..]) .finish() } } impl AddSignal for PendingSignals { fn add_signal( self: Arc, write: Arc, signal: c_int, ) -> Result { assert!(signal >= 0); assert!( (signal as usize) < MAX_SIGNUM, "Signal number {} too large. If your OS really supports such signal, file a bug", signal, ); assert!( self.exfiltrator.supports_signal(signal), "Signal {} not supported by exfiltrator {:?}", signal, self.exfiltrator, ); self.exfiltrator.init(&self.slots[signal as usize], signal); let action = move |act: &_| { let slot = &self.slots[signal as usize]; let ex = &self.exfiltrator; ex.store(slot, signal, act); write.wake_readers(); }; let id = unsafe { signal_hook_registry::register_sigaction(signal, action) }?; Ok(id) } } /// A struct to control an instance of an associated type /// (like for example [`Signals`][super::Signals]). /// /// It allows to register more signal handlers and to shutdown the signal /// delivery. You can [`clone`][Handle::clone] this type which isn't a /// very expensive operation. The cloned instances can be shared between /// multiple threads. #[derive(Debug, Clone)] pub struct Handle { pending: Arc, write: Arc, delivery_state: Arc, } impl Handle { fn new(write: W, pending: Arc) -> Self where W: 'static + SelfPipeWrite, { Self { pending, write: Arc::new(write), delivery_state: Arc::new(DeliveryState::new()), } } /// Registers another signal to the set watched by the associated instance. /// /// # Notes /// /// * This is safe to call concurrently from whatever thread. /// * This is *not* safe to call from within a signal handler. /// * If the signal number was already registered previously, this is a no-op. /// * If this errors, the original set of signals is left intact. /// /// # Panics /// /// * If the given signal is [forbidden][crate::FORBIDDEN]. /// * If the signal number is negative or larger than internal limit. The limit should be /// larger than any supported signal the OS supports. /// * If the relevant [`Exfiltrator`] does not support this particular signal. The default /// [`SignalOnly`] one supports all signals. pub fn add_signal(&self, signal: c_int) -> Result<(), Error> { let mut lock = self.delivery_state.registered_signal_ids.lock().unwrap(); // Already registered, ignoring if lock[signal as usize].is_some() { return Ok(()); } let id = Arc::clone(&self.pending).add_signal(Arc::clone(&self.write), signal)?; lock[signal as usize] = Some(id); Ok(()) } /// Closes the associated instance. /// /// This is meant to signalize termination of the signal delivery process. /// After calling close: /// /// * [`is_closed`][Handle::is_closed] will return true. /// * All currently blocking operations of associated instances /// are interrupted and terminate. /// * Any further operations will not block. /// * Further signals may or may not be returned from the iterators. However, if any are /// returned, these are real signals that happened. /// /// The goal is to be able to shut down any background thread that handles only the signals. pub fn close(&self) { self.delivery_state.closed.store(true, Ordering::SeqCst); self.write.wake_readers(); } /// Is it closed? /// /// See [`close`][Handle::close]. pub fn is_closed(&self) -> bool { self.delivery_state.closed.load(Ordering::SeqCst) } } /// A struct for delivering received signals to the main program flow. /// The self-pipe IO type is generic. See the /// [`with_pipe`][SignalDelivery::with_pipe] method for requirements /// for the IO type. #[derive(Debug)] pub struct SignalDelivery { read: R, handle: Handle, pending: Arc>, } impl SignalDelivery where R: 'static + AsRawFd + Send + Sync, { /// Creates the `SignalDelivery` structure. /// /// The read and write arguments must be the ends of a suitable pipe type. These are used /// for communication between the signal handler and main program flow. /// /// Registers all the signals listed. The same restrictions (panics, errors) apply as with /// [`add_signal`][Handle::add_signal]. /// /// # Requirements for the pipe type /// /// * Must support [`send`](https://man7.org/linux/man-pages/man2/send.2.html) for /// asynchronously writing bytes to the write end /// * Must support [`recv`](https://man7.org/linux/man-pages/man2/recv.2.html) for /// reading bytes from the read end /// /// So UnixStream is a good choice for this. pub fn with_pipe(read: R, write: W, exfiltrator: E, signals: I) -> Result where I: IntoIterator, S: Borrow, W: 'static + AsRawFd + Debug + Send + Sync, { let pending = Arc::new(PendingSignals::new(exfiltrator)); let pending_add_signal = Arc::clone(&pending); let handle = Handle::new(write, pending_add_signal); let me = Self { read, handle, pending, }; for sig in signals { me.handle.add_signal(*sig.borrow())?; } Ok(me) } /// Get a reference to the read end of the self pipe /// /// You may use this method to register the underlying file descriptor /// with an eventing system (e. g. epoll) to get notified if there are /// bytes in the pipe. If the event system reports the file descriptor /// ready for reading you can then call [`pending`][SignalDelivery::pending] /// to get the arrived signals. pub fn get_read(&self) -> &R { &self.read } /// Get a mutable reference to the read end of the self pipe /// /// See the [`get_read`][SignalDelivery::get_read] method for some additional /// information. pub fn get_read_mut(&mut self) -> &mut R { &mut self.read } /// Drains all data from the internal self-pipe. This method will never block. fn flush(&mut self) { const SIZE: usize = 1024; let mut buff = [0u8; SIZE]; unsafe { // Draining the data in the self pipe. We ignore all errors on purpose. This // should not be something like closed file descriptor. It could EAGAIN, but // that's OK in case we say MSG_DONTWAIT. If it's EINTR, then it's OK too, // it'll only create a spurious wakeup. #[cfg(target_os = "aix")] let nowait_flag = libc::MSG_NONBLOCK; #[cfg(not(target_os = "aix"))] let nowait_flag = libc::MSG_DONTWAIT; while libc::recv( self.read.as_raw_fd(), buff.as_mut_ptr() as *mut libc::c_void, SIZE, nowait_flag, ) > 0 {} } } /// Returns an iterator of already received signals. /// /// This returns an iterator over all the signal numbers of the signals received since last /// time they were read (out of the set registered by this `SignalDelivery` instance). Note /// that they are returned in arbitrary order and a signal number is returned only once even /// if it was received multiple times. /// /// This method returns immediately (does not block) and may produce an empty iterator if /// there are no signals ready. pub fn pending(&mut self) -> Pending { self.flush(); Pending::new(Arc::clone(&self.pending)) } /// Checks the reading end of the self pipe for available signals. /// /// If there are no signals available or this instance was already closed it returns /// [`Option::None`]. If there are some signals it returns a [`Pending`] /// instance wrapped inside a [`Option::Some`]. However, due to implementation details, /// this still can produce an empty iterator. /// /// This method doesn't check the reading end by itself but uses the passed in callback. /// This method blocks if and only if the callback blocks trying to read some bytes. pub fn poll_pending(&mut self, has_signals: &mut F) -> Result>, Error> where F: FnMut(&mut R) -> Result, { if self.handle.is_closed() { return Ok(None); } match has_signals(self.get_read_mut()) { Ok(false) => Ok(None), Ok(true) => Ok(Some(self.pending())), Err(err) => Err(err), } } /// Get a [`Handle`] for this `SignalDelivery` instance. /// /// This can be used to add further signals or close the whole /// signal delivery mechanism. pub fn handle(&self) -> Handle { self.handle.clone() } } /// The iterator of one batch of signals. /// /// This is returned by the [`pending`][SignalDelivery::pending] method. #[derive(Debug)] pub struct Pending { pending: Arc>, position: usize, } impl Pending { fn new(pending: Arc>) -> Self { Self { pending, position: 0, } } } impl Iterator for Pending { type Item = E::Output; fn next(&mut self) -> Option { while self.position < self.pending.slots.len() { let sig = self.position; let slot = &self.pending.slots[sig]; let result = self.pending.exfiltrator.load(slot, sig as c_int); if result.is_some() { return result; } else { self.position += 1; } } None } } /// Possible results of the [`poll_signal`][SignalIterator::poll_signal] function. pub enum PollResult { /// A signal arrived Signal(O), /// There are no signals yet but there may arrive some in the future Pending, /// The iterator was closed. There won't be any signals reported from now on. Closed, /// An error happened during polling for arrived signals. Err(Error), } /// An infinite iterator of received signals. pub struct SignalIterator { signals: SD, iter: Pending, } impl SignalIterator { /// Create a new infinite iterator for signals registered with the passed /// in [`SignalDelivery`] instance. pub fn new(mut signals: SD) -> Self where SD: BorrowMut>, R: 'static + AsRawFd + Send + Sync, { let iter = signals.borrow_mut().pending(); Self { signals, iter } } /// Return a signal if there is one or tell the caller that there is none at the moment. /// /// You have to pass in a callback which checks the underlying reading end of the pipe if /// there may be any pending signals. This callback may or may not block. If the callback /// returns [`true`] this method will try to fetch the next signal and return it as a /// [`PollResult::Signal`]. If the callback returns [`false`] the method will return /// [`PollResult::Pending`] and assume it will be called again at a later point in time. /// The callback may be called any number of times by this function. /// /// If the iterator was closed by the [`close`][Handle::close] method of the associated /// [`Handle`] this method will return [`PollResult::Closed`]. pub fn poll_signal(&mut self, has_signals: &mut F) -> PollResult where SD: BorrowMut>, R: 'static + AsRawFd + Send + Sync, F: FnMut(&mut R) -> Result, { // The loop is necessary because it is possible that a signal was already consumed // by a previous pending iterator due to the asynchronous nature of signals and // always moving to the end of the iterator before calling has_more. while !self.signals.borrow_mut().handle.is_closed() { if let Some(result) = self.iter.next() { return PollResult::Signal(result); } match self.signals.borrow_mut().poll_pending(has_signals) { Ok(Some(pending)) => self.iter = pending, Ok(None) => return PollResult::Pending, Err(err) => return PollResult::Err(err), } } PollResult::Closed } /// Get a shareable [`Handle`] for this instance. /// /// This can be used to add further signals or terminate the whole /// signal iteration using the [`close`][Handle::close] method. pub fn handle(&self) -> Handle where SD: Borrow>, R: 'static + AsRawFd + Send + Sync, { self.signals.borrow().handle() } } /// A signal iterator which consumes a [`SignalDelivery`] instance and takes /// ownership of it. pub type OwningSignalIterator = SignalIterator, E>; /// A signal iterator which takes a mutable reference to a [`SignalDelivery`] /// instance. pub type RefSignalIterator<'a, R, E> = SignalIterator<&'a mut SignalDelivery, E>; signal-hook-0.3.17/src/iterator/exfiltrator/mod.rs000064400000000000000000000147131046102023000202450ustar 00000000000000//! An abstraction over exfiltrating information out of signal handlers. //! //! The [`Exfiltrator`] trait provides a way to abstract the information extracted from a signal //! handler and the way it is extracted out of it. //! //! The implementations can be used to parametrize the //! [`SignalsInfo`][crate::iterator::SignalsInfo] to specify what results are returned. //! //! # Sealed //! //! Currently, the trait is sealed and all methods hidden. This is likely temporary, until some //! experience with them is gained. #[cfg(feature = "extended-siginfo")] #[cfg_attr(docsrs, doc(cfg(feature = "extended-siginfo")))] pub mod origin; pub mod raw; #[cfg(feature = "extended-siginfo")] pub use origin::WithOrigin; pub use raw::WithRawSiginfo; use std::sync::atomic::{AtomicBool, Ordering}; use libc::{c_int, siginfo_t}; mod sealed { use std::fmt::Debug; use libc::{c_int, siginfo_t}; /// The actual implementation of the [`Exfiltrator`][super::Exfiltrator]. /// /// For now, this is hidden from the public API, but the intention is to move it to a public /// place so users can implement it eventually, once we verify that it works well. /// /// # Safety /// /// The trait is unsafe as the [`Exfiltrator::store`] is called inside the signal handler and /// must be async-signal-safe. Implementing this correctly may be difficult, therefore care /// needs to be taken. One method known to work is encoding the data into an atomic variable. /// Other, less limiting approaches, will be eventually explored. pub unsafe trait Exfiltrator: Debug + Send + Sync + 'static { /// One slot for storing the data. /// /// Each signal will get its one slot of this type, independent of other signals. It can /// store the information in there inside the signal handler and will be loaded from it in /// load. /// /// Each slot is initialized to the [`Default`] value. It is expected this value represents /// „no signal delivered“ state. type Storage: Debug + Default + Send + Sync + 'static; /// The type returned to the user. type Output; /// If the given signal is supported by this specific exfiltrator. /// /// Not all information is available to all signals, therefore not all exfiltrators must /// support all signals. If `false` is returned, the user is prevented for registering such /// signal number with the given exfiltrator. fn supports_signal(&self, sig: c_int) -> bool; /// Puts the signal information inside the slot. /// /// It needs to somehow store the relevant information and the fact that a signal happened. /// /// # Warning /// /// This will be called inside the signal handler. It needs to be async-signal-safe. In /// particular, very small amount of operations are allowed in there. This namely does /// *not* include any locking nor allocation. /// /// It is also possible that multiple store methods are called concurrently; it is up to /// the implementor to deal with that. fn store(&self, slot: &Self::Storage, signal: c_int, info: &siginfo_t); /// Loads the signal information from the given slot. /// /// The method shall check if the signal happened (it may be possible to be called without /// the signal previously being delivered; it is up to the implementer to recognize it). It /// is assumed the [`Default`] value is recognized as no signal delivered. /// /// If it was delivered, the method shall extract the relevant information *and reset the /// slot* to the no signal delivered state. /// /// It shall return `Some(value)` if the signal was successfully received and `None` in /// case no signal was delivered. /// /// No blocking shall happen inside this method. It may be called concurrently with /// [`store`][Exfiltrator::store] (due to how signals work, concurrently even inside the /// same thread ‒ a `store` may „interrupt“ a call to `load`). It is up to the implementer /// to deal with that. fn load(&self, slot: &Self::Storage, signal: c_int) -> Option; /// Initialize the given slot for the given signal before the first use. /// /// This is called before the first use of the given slot (and it is annotated with the /// corresponding signal). The default does nothing, this is just an opportunity to /// allocate data lazily (this is called outside of the signal handler, so it doesn't have /// to be async-signal-safe). It will be called at most once for each slot. /// /// Note that you can rely on this being called for correctness, but not for safety (this /// crate calls it before the first use, but a user abusing the trait might not and in such /// case it is OK to eg. lose signals, but not segfault). fn init(&self, slot: &Self::Storage, signal: c_int) { // Suppress unused variable warning without putting the underscores into public // signature. let _ = slot; let _ = signal; } } } /// A trait describing what and how is extracted from signal handlers. /// /// By choosing a specific implementor as the type parameter for /// [`SignalsInfo`][crate::iterator::SignalsInfo], one can pick how much and what information is /// returned from the iterator. pub trait Exfiltrator: sealed::Exfiltrator {} impl Exfiltrator for E {} /// An [`Exfiltrator`] providing just the signal numbers. /// /// This is the basic exfiltrator for most needs. For that reason, there's the /// [`crate::iterator::Signals`] type alias, to simplify the type names for usual needs. #[derive(Clone, Copy, Debug, Default)] pub struct SignalOnly; unsafe impl sealed::Exfiltrator for SignalOnly { type Storage = AtomicBool; fn supports_signal(&self, _: c_int) -> bool { true } type Output = c_int; fn store(&self, slot: &Self::Storage, _: c_int, _: &siginfo_t) { slot.store(true, Ordering::SeqCst); } fn load(&self, slot: &Self::Storage, signal: c_int) -> Option { if slot .compare_exchange(true, false, Ordering::SeqCst, Ordering::Relaxed) .is_ok() { Some(signal) } else { None } } } signal-hook-0.3.17/src/iterator/exfiltrator/origin.rs000064400000000000000000000041521046102023000207510ustar 00000000000000//! 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 libc::{c_int, siginfo_t}; pub use super::raw::Slot; use super::sealed::Exfiltrator; use super::WithRawSiginfo; pub use crate::low_level::siginfo::{Origin, Process}; /// The [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that produces [`Origin`] of /// signals. /// /// # Examples /// /// ```rust /// # use signal_hook::consts::SIGUSR1; /// # use signal_hook::iterator::SignalsInfo; /// # use signal_hook::iterator::exfiltrator::WithOrigin; /// # /// # fn main() -> Result<(), std::io::Error> { /// // Subscribe to SIGUSR1, with information about the process. /// let mut signals = SignalsInfo::::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.wait().next().unwrap(); /// /// assert_eq!(SIGUSR1, received.signal); /// assert_eq!(my_pid, received.process.unwrap().pid); /// # Ok(()) } /// ``` #[derive(Copy, Clone, Debug, Default)] pub struct WithOrigin(WithRawSiginfo); // Safety: We need to be async-signal-safe. We delegate to other Exfiltrator, which already is and // call a function that promises to be (Origin::extract) unsafe impl Exfiltrator for WithOrigin { type Storage = Slot; type Output = Origin; fn supports_signal(&self, signal: c_int) -> bool { self.0.supports_signal(signal) } fn store(&self, slot: &Slot, signal: c_int, info: &siginfo_t) { self.0.store(slot, signal, info) } fn load(&self, slot: &Self::Storage, signal: c_int) -> Option { self.0 .load(slot, signal) .map(|info| unsafe { Origin::extract(&info) }) } fn init(&self, slot: &Self::Storage, signal: c_int) { self.0.init(slot, signal) } } signal-hook-0.3.17/src/iterator/exfiltrator/raw.rs000064400000000000000000000065461046102023000202640ustar 00000000000000//! An exfiltrator providing the raw [`siginfo_t`]. // Note on unsafety in this module: // * Implementing an unsafe trait, that one needs to ensure at least store is async-signal-safe. // That's done by delegating to the Channel (and reading an atomic pointer, but that one is // primitive op). // * A bit of juggling with atomic and raw pointers. In effect, that is just late lazy // initialization, the Slot is in line with Option would be, except that it is set atomically // during the init. Lifetime is ensured by not dropping until the Drop of the whole slot and that // is checked by taking `&mut self`. use std::sync::atomic::{AtomicPtr, Ordering}; use libc::{c_int, siginfo_t}; use super::sealed::Exfiltrator; use crate::low_level::channel::Channel; #[doc(hidden)] #[derive(Default, Debug)] pub struct Slot(AtomicPtr>); impl Drop for Slot { fn drop(&mut self) { let ptr = self.0.load(Ordering::Acquire); if !ptr.is_null() { drop(unsafe { Box::from_raw(ptr) }); } } } /// The [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that produces the raw /// [`libc::siginfo_t`]. Note that it might look differently on different OSes and its API is a /// little bit more limited than its C counterpart. /// /// You might prefer the [`WithOrigin`][super::WithOrigin] if you simply need information about the /// origin of the signal. /// /// # Examples /// /// ```rust /// # use signal_hook::consts::SIGUSR1; /// # use signal_hook::iterator::SignalsInfo; /// # use signal_hook::iterator::exfiltrator::WithRawSiginfo; /// # /// # fn main() -> Result<(), std::io::Error> { /// // Subscribe to SIGUSR1, with information about the process. /// let mut signals = SignalsInfo::::new(&[SIGUSR1])?; /// /// // Send ourselves a signal. /// signal_hook::low_level::raise(SIGUSR1)?; /// /// // Grab the signal and look into the details. /// let received = signals.wait().next().unwrap(); /// /// // Not much else is useful in a cross-platform way :-( /// assert_eq!(SIGUSR1, received.si_signo); /// # Ok(()) } /// ``` #[derive(Copy, Clone, Debug, Default)] pub struct WithRawSiginfo; unsafe impl Exfiltrator for WithRawSiginfo { type Storage = Slot; type Output = siginfo_t; fn supports_signal(&self, _: c_int) -> bool { true } fn store(&self, slot: &Slot, _: c_int, info: &siginfo_t) { let info = *info; // Condition just not to crash if someone forgot to call init. // // Lifetime is from init to our own drop, and drop needs &mut self. if let Some(slot) = unsafe { slot.0.load(Ordering::Acquire).as_ref() } { slot.send(info); } } fn load(&self, slot: &Slot, _: libc::c_int) -> Option { let slot = unsafe { slot.0.load(Ordering::Acquire).as_ref() }; // Condition just not to crash if someone forgot to call init. slot.and_then(|s| s.recv()) } fn init(&self, slot: &Self::Storage, _: c_int) { let new = Box::default(); let old = slot.0.swap(Box::into_raw(new), Ordering::Release); // We leak the pointer on purpose here. This is invalid state anyway and must not happen, // but if it still does, we can't drop that while some other thread might still be having // the raw pointer. assert!(old.is_null(), "Init called multiple times"); } } signal-hook-0.3.17/src/iterator/mod.rs000064400000000000000000000273611046102023000157050ustar 00000000000000//! An iterator over incoming signals. //! //! This provides a higher abstraction over the signals, providing //! 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. 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. //! //! This module (and everything in it) is turned by the `iterator` feature. It is **on** by //! default, the possibility to turn off is mostly possible for very special purposes (compiling on //! ` Result<(), Error> { //! let mut signals = Signals::new(&[ //! SIGHUP, //! SIGTERM, //! SIGINT, //! SIGQUIT, //! # SIGUSR1, //! ])?; //! # // A trick to terminate the example when run as doc-test. Not part of the real code. //! # signal_hook::low_level::raise(SIGUSR1).unwrap(); //! 'outer: loop { //! // Pick up signals that arrived since last time //! for signal in signals.pending() { //! match signal as libc::c_int { //! SIGHUP => { //! // Reload configuration //! // Reopen the log file //! } //! SIGTERM | SIGINT | SIGQUIT => { //! break 'outer; //! }, //! # SIGUSR1 => return Ok(()), //! _ => unreachable!(), //! } //! } //! // Do some bit of work ‒ something with upper limit on waiting, so we don't block //! // forever with a SIGTERM already waiting. //! } //! println!("Terminating. Bye bye"); //! Ok(()) //! } //! ``` pub mod backend; pub mod exfiltrator; use std::borrow::Borrow; use std::fmt::{Debug, Formatter, Result as FmtResult}; use std::io::{Error, ErrorKind, Read}; use std::os::unix::net::UnixStream; use libc::{self, c_int}; pub use self::backend::{Handle, Pending}; use self::backend::{PollResult, RefSignalIterator, SignalDelivery}; use self::exfiltrator::{Exfiltrator, SignalOnly}; /// The main structure of the module, representing interest in some signals. /// /// Unlike the helpers in other modules, this registers the signals when created and unregisters /// them on drop. It provides the pending signals during its lifetime, either in batches or as an /// infinite iterator. /// /// Most users will want to use it through the [`Signals`] type alias for simplicity. /// /// # Multiple threads /// /// Instances of this struct can be [sent][std::marker::Send] to other threads. In a multithreaded /// application this can be used to dedicate a separate thread for signal handling. In this case /// you should get a [`Handle`] using the [`handle`][Signals::handle] method before sending the /// `Signals` instance to a background thread. With the handle you will be able to shut down the /// background thread later, or to operatively add more signals. /// /// 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 /// # extern crate signal_hook; /// # /// # use std::io::Error; /// # use std::thread; /// use signal_hook::consts::signal::*; /// use signal_hook::iterator::Signals; /// /// # /// # fn main() -> Result<(), Error> { /// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?; /// let handle = signals.handle(); /// let thread = thread::spawn(move || { /// for signal in &mut signals { /// match signal { /// SIGUSR1 => {}, /// SIGUSR2 => {}, /// _ => unreachable!(), /// } /// } /// }); /// /// // Some time later... /// handle.close(); /// thread.join().unwrap(); /// # Ok(()) /// # } /// ``` pub struct SignalsInfo(SignalDelivery); impl SignalsInfo { /// Creates the `Signals` structure. /// /// This registers all the signals listed. The same restrictions (panics, errors) apply as /// for the [`Handle::add_signal`] method. pub fn new(signals: I) -> Result where I: IntoIterator, S: Borrow, E: Default, { Self::with_exfiltrator(signals, E::default()) } /// An advanced constructor with explicit [`Exfiltrator`]. pub fn with_exfiltrator(signals: I, exfiltrator: E) -> Result where I: IntoIterator, S: Borrow, { let (read, write) = UnixStream::pair()?; Ok(SignalsInfo(SignalDelivery::with_pipe( read, write, exfiltrator, signals, )?)) } /// Registers another signal to the set watched by this [`Signals`] instance. /// /// The same restrictions (panics, errors) apply as for the [`Handle::add_signal`] /// method. pub fn add_signal(&self, signal: c_int) -> Result<(), Error> { self.handle().add_signal(signal) } /// Returns an iterator of already received signals. /// /// This returns an iterator over all the signal numbers of the signals received since last /// time they were read (out of the set registered by this `Signals` instance). Note that they /// are returned in arbitrary order and a signal instance may returned only once even if it was /// received multiple times. /// /// This method returns immediately (does not block) and may produce an empty iterator if there /// are no signals ready. pub fn pending(&mut self) -> Pending { self.0.pending() } /// Block until the stream contains some bytes. /// /// Returns true if it was possible to read a byte and false otherwise. fn has_signals(read: &mut UnixStream) -> Result { loop { match read.read(&mut [0u8]) { Ok(num_read) => break Ok(num_read > 0), // If we get an EINTR error it is fine to retry reading from the stream. // Otherwise we should pass on the error to the caller. Err(error) => { if error.kind() != ErrorKind::Interrupted { break Err(error); } } } } } /// Waits for some signals to be available and returns an iterator. /// /// This is similar to [`pending`][SignalsInfo::pending]. If there are no signals available, it /// tries to wait for some to arrive. However, due to implementation details, this still can /// produce an empty iterator. /// /// This can block for arbitrary long time. If the [`Handle::close`] method is used in /// another thread this method will return immediately. /// /// Note that the blocking is done in this method, not in the iterator. pub fn wait(&mut self) -> Pending { match self.0.poll_pending(&mut Self::has_signals) { Ok(Some(pending)) => pending, // Because of the blocking has_signals method the poll_pending method // only returns None if the instance is closed. But we want to return // a possibly empty pending object anyway. Ok(None) => self.pending(), // Users can't manipulate the internal file descriptors and the way we use them // shouldn't produce any errors. So it is OK to panic. Err(error) => panic!("Unexpected error: {}", error), } } /// Is it closed? /// /// See [`close`][Handle::close]. pub fn is_closed(&self) -> bool { self.handle().is_closed() } /// Get an infinite iterator over arriving signals. /// /// The iterator's `next()` blocks as necessary to wait for signals to arrive. This is adequate /// if you want to designate a thread solely to handling signals. If multiple signals come at /// the same time (between two values produced by the iterator), they will be returned in /// arbitrary order. Multiple instances of the same signal may be collated. /// /// This is also the iterator returned by `IntoIterator` implementation on `&mut Signals`. /// /// This iterator terminates only if explicitly [closed][Handle::close]. /// /// # Examples /// /// ```rust /// # extern crate libc; /// # extern crate signal_hook; /// # /// # use std::io::Error; /// # use std::thread; /// # /// use signal_hook::consts::signal::*; /// use signal_hook::iterator::Signals; /// /// # fn main() -> Result<(), Error> { /// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?; /// let handle = signals.handle(); /// thread::spawn(move || { /// for signal in signals.forever() { /// match signal { /// SIGUSR1 => {}, /// SIGUSR2 => {}, /// _ => unreachable!(), /// } /// } /// }); /// handle.close(); /// # Ok(()) /// # } /// ``` pub fn forever(&mut self) -> Forever { Forever(RefSignalIterator::new(&mut self.0)) } /// Get a shareable handle to a [`Handle`] for this instance. /// /// This can be used to add further signals or close the [`Signals`] instance. pub fn handle(&self) -> Handle { self.0.handle() } } impl Debug for SignalsInfo where E: Debug + Exfiltrator, E::Storage: Debug, { fn fmt(&self, fmt: &mut Formatter) -> FmtResult { fmt.debug_tuple("Signals").field(&self.0).finish() } } impl<'a, E: Exfiltrator> IntoIterator for &'a mut SignalsInfo { type Item = E::Output; type IntoIter = Forever<'a, E>; fn into_iter(self) -> Self::IntoIter { self.forever() } } /// An infinite iterator of arriving signals. pub struct Forever<'a, E: Exfiltrator>(RefSignalIterator<'a, UnixStream, E>); impl<'a, E: Exfiltrator> Iterator for Forever<'a, E> { type Item = E::Output; fn next(&mut self) -> Option { loop { match self.0.poll_signal(&mut SignalsInfo::::has_signals) { PollResult::Signal(result) => break Some(result), PollResult::Closed => break None, // In theory, the poll_signal should not return PollResult::Pending. Nevertheless, // there's a race condition - if the other side closes the pipe/socket after // checking for it being closed, then the `read` there returns 0 as EOF. That // appears as pending here. Next time we should get Closed. PollResult::Pending => continue, // Users can't manipulate the internal file descriptors and the way we use them // shouldn't produce any errors. So it is OK to panic. PollResult::Err(error) => panic!("Unexpected error: {}", error), } } } } /// A type alias for an iterator returning just the signal numbers. /// /// This is the simplified version for most of the use cases. For advanced usages, the /// [`SignalsInfo`] with explicit [`Exfiltrator`] type can be used. pub type Signals = SignalsInfo; signal-hook-0.3.17/src/lib.rs000064400000000000000000000502721046102023000140400ustar 00000000000000#![doc( test(attr(deny(warnings))), test(attr(allow(bare_trait_objects, unknown_lints))) )] #![warn(missing_docs)] // Don't fail on links to things not enabled in features #![allow( unknown_lints, renamed_and_removed_lints, intra_doc_link_resolution_failure, broken_intra_doc_links )] // These little nifty labels saying that something needs a feature to be enabled #![cfg_attr(docsrs, feature(doc_cfg))] //! Library for easier and safe Unix signal handling //! //! Unix signals are inherently hard to handle correctly, for several reasons: //! //! * They are a global resource. If a library wants to set its own signal handlers, it risks //! disrupting some other library. It is possible to chain the previous signal handler, but then //! it is impossible to remove the old signal handlers from the chains in any practical manner. //! * They can be called from whatever thread, requiring synchronization. Also, as they can //! interrupt a thread at any time, making most handling race-prone. //! * According to the POSIX standard, the set of functions one may call inside a signal handler is //! limited to very few of them. To highlight, mutexes (or other locking mechanisms) and memory //! allocation and deallocation is *not* allowed. //! //! # The goal of the library //! //! The aim is to subscriptions to signals a „structured“ resource, in a similar way memory //! allocation is ‒ parts of the program can independently subscribe and it's the same part of the //! program that can give them up, independently of what the other parts do. Therefore, it is //! possible to register multiple actions to the same signal. //! //! Another goal is to shield applications away from differences between platforms. Various Unix //! systems have little quirks and differences that need to be worked around and that's not //! something every application should be dealing with. We even try to provide some support for //! Windows, but we lack the expertise in that area, so that one is not complete and is a bit rough //! (if you know how it works there and are willing to either contribute the code or consult, //! please get in touch). //! //! Furthermore, it provides implementation of certain common signal-handling patterns, usable from //! safe Rust, without the application author needing to learn about *all* the traps. //! //! Note that despite everything, there are still some quirks around signal handling that are not //! possible to paper over and need to be considered. Also, there are some signal use cases that //! are inherently unsafe and they are not covered by this crate. //! //! # Anatomy of the crate //! //! The crate is split into several modules. //! //! The easiest way to handle signals is using the [`Signals`][crate::iterator::Signals] iterator //! thing. It can register for a set of signals and produce them one by one, in a blocking manner. //! You can reserve a thread for handling them as they come. If you want something asynchronous, //! there are adaptor crates for the most common asynchronous runtimes. The module also contains //! ways to build iterators that produce a bit more information that just the signal number. //! //! The [`flag`] module contains routines to set a flag based on incoming signals and to do //! certain actions inside the signal handlers based on the flags (the flags can also be //! manipulated by the rest of the application). This allows building things like checking if a //! signal happened on each loop iteration or making sure application shuts down on the second //! CTRL+C if it got stuck in graceful shutdown requested by the first. //! //! The [`consts`] module contains some constants, most importantly the signal numbers themselves //! (these are just re-exports from [`libc`] and if your OS has some extra ones, you can use them //! too, this is just for convenience). //! //! And last, there is the [`low_level`] module. It contains routines to directly register and //! unregister arbitrary actions. Some of the patters in the above modules return a [`SigId`], //! which can be used with the [`low_level::unregister`] to remove the action. There are also some //! other utilities that are more suited to build other abstractions with than to use directly. //! //! Certain parts of the library can be enabled or disabled with use flags: //! //! * `channel`: The [low_level::channel] module (on by default). //! * `iterator`: The [iterator] module (on by default). //! * `extended-sig-info`: Support for providing more information in the iterators or from the //! async adaptor crates. This is off by default. //! //! # Limitations //! //! * OS limitations still apply. Certain signals are not possible to override or subscribe to ‒ //! `SIGKILL` or `SIGSTOP`. //! * Overriding some others is probably a very stupid idea (or very unusual needs) ‒ handling eg. //! `SIGSEGV` is not something done lightly. For that reason, the crate will panic in case //! registering of these is attempted (see [`FORBIDDEN`][crate::consts::FORBIDDEN]. If you still //! need to do so, you can find such APIs in the `signal-hook-registry` backend crate, but //! additional care must be taken. //! * Interaction with other signal-handling libraries is limited. If signal-hook finds an existing //! handler present, it chain-calls it from the signal it installs and assumes other libraries //! would do the same, but that's everything that can be done to make it work with libraries not //! based on [`signal-hook-registry`](https://lib.rs/signal-hook-registry) //! (the backend of this crate). //! * The above chaining contains a race condition in multi-threaded programs, where the previous //! handler might not get called if it is received during the registration process. This is //! handled (at least on non-windows platforms) on the same thread where the registration //! happens, therefore it is advised to register at least one action for each signal of interest //! early, before any additional threads are started. Registering any additional (or removing and //! registering again) action on the same signal is without the race condition. //! * Once at least one action is registered for a signal, the default action is replaced (this is //! how signals work in the OS). Even if all actions of that signal are removed, `signal-hook` //! does not restore the default handler (such behaviour would be at times inconsistent with //! making the actions independent and there's no reasonable way to do so in a race-free way in a //! multi-threaded program while also dealing with signal handlers registered with other //! libraries). It is, however, possible to *emulate* the default handler (see the //! [`emulate_default_handler`][low_level::emulate_default_handler]) ‒ there are only 4 //! default handlers: //! - Ignore. This is easy to emulate. //! - Abort. Depending on if you call it from within a signal handler of from outside, the //! [`low_level::abort`] or [`std::process::abort`] can be used. //! - Terminate. This can be done with `exit` ([`low_level::exit`] or [`std::process::exit`]). //! - Stop. It is possible to [`raise`][low_level::raise] the [`SIGSTOP`][consts::SIGSTOP] signal. //! That one can't be replaced and always stops the application. //! * Many of the patterns here can collate multiple instances of the same signal into fewer //! instances, if the application doesn't consume them fast enough. This is consistent with what //! the kernel does if the application doesn't keep up with them (at least for non-realtime //! signals, see below), so it is something one needs to deal with anyway. //! * (By design) the library mostly _postpones_ or helps the user postpone acting on the signals //! until later. This, in combination with the above collating inside the library may make it //! unsuitable for realtime signals. These usually want to be handled directly inside the signal //! handler ‒ which still can be done with [signal_hook_registry::register], but using unsafe and //! due care. Patterns for working safely with realtime signals are not unwanted in the library, //! but nobody contributed them yet. //! //! # Signal masks //! //! As the library uses `sigaction` under the hood, signal masking works as expected (eg. with //! `pthread_sigmask`). This means, signals will *not* be delivered if the signal is masked in all //! program's threads. //! //! By the way, if you do want to modify the signal mask (or do other Unix-specific magic), the //! [nix](https://lib.rs/crates/nix) crate offers safe interface to many low-level functions, //! including //! [`pthread_sigmask`](https://docs.rs/nix/0.11.0/nix/sys/signal/fn.pthread_sigmask.html). //! //! # Portability //! //! It should work on any POSIX.1-2001 system, which are all the major big OSes with the notable //! exception of Windows. //! //! Non-standard signals are also supported. Pass the signal value directly from `libc` or use //! the numeric value directly. //! //! ```rust //! use std::sync::Arc; //! use std::sync::atomic::{AtomicBool}; //! let term = Arc::new(AtomicBool::new(false)); //! let _ = signal_hook::flag::register(libc::SIGINT, Arc::clone(&term)); //! ``` //! //! This crate includes a limited support for Windows, based on `signal`/`raise` in the CRT. //! There are differences in both API and behavior: //! //! - Many parts of the library are not available there. //! - We have only a few signals: `SIGABRT`, `SIGABRT_COMPAT`, `SIGBREAK`, //! `SIGFPE`, `SIGILL`, `SIGINT`, `SIGSEGV` and `SIGTERM`. //! - Due to lack of signal blocking, there's a race condition. //! After the call to `signal`, there's a moment where we miss a signal. //! That means when you register a handler, there may be a signal which invokes //! neither the default handler or the handler you register. //! - Handlers registered by `signal` in Windows are cleared on first signal. //! To match behavior in other platforms, we re-register the handler each time the handler is //! called, but there's a moment where we miss a handler. //! That means when you receive two signals in a row, there may be a signal which invokes //! the default handler, nevertheless you certainly have registered the handler. //! //! Moreover, signals won't work as you expected. `SIGTERM` isn't actually used and //! not all `Ctrl-C`s are turned into `SIGINT`. //! //! Patches to improve Windows support in this library are welcome. //! //! # Features //! //! There are several feature flags that control how much is available as part of the crate, some //! enabled by default. //! //! * `channel`: (enabled by default) The [Channel][crate::low_level::channel] synchronization //! primitive for exporting data out of signal handlers. //! * `iterator`: (enabled by default) An [Signals iterator][crate::iterator::Signals] that //! provides a convenient interface for receiving signals in rust-friendly way. //! * `extended-siginfo` adds support for providing extra information as part of the iterator //! interface. //! //! # Examples //! //! ## Using a flag to terminate a loop-based application //! //! ```rust //! use std::io::Error; //! use std::sync::Arc; //! use std::sync::atomic::{AtomicBool, Ordering}; //! //! fn main() -> Result<(), Error> { //! let term = Arc::new(AtomicBool::new(false)); //! signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term))?; //! while !term.load(Ordering::Relaxed) { //! // Do some time-limited stuff here //! // (if this could block forever, then there's no guarantee the signal will have any //! // effect). //! # //! # // Hack to terminate the example, not part of the real code. //! # term.store(true, Ordering::Relaxed); //! } //! Ok(()) //! } //! ``` //! //! ## A complex signal handling with a background thread //! //! This also handles the double CTRL+C situation (eg. the second CTRL+C kills) and resetting the //! terminal on `SIGTSTP` (CTRL+Z, curses-based applications should do something like this). //! //! ```rust //! # #[cfg(feature = "extended-siginfo")] pub mod test { //! use std::io::Error; //! use std::sync::Arc; //! use std::sync::atomic::AtomicBool; //! //! use signal_hook::consts::signal::*; //! use signal_hook::consts::TERM_SIGNALS; //! use signal_hook::flag; //! // A friend of the Signals iterator, but can be customized by what we want yielded about each //! // signal. //! use signal_hook::iterator::SignalsInfo; //! use signal_hook::iterator::exfiltrator::WithOrigin; //! use signal_hook::low_level; //! //! # struct App; //! # impl App { //! # fn run_background() -> Self { Self } //! # fn wait_for_stop(self) {} //! # fn restore_term(&self) {} //! # fn claim_term(&self) {} //! # fn resize_term(&self) {} //! # fn reload_config(&self) {} //! # fn print_stats(&self) {} //! # } //! # pub //! fn main() -> Result<(), Error> { //! // Make sure double CTRL+C and similar kills //! let term_now = Arc::new(AtomicBool::new(false)); //! for sig in TERM_SIGNALS { //! // When terminated by a second term signal, exit with exit code 1. //! // This will do nothing the first time (because term_now is false). //! flag::register_conditional_shutdown(*sig, 1, Arc::clone(&term_now))?; //! // But this will "arm" the above for the second time, by setting it to true. //! // The order of registering these is important, if you put this one first, it will //! // first arm and then terminate ‒ all in the first round. //! flag::register(*sig, Arc::clone(&term_now))?; //! } //! //! // Subscribe to all these signals with information about where they come from. We use the //! // extra info only for logging in this example (it is not available on all the OSes or at //! // all the occasions anyway, it may return `Unknown`). //! let mut sigs = vec![ //! // Some terminal handling //! SIGTSTP, SIGCONT, SIGWINCH, //! // Reload of configuration for daemons ‒ um, is this example for a TUI app or a daemon //! // O:-)? You choose... //! SIGHUP, //! // Application-specific action, to print some statistics. //! SIGUSR1, //! ]; //! sigs.extend(TERM_SIGNALS); //! let mut signals = SignalsInfo::::new(&sigs)?; //! # low_level::raise(SIGTERM)?; // Trick to terminate the example //! //! // This is the actual application that'll start in its own thread. We'll control it from //! // this thread based on the signals, but it keeps running. //! // This is called after all the signals got registered, to avoid the short race condition //! // in the first registration of each signal in multi-threaded programs. //! let app = App::run_background(); //! //! // Consume all the incoming signals. This happens in "normal" Rust thread, not in the //! // signal handlers. This means that we are allowed to do whatever we like in here, without //! // restrictions, but it also means the kernel believes the signal already got delivered, we //! // handle them in delayed manner. This is in contrast with eg the above //! // `register_conditional_shutdown` where the shutdown happens *inside* the handler. //! let mut has_terminal = true; //! for info in &mut signals { //! // Will print info about signal + where it comes from. //! eprintln!("Received a signal {:?}", info); //! match info.signal { //! SIGTSTP => { //! // Restore the terminal to non-TUI mode //! if has_terminal { //! app.restore_term(); //! has_terminal = false; //! // And actually stop ourselves. //! low_level::emulate_default_handler(SIGTSTP)?; //! } //! } //! SIGCONT => { //! if !has_terminal { //! app.claim_term(); //! has_terminal = true; //! } //! } //! SIGWINCH => app.resize_term(), //! SIGHUP => app.reload_config(), //! SIGUSR1 => app.print_stats(), //! term_sig => { // These are all the ones left //! eprintln!("Terminating"); //! assert!(TERM_SIGNALS.contains(&term_sig)); //! break; //! } //! } //! } //! //! // If during this another termination signal comes, the trick at the top would kick in and //! // terminate early. But if it doesn't, the application shuts down gracefully. //! app.wait_for_stop(); //! //! Ok(()) //! } //! # } //! # fn main() { //! # #[cfg(feature = "extended-siginfo")] test::main().unwrap(); //! # } //! ``` //! //! # Asynchronous runtime support //! //! If you are looking for integration with an asynchronous runtime take a look at one of the //! following adapter crates: //! //! * [`signal-hook-async-std`](https://docs.rs/signal-hook-async-std) for async-std support //! * [`signal-hook-mio`](https://docs.rs/signal-hook-mio) for MIO support //! * [`signal-hook-tokio`](https://docs.rs/signal-hook-tokio) for Tokio support //! //! Feel free to open a pull requests if you want to add support for runtimes not mentioned above. //! //! # Porting from previous versions //! //! There were some noisy changes when going from 0.2 version to the 0.3 version. In particular: //! //! * A lot of things moved around to make the structure of the crate a bit more understandable. //! Most of the time it should be possible to just search the documentation for the name that //! can't be resolved to discover the new location. //! - The signal constants (`SIGTERM`, for example) are in [`consts`] submodule (individual //! imports) and in the [`consts::signal`] (for wildcard import of all of them). //! - Some APIs that are considered more of a low-level building blocks than for casual day to //! day use are now in the [`low_level`] submodule. //! * The previous version contained the `cleanup` module that allowed for removal of the actions //! in rather destructive way (nuking actions of arbitrary other parts of the program). This is //! completely gone in this version. The use case of shutting down the application on second //! CTRL+C is now supported by a pattern described in the [`flag`] submodule. For other similar //! needs, refer above for emulating default handlers. pub mod flag; #[cfg(all(not(windows), feature = "iterator"))] #[cfg_attr(docsrs, doc(cfg(all(not(windows), feature = "iterator"))))] pub mod iterator; pub mod low_level; /// The low-level constants. /// /// Like the signal numbers. pub mod consts { use libc::c_int; /// The signal constants. /// /// Can be mass-imported by `use signal_hook::consts::signal::*`, without polluting the /// namespace with other names. Also available in the [`consts`][crate::consts] directly (but /// with more constants around). pub mod signal { #[cfg(not(windows))] pub use libc::{ SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGKILL, SIGPIPE, SIGPROF, SIGQUIT, SIGSEGV, SIGSTOP, SIGSYS, SIGTERM, SIGTRAP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGUSR1, SIGUSR2, SIGVTALRM, SIGWINCH, SIGXCPU, SIGXFSZ, }; #[cfg(not(any(windows, target_os = "haiku")))] pub use libc::SIGIO; #[cfg(any( target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] pub use libc::SIGINFO; #[cfg(windows)] pub use libc::{SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM}; // NOTE: they perhaps deserve backport to libc. #[cfg(windows)] /// Same as `SIGABRT`, but the number is compatible to other platforms. pub const SIGABRT_COMPAT: libc::c_int = 6; #[cfg(windows)] /// Ctrl-Break is pressed for Windows Console processes. pub const SIGBREAK: libc::c_int = 21; } pub use self::signal::*; pub use signal_hook_registry::FORBIDDEN; /// Various signals commonly requesting shutdown of an application. #[cfg(not(windows))] pub const TERM_SIGNALS: &[c_int] = &[SIGTERM, SIGQUIT, SIGINT]; /// Various signals commonly requesting shutdown of an application. #[cfg(windows)] pub const TERM_SIGNALS: &[c_int] = &[SIGTERM, SIGINT]; } pub use signal_hook_registry::SigId; signal-hook-0.3.17/src/low_level/channel.rs000064400000000000000000000174151046102023000166740ustar 00000000000000//! A restricted channel to pass data from signal handler. //! //! When trying to communicate data from signal handler to the outside world, one can use an atomic //! variable (as it doesn't lock, so it can be made async-signal-safe). But this won't work for //! larger data. //! //! This module provides a channel that can be used for that purpose. It is used by certain //! [exfiltrators][crate::iterator::exfiltrator], but can be used as building block for custom //! actions. In general, this is not a ready-made end-user API. //! //! # How does it work //! //! Each channel has a fixed number of slots and two queues (one for empty slots, one for full //! slots). A signal handler takes a slot out of the empty one, fills it and passes it into the //! full one. Outside of signal handler, it can take the value out of the full queue and return the //! slot to the empty queue. //! //! The queues are implemented as bit-encoded indexes of the slots in the storage. The bits are //! stored in an atomic variable. //! //! Note that the algorithm allows for a slot to be in neither queue (when it is being emptied or //! filled). //! //! # Fallible allocation of a slot //! //! It is apparent that allocation of a new slot can fail (there's nothing in the empty slot). In //! such case, there's no way to send the new value out of the handler (there's no way to safely //! wait for a slot to appear, because the handler can be blocking the thread that is responsible //! for emptying them). But that's considered acceptable ‒ even the kernel collates the same kinds //! of signals together if they are not consumed by application fast enough and there are no free //! slots exactly because some are being filled, emptied or are full ‒ in particular, the whole //! system will yield a signal. //! //! This assumes that separate signals don't share the same buffer and that there's only one reader //! (using multiple readers is still safe, but it is possible that all slots would be inside the //! readers, but already empty, so the above argument would not hold). // TODO: Other sizes? Does anyone need more than 5 slots? use std::cell::UnsafeCell; use std::sync::atomic::{AtomicU16, Ordering}; const SLOTS: usize = 5; const BITS: u16 = 3; const MASK: u16 = 0b111; fn get(n: u16, idx: u16) -> u16 { (n >> (BITS * idx)) & MASK } fn set(n: u16, idx: u16, v: u16) -> u16 { let v = v << (BITS * idx); let mask = MASK << (BITS * idx); (n & !mask) | v } fn enqueue(q: &AtomicU16, val: u16) { let mut current = q.load(Ordering::Relaxed); loop { let empty = (0..SLOTS as u16) .find(|i| get(current, *i) == 0) .expect("No empty slot available"); let modified = set(current, empty, val); match q.compare_exchange_weak(current, modified, Ordering::Release, Ordering::Relaxed) { Ok(_) => break, Err(changed) => current = changed, // And retry with the changed value } } } fn dequeue(q: &AtomicU16) -> Option { let mut current = q.load(Ordering::Relaxed); loop { let val = current & MASK; // It's completely empty if val == 0 { break None; } let modified = current >> BITS; match q.compare_exchange_weak(current, modified, Ordering::Acquire, Ordering::Relaxed) { Ok(_) => break Some(val), Err(changed) => current = changed, } } } /// A restricted async-signal-safe channel /// /// This is a bit like the usual channel used for inter-thread communication, but with several /// restrictions: /// /// * There's a limited number of slots (currently 5). /// * There's no way to wait for a place in it or for a value. If value is not available, `None` is /// returned. If there's no space for a value, the value is silently dropped. /// /// In exchange for that, all the operations on that channel are async-signal-safe. That means it /// is possible to use it to communicate between a signal handler and the rest of the world with it /// (specifically, it's designed to send information from the handler to the rest of the /// application). The throwing out of values when full is in line with collating of the same type /// in kernel (you should not use the same channel for multiple different signals). /// /// Technically, this is a MPMC queue which preserves order, but it is expected to be used in MPSC /// mode mostly (in theory, multiple threads can be executing a signal handler for the same signal /// at the same time). The channel is not responsible for wakeups. /// /// While the channel is async-signal-safe, you still need to make sure *creating* of the values is /// too (it should not contain anything that allocates, for example ‒ so no `String`s inside, etc). /// /// The code was *not* tuned for performance (signals are not expected to happen often). pub struct Channel { storage: [UnsafeCell>; SLOTS], empty: AtomicU16, full: AtomicU16, } impl Channel { /// Creates a new channel with nothing in it. pub fn new() -> Self { let storage = Default::default(); let me = Self { storage, empty: AtomicU16::new(0), full: AtomicU16::new(0), }; for i in 1..SLOTS + 1 { enqueue(&me.empty, i as u16); } me } /// Inserts a value into the channel. /// /// If the value doesn't fit, it is silently dropped. Never blocks. pub fn send(&self, val: T) { if let Some(empty_idx) = dequeue(&self.empty) { unsafe { *self.storage[empty_idx as usize - 1].get() = Some(val) }; enqueue(&self.full, empty_idx); } } /// Takes a value from the channel. /// /// Or returns `None` if the channel is empty. Never blocks. pub fn recv(&self) -> Option { dequeue(&self.full).map(|idx| { let result = unsafe { &mut *self.storage[idx as usize - 1].get() } .take() .expect("Full slot with nothing in it"); enqueue(&self.empty, idx); result }) } } impl Default for Channel { fn default() -> Self { Self::new() } } unsafe impl Send for Channel {} // Yes, really Send -> Sync. Having a reference to Channel allows Sending Ts, but not having refs // on them. unsafe impl Sync for Channel {} #[cfg(test)] mod tests { use std::sync::Arc; use std::thread; use super::*; #[test] fn new_empty() { let channel = Channel::::new(); assert!(channel.recv().is_none()); assert!(channel.recv().is_none()); } #[test] fn pass_value() { let channel = Channel::new(); channel.send(42); assert_eq!(42, channel.recv().unwrap()); assert!(channel.recv().is_none()); } #[test] fn multiple() { let channel = Channel::new(); for i in 0..1000 { channel.send(i); assert_eq!(i, channel.recv().unwrap()); assert!(channel.recv().is_none()); } } #[test] fn overflow() { let channel = Channel::new(); for i in 0..10 { channel.send(i); } for i in 0..5 { assert_eq!(i, channel.recv().unwrap()); } assert!(channel.recv().is_none()); } #[test] fn multi_thread() { let channel = Arc::new(Channel::::new()); let sender = thread::spawn({ let channel = Arc::clone(&channel); move || { for i in 0..4 { channel.send(i); } } }); let mut results = Vec::new(); while results.len() < 4 { results.extend(channel.recv()); } assert_eq!(vec![0, 1, 2, 3], results); sender.join().unwrap(); } } signal-hook-0.3.17/src/low_level/extract.c000064400000000000000000000025261046102023000165310ustar 00000000000000/* * Low-level extraction code to overcome rust's libc not having the best access * to siginfo_t details. */ #include #include #include 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 }, #ifdef SI_MESGQ { SI_MESGQ, -1, 5 }, #endif { 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; } signal-hook-0.3.17/src/low_level/mod.rs000064400000000000000000000032121046102023000160310ustar 00000000000000//! Some low level utilities //! //! More often to build other abstractions than used directly. use std::io::Error; use libc::c_int; #[cfg(feature = "channel")] #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] pub mod channel; #[cfg(not(windows))] #[cfg_attr(docsrs, doc(cfg(not(windows))))] pub mod pipe; #[cfg(feature = "extended-siginfo-raw")] #[cfg_attr(docsrs, doc(cfg(feature = "extended-siginfo-raw")))] pub mod siginfo; mod signal_details; pub use signal_hook_registry::{register, unregister}; pub use self::signal_details::{emulate_default_handler, signal_name}; /// The usual raise, just the safe wrapper around it. /// /// This is async-signal-safe. pub fn raise(sig: c_int) -> Result<(), Error> { let result = unsafe { libc::raise(sig) }; if result == -1 { Err(Error::last_os_error()) } else { Ok(()) } } /// A bare libc abort. /// /// Unlike the [std::process::abort], this one is guaranteed to contain no additions or wrappers /// and therefore is async-signal-safe. You can use this to terminate the application from within a /// signal handler. pub fn abort() -> ! { unsafe { libc::abort(); } } /// A bare libc exit. /// /// Unlike the [std::process::exit], this one is guaranteed to contain no additions or wrappers and /// therefore is async-signal-safe. You can use this to terminate the application from within a /// signal handler. /// /// Also, see [`register_conditional_shutdown`][crate::flag::register_conditional_shutdown]. pub fn exit(status: c_int) -> ! { unsafe { // Yes, the one with underscore. That one doesn't call the at-exit hooks. libc::_exit(status); } } signal-hook-0.3.17/src/low_level/pipe.rs000064400000000000000000000240301046102023000162100ustar 00000000000000//! Module with the self-pipe pattern. //! //! One of the common patterns around signals is to have a pipe with both ends in the same program. //! Whenever there's a signal, the signal handler writes one byte of garbage data to the write end, //! unless the pipe's already full. The application then can handle the read end. //! //! This has two advantages. First, the real signal action moves outside of the signal handler //! where there are a lot less restrictions. Second, it fits nicely in all kinds of asynchronous //! loops and has less chance of race conditions. //! //! This module offers premade functions for the write end (and doesn't insist that it must be a //! pipe ‒ anything that can be written to is fine ‒ sockets too, therefore `UnixStream::pair` is a //! good candidate). //! //! If you want to integrate with some asynchronous library, plugging streams from `mio-uds` or //! `tokio-uds` libraries should work. //! //! If it looks too low-level for your needs, the [`iterator`][crate::iterator] module contains some //! higher-lever interface that also uses a self-pipe pattern under the hood. //! //! # Correct order of handling //! //! A care needs to be taken to avoid race conditions, especially when handling the same signal in //! a loop. Specifically, another signal might come when the action for the previous signal is //! being taken. The correct order is first to clear the content of the pipe (read some/all data //! from it) and then take the action. This way a spurious wakeup can happen (the pipe could wake //! up even when no signal came after the signal was taken, because ‒ it arrived between cleaning //! the pipe and taking the action). Note that some OS primitives (eg. `select`) suffer from //! spurious wakeups themselves (they can claim a FD is readable when it is not true) and blocking //! `read` might return prematurely (with eg. `EINTR`). //! //! The reverse order of first taking the action and then clearing the pipe might lose signals, //! which is usually worse. //! //! This is not a problem with blocking on reading from the pipe (because both the blocking and //! cleaning is the same action), but in case of asynchronous handling it matters. //! //! If you want to combine setting some flags with a self-pipe pattern, the flag needs to be set //! first, then the pipe written. On the read end, first the pipe needs to be cleaned, then the //! flag and then the action taken. This is what the [`SignalsInfo`][crate::iterator::SignalsInfo] //! structure does internally. //! //! # Write collating //! //! While unlikely if handled correctly, it is possible the write end is full when a signal comes. //! In such case the signal handler simply does nothing. If the write end is full, the read end is //! readable and therefore will wake up. On the other hand, blocking in the signal handler would //! definitely be a bad idea. //! //! However, this also means the number of bytes read from the end might be lower than the number //! of signals that arrived. This should not generally be a problem, since the OS already collates //! signals of the same kind together. //! //! # Examples //! //! This example waits for at last one `SIGUSR1` signal to come before continuing (and //! terminating). It sends the signal to itself, so it correctly terminates. //! //! ```rust //! use std::io::{Error, Read}; //! use std::os::unix::net::UnixStream; //! //! use signal_hook::consts::SIGUSR1; //! use signal_hook::low_level::{pipe, raise}; //! //! fn main() -> Result<(), Error> { //! let (mut read, write) = UnixStream::pair()?; //! pipe::register(SIGUSR1, write)?; //! // This will write into the pipe write end through the signal handler //! raise(SIGUSR1).unwrap(); //! let mut buff = [0]; //! read.read_exact(&mut buff)?; //! println!("Happily terminating"); //! Ok(()) //! } use std::io::{Error, ErrorKind}; use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; use libc::{self, c_int}; use crate::SigId; #[cfg(target_os = "aix")] const MSG_NOWAIT: i32 = libc::MSG_NONBLOCK; #[cfg(not(target_os = "aix"))] const MSG_NOWAIT: i32 = libc::MSG_DONTWAIT; #[derive(Copy, Clone)] pub(crate) enum WakeMethod { Send, Write, } struct WakeFd { fd: RawFd, method: WakeMethod, } impl WakeFd { /// Sets close on exec and nonblock on the inner file descriptor. fn set_flags(&self) -> Result<(), Error> { unsafe { let flags = libc::fcntl(self.as_raw_fd(), libc::F_GETFL, 0); if flags == -1 { return Err(Error::last_os_error()); } let flags = flags | libc::O_NONBLOCK | libc::O_CLOEXEC; if libc::fcntl(self.as_raw_fd(), libc::F_SETFL, flags) == -1 { return Err(Error::last_os_error()); } } Ok(()) } fn wake(&self) { wake(self.fd, self.method); } } impl AsRawFd for WakeFd { fn as_raw_fd(&self) -> RawFd { self.fd } } impl Drop for WakeFd { fn drop(&mut self) { unsafe { libc::close(self.fd); } } } pub(crate) fn wake(pipe: RawFd, method: WakeMethod) { unsafe { // This writes some data into the pipe. // // There are two tricks: // * First, the crazy cast. The first part turns reference into pointer. The second part // turns pointer to u8 into a pointer to void, which is what write requires. // * Second, we ignore errors, on purpose. We don't have any means to handling them. The // two conceivable errors are EBADFD, if someone passes a non-existent file descriptor or // if it is closed. The second is EAGAIN, in which case the pipe is full ‒ there were // many signals, but the reader didn't have time to read the data yet. It'll still get // woken up, so not fitting another letter in it is fine. let data = b"X" as *const _ as *const _; match method { WakeMethod::Write => libc::write(pipe, data, 1), WakeMethod::Send => libc::send(pipe, data, 1, MSG_NOWAIT), }; } } /// Registers a write to a self-pipe whenever there's the signal. /// /// In this case, the pipe is taken as the `RawFd`. It'll be closed on deregistration. Effectively, /// the function takes ownership of the file descriptor. This includes feeling free to set arbitrary /// flags on it, including file status flags (that are shared across file descriptors created by /// `dup`). /// /// Note that passing the wrong file descriptor won't cause UB, but can still lead to severe bugs ‒ /// like data corruptions in files. Prefer using [`register`] if possible. /// /// Also, it is perfectly legal for multiple writes to be collated together (if not consumed) and /// to generate spurious wakeups (but will not generate spurious *bytes* in the pipe). /// /// # Internal details /// /// Internally, it *currently* does following. Note that this is *not* part of the stability /// guarantees and may change if necessary. /// /// * If the file descriptor can be used with [`send`][libc::send], it'll be used together with /// [`MSG_DONTWAIT`][libc::MSG_DONTWAIT]. This is tested by sending `0` bytes of data (depending /// on the socket type, this might wake the read end with an empty message). /// * If it is not possible, the [`O_NONBLOCK`][libc::O_NONBLOCK] will be set on the file /// descriptor and [`write`][libc::write] will be used instead. pub fn register_raw(signal: c_int, pipe: RawFd) -> Result { let res = unsafe { libc::send(pipe, &[] as *const _, 0, MSG_NOWAIT) }; let fd = match (res, Error::last_os_error().kind()) { (0, _) | (-1, ErrorKind::WouldBlock) => WakeFd { fd: pipe, method: WakeMethod::Send, }, _ => { let fd = WakeFd { fd: pipe, method: WakeMethod::Write, }; fd.set_flags()?; fd } }; let action = move || fd.wake(); unsafe { super::register(signal, action) } } /// Registers a write to a self-pipe whenever there's the signal. /// /// The ownership of pipe is taken and will be closed whenever the created action is unregistered. /// /// Note that if you want to register the same pipe for multiple signals, there's `try_clone` /// method on many unix socket primitives. /// /// See [`register_raw`] for further details. pub fn register

(signal: c_int, pipe: P) -> Result where P: IntoRawFd + 'static, { register_raw(signal, pipe.into_raw_fd()) } #[cfg(test)] mod tests { use std::io::Read; use std::os::unix::net::{UnixDatagram, UnixStream}; use super::*; // Note: multiple tests share the SIGUSR1 signal. This is fine, we only need to know the signal // arrives. It's OK to arrive multiple times, from multiple tests. fn wakeup() { crate::low_level::raise(libc::SIGUSR1).unwrap(); } #[test] fn register_with_socket() -> Result<(), Error> { let (mut read, write) = UnixStream::pair()?; register(libc::SIGUSR1, write)?; wakeup(); let mut buff = [0; 1]; read.read_exact(&mut buff)?; assert_eq!(b"X", &buff); Ok(()) } #[test] #[cfg(not(target_os = "haiku"))] fn register_dgram_socket() -> Result<(), Error> { let (read, write) = UnixDatagram::pair()?; register(libc::SIGUSR1, write)?; wakeup(); let mut buff = [0; 1]; // The attempt to detect if it is socket can generate an empty message. Therefore, do a few // retries. for _ in 0..3 { let len = read.recv(&mut buff)?; if len == 1 && &buff == b"X" { return Ok(()); } } panic!("Haven't received the right data"); } #[test] fn register_with_pipe() -> Result<(), Error> { let mut fds = [0; 2]; unsafe { assert_eq!(0, libc::pipe(fds.as_mut_ptr())) }; register_raw(libc::SIGUSR1, fds[1])?; wakeup(); let mut buff = [0; 1]; unsafe { assert_eq!(1, libc::read(fds[0], buff.as_mut_ptr() as *mut _, 1)) } assert_eq!(b"X", &buff); Ok(()) } } signal-hook-0.3.17/src/low_level/siginfo.rs000064400000000000000000000175211046102023000167200ustar 00000000000000//! 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 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, /// 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, } } } signal-hook-0.3.17/src/low_level/signal_details.rs000064400000000000000000000211771046102023000202460ustar 00000000000000//! Providing auxiliary information for signals. use std::io::Error; use std::mem; use std::ptr; use libc::{c_int, EINVAL}; #[cfg(not(windows))] use libc::{sigset_t, SIG_UNBLOCK}; use crate::consts::signal::*; use crate::low_level; #[derive(Clone, Copy, Debug)] enum DefaultKind { Ignore, #[cfg(not(windows))] Stop, Term, } struct Details { signal: c_int, name: &'static str, default_kind: DefaultKind, } macro_rules! s { ($name: expr, $kind: ident) => { Details { signal: $name, name: stringify!($name), default_kind: DefaultKind::$kind, } }; } #[cfg(not(windows))] const DETAILS: &[Details] = &[ s!(SIGABRT, Term), s!(SIGALRM, Term), s!(SIGBUS, Term), s!(SIGCHLD, Ignore), // Technically, continue the process... but this is not done *by* the process. s!(SIGCONT, Ignore), s!(SIGFPE, Term), s!(SIGHUP, Term), s!(SIGILL, Term), s!(SIGINT, Term), #[cfg(any( target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd", target_os = "openbsd", target_os = "macos" ))] s!(SIGINFO, Ignore), #[cfg(not(target_os = "haiku"))] s!(SIGIO, Ignore), // Can't override anyway, but... s!(SIGKILL, Term), s!(SIGPIPE, Term), s!(SIGPROF, Term), s!(SIGQUIT, Term), s!(SIGSEGV, Term), // Can't override anyway, but... s!(SIGSTOP, Stop), s!(SIGSYS, Term), s!(SIGTERM, Term), s!(SIGTRAP, Term), s!(SIGTSTP, Stop), s!(SIGTTIN, Stop), s!(SIGTTOU, Stop), s!(SIGURG, Ignore), s!(SIGUSR1, Term), s!(SIGUSR2, Term), s!(SIGVTALRM, Term), s!(SIGWINCH, Ignore), s!(SIGXCPU, Term), s!(SIGXFSZ, Term), ]; #[cfg(windows)] const DETAILS: &[Details] = &[ s!(SIGABRT, Term), s!(SIGFPE, Term), s!(SIGILL, Term), s!(SIGINT, Term), s!(SIGSEGV, Term), s!(SIGTERM, Term), ]; /// Provides a human-readable name of a signal. /// /// Note that the name does not have to be known (in case it is some less common, or non-standard /// signal). /// /// # Examples /// /// ``` /// # use signal_hook::low_level::signal_name; /// assert_eq!("SIGKILL", signal_name(9).unwrap()); /// assert!(signal_name(142).is_none()); /// ``` pub fn signal_name(signal: c_int) -> Option<&'static str> { DETAILS.iter().find(|d| d.signal == signal).map(|d| d.name) } #[cfg(not(windows))] fn restore_default(signal: c_int) -> Result<(), Error> { unsafe { // A C structure, supposed to be memset to 0 before use. let mut action: libc::sigaction = mem::zeroed(); #[cfg(target_os = "aix")] { action.sa_union.__su_sigaction = mem::transmute::< usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void), >(libc::SIG_DFL); } #[cfg(not(target_os = "aix"))] { action.sa_sigaction = libc::SIG_DFL as _; } if libc::sigaction(signal, &action, ptr::null_mut()) == 0 { Ok(()) } else { Err(Error::last_os_error()) } } } #[cfg(windows)] fn restore_default(signal: c_int) -> Result<(), Error> { unsafe { // SIG_DFL = 0, but not in libc :-( if libc::signal(signal, 0) == 0 { Ok(()) } else { Err(Error::last_os_error()) } } } /// Emulates the behaviour of a default handler for the provided signal. /// /// This function does its best to provide the same action as the default handler would do, without /// disrupting the rest of the handling of such signal in the application. It is also /// async-signal-safe. /// /// This function necessarily looks up the appropriate action in a table. That means it is possible /// your system has a signal that is not known to this function. In such case an error is returned /// (equivalent of `EINVAL`). /// /// See also the [`register_conditional_default`][crate::flag::register_conditional_default]. /// /// # Warning /// /// There's a short race condition in case of signals that terminate (either with or without a core /// dump). The emulation first resets the signal handler back to default (as the application is /// going to end, it's not a problem) and invokes it. But if some other thread installs a signal /// handler in the meantime (without assistance from `signal-hook`), it can happen this will be /// invoked by the re-raised signal. /// /// This function will still terminate the application (there's a fallback on `abort`), the risk is /// invoking the newly installed signal handler. Note that manipulating the low-level signals is /// always racy in a multi-threaded program, therefore the described situation is already /// discouraged. /// /// If you are uneasy about such race condition, the recommendation is to run relevant termination /// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they /// say, but slightly differ in externally observable behaviour from termination by a signal (the /// exit code will specify that the application exited, not that it terminated with a signal in the /// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be /// different). pub fn emulate_default_handler(signal: c_int) -> Result<(), Error> { #[cfg(not(windows))] { if signal == SIGSTOP || signal == SIGKILL { return low_level::raise(signal); } } let kind = DETAILS .iter() .find(|d| d.signal == signal) .map(|d| d.default_kind) .ok_or_else(|| Error::from_raw_os_error(EINVAL))?; match kind { DefaultKind::Ignore => Ok(()), #[cfg(not(windows))] DefaultKind::Stop => low_level::raise(SIGSTOP), DefaultKind::Term => { if let Ok(()) = restore_default(signal) { #[cfg(not(windows))] unsafe { #[allow(deprecated)] let mut newsigs: sigset_t = mem::zeroed(); // Some android versions don't have the sigemptyset and sigaddset. // Unfortunately, we don't have an access to the android _version_. We just // know that 64bit versions are all OK, so this is a best-effort guess. // // For the affected/guessed versions, we provide our own implementation. We // hope it to be correct (it's inspired by a libc implementation and we assume // the kernel uses the same format ‒ it's unlikely to be different both because // of compatibility and because there's really nothing to invent about a // bitarray). // // We use the proper way for other systems. #[cfg(all(target_os = "android", target_pointer_width = "32"))] unsafe fn prepare_sigset(set: *mut sigset_t, mut signal: c_int) { signal -= 1; let set_raw: *mut libc::c_ulong = set.cast(); let size = mem::size_of::(); assert_eq!(set_raw as usize % mem::align_of::(), 0); let pos = signal as usize / size; assert!(pos < mem::size_of::() / size); let bit = 1 << (signal as usize % size); set_raw.add(pos).write(bit); } #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] unsafe fn prepare_sigset(set: *mut sigset_t, signal: c_int) { libc::sigemptyset(set); libc::sigaddset(set, signal); } prepare_sigset(&mut newsigs, signal); // Ignore the result, if it doesn't work, we try anyway // Also, sigprocmask is unspecified, but available on more systems. And we want // to just enable _something_. And if it doesn't work, we'll terminate // anyway... It's not UB, so we are good. libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut()); } let _ = low_level::raise(signal); } // Fallback if anything failed or someone managed to put some other action in in // between. unsafe { libc::abort() } } } } #[cfg(test)] mod test { use super::*; #[test] fn existing() { assert_eq!("SIGTERM", signal_name(SIGTERM).unwrap()); } #[test] fn unknown() { assert!(signal_name(128).is_none()); } } signal-hook-0.3.17/tests/default.rs000064400000000000000000000011031046102023000152560ustar 00000000000000//! Check the hack of SIG_DFL for windows. //! //! Libc doesn't export SIG_DFL on windows. It seems to be 0 on all platforms, though, but just to //! make sure, we observe it is so. We try to read the previous signal on startup and it must be //! the default. extern crate libc; use libc::{sighandler_t, signal, SIGTERM}; const SIG_DFL: sighandler_t = 0; #[test] fn sig_dfl() { unsafe { let prev = signal(SIGTERM, SIG_DFL); assert_eq!(SIG_DFL, prev); } } #[cfg(not(windows))] #[test] fn sig_dfl_static() { assert_eq!(::libc::SIG_DFL, SIG_DFL); } signal-hook-0.3.17/tests/iterator.rs000064400000000000000000000151021046102023000154670ustar 00000000000000#![cfg(not(windows))] extern crate signal_hook; use std::collections::HashSet; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{self, RecvTimeoutError}; use std::sync::Arc; use std::thread::{self, JoinHandle}; use std::time::Duration; use signal_hook::consts::{SIGUSR1, SIGUSR2}; use signal_hook::iterator::{Handle, Signals}; use signal_hook::low_level::raise; use serial_test::serial; fn send_sigusr1() { raise(SIGUSR1).unwrap(); } fn send_sigusr2() { raise(SIGUSR2).unwrap(); } fn setup_without_any_signals() -> (Signals, Handle) { let signals = Signals::new(&[]).unwrap(); let controller = signals.handle(); (signals, controller) } fn setup_for_sigusr2() -> (Signals, Handle) { let signals = Signals::new(&[SIGUSR2]).unwrap(); let controller = signals.handle(); (signals, controller) } macro_rules! assert_signals { ($actual:expr, $($expected:expr),+ $(,)?) => { let actual = $actual.collect::>(); let expected = vec!($($expected),+).into_iter().collect::>(); assert_eq!(actual, expected); }; } macro_rules! assert_no_signals { ($signals:expr) => { assert_eq!($signals.next(), None); }; } #[test] #[serial] fn forever_terminates_when_closed() { let (mut signals, controller) = setup_for_sigusr2(); // Detect early terminations. let stopped = Arc::new(AtomicBool::new(false)); let stopped_bg = Arc::clone(&stopped); let thread = thread::spawn(move || { // Eat all the signals there are (might come from a concurrent test, in theory). // Would wait forever, but it should be terminated by the close below. for _sig in &mut signals {} stopped_bg.store(true, Ordering::SeqCst); }); // Wait a bit to see if the thread terminates by itself. thread::sleep(Duration::from_millis(100)); assert!(!stopped.load(Ordering::SeqCst)); controller.close(); thread.join().unwrap(); assert!(stopped.load(Ordering::SeqCst)); } // A reproducer for #16: if we had the mio-support enabled (which is enabled also by the // tokio-support feature), blocking no longer works. The .wait() would return immediately (an empty // iterator, possibly), .forever() would do a busy loop. // flag) #[test] #[serial] fn signals_block_wait() { let mut signals = Signals::new(&[SIGUSR2]).unwrap(); let (s, r) = mpsc::channel(); let finish = Arc::new(AtomicBool::new(false)); let thread_id = thread::spawn({ let finish = Arc::clone(&finish); move || { // Technically, it may spuriously return early. But it shouldn't be doing it too much, // so we just try to wait multiple times ‒ if they *all* return right away, it is // broken. for _ in 0..10 { for _ in signals.wait() { if finish.load(Ordering::SeqCst) { // Asked to terminate at the end of the thread. Do so (but without // signalling the receipt). return; } else { panic!("Someone really did send us SIGUSR2, which breaks the test"); } } } let _ = s.send(()); } }); // A RAII guard to make sure we shut down the thread even if the test fails. struct ThreadGuard { thread: Option>, finish: Arc, } impl ThreadGuard { fn shutdown(&mut self) { // Tell it to shut down self.finish.store(true, Ordering::SeqCst); // Wake it up send_sigusr2(); // Wait for it to actually terminate. if let Some(thread) = self.thread.take() { thread.join().unwrap(); // Propagate panics } } } impl Drop for ThreadGuard { fn drop(&mut self) { self.shutdown(); // OK if done twice, won't have the thread any more. } } let mut bg_thread = ThreadGuard { thread: Some(thread_id), finish, }; let err = r .recv_timeout(Duration::from_millis(100)) .expect_err("Wait didn't wait properly"); assert_eq!(err, RecvTimeoutError::Timeout); bg_thread.shutdown(); } #[test] #[serial] fn pending_doesnt_block() { let (mut signals, _) = setup_for_sigusr2(); let mut recieved_signals = signals.pending(); assert_no_signals!(recieved_signals); } #[test] #[serial] fn wait_returns_recieved_signals() { let (mut signals, _) = setup_for_sigusr2(); send_sigusr2(); let recieved_signals = signals.wait(); assert_signals!(recieved_signals, SIGUSR2); } #[test] #[serial] fn forever_returns_recieved_signals() { let (mut signals, _) = setup_for_sigusr2(); send_sigusr2(); let signal = signals.forever().take(1); assert_signals!(signal, SIGUSR2); } #[test] #[serial] fn wait_doesnt_block_when_closed() { let (mut signals, controller) = setup_for_sigusr2(); controller.close(); let mut recieved_signals = signals.wait(); assert_no_signals!(recieved_signals); } #[test] #[serial] fn wait_unblocks_when_closed() { let (mut signals, controller) = setup_without_any_signals(); let thread = thread::spawn(move || { signals.wait(); }); controller.close(); thread.join().unwrap(); } #[test] #[serial] fn forever_doesnt_block_when_closed() { let (mut signals, controller) = setup_for_sigusr2(); controller.close(); let mut signal = signals.forever(); assert_no_signals!(signal); } #[test] #[serial] fn add_signal_after_creation() { let (mut signals, _) = setup_without_any_signals(); signals.add_signal(SIGUSR1).unwrap(); send_sigusr1(); assert_signals!(signals.pending(), SIGUSR1); } #[test] #[serial] fn delayed_signal_consumed() { let (mut signals, _) = setup_for_sigusr2(); signals.add_signal(SIGUSR1).unwrap(); send_sigusr1(); let mut recieved_signals = signals.wait(); send_sigusr2(); assert_signals!(recieved_signals, SIGUSR1, SIGUSR2); // The pipe still contains the byte from the second // signal and so wait won't block but won't return // a signal. recieved_signals = signals.wait(); assert_no_signals!(recieved_signals); } #[test] #[serial] fn is_closed_initially_returns_false() { let (_, controller) = setup_for_sigusr2(); assert!(!controller.is_closed()); } #[test] #[serial] fn is_closed_returns_true_when_closed() { let (_, controller) = setup_for_sigusr2(); controller.close(); assert!(controller.is_closed()); } signal-hook-0.3.17/tests/shutdown.rs000064400000000000000000000063111046102023000155130ustar 00000000000000//! Tests for the shutdown. //! //! The tests work like this: //! //! * The register an alarm, to fail if anything takes too long (which is very much possible here). //! * A fork is done, with the child registering a signal with a NOP and cleanup operation (one or //! the other). //! * The child puts some kind of infinite loop or sleep inside itself, so it never actually //! terminates on the first, but would terminate after the signal. #![cfg(not(windows))] // Forks don't work on Windows, but windows has the same implementation. use std::io::Error; use std::ptr; use std::sync::atomic::AtomicBool; use std::sync::Arc; use std::thread; use std::time::Duration; use signal_hook::consts::signal::*; use signal_hook::flag; use signal_hook::low_level; fn do_test(child: C) { unsafe { libc::alarm(10); // Time out the test after 10 seconds and get it killed. match libc::fork() { -1 => panic!("Fork failed: {}", Error::last_os_error()), 0 => { child(); loop { thread::sleep(Duration::from_secs(1)); } } pid => { // Give the child some time to register signals and stuff // We could actually signal that the child is ready by it eg. closing STDOUT, but // this is just a test so we don't really bother. thread::sleep(Duration::from_millis(250)); libc::kill(pid, libc::SIGTERM); // Wait a small bit to make sure the signal got delivered. thread::sleep(Duration::from_millis(50)); // The child is still running, because the first signal got "handled" by being // ignored. let terminated = libc::waitpid(pid, ptr::null_mut(), libc::WNOHANG); assert_eq!(0, terminated, "Process {} terminated prematurely", pid); // But it terminates on the second attempt (we do block on wait here). libc::kill(pid, libc::SIGTERM); let terminated = libc::waitpid(pid, ptr::null_mut(), 0); assert_eq!(pid, terminated); } } } } /// Use automatic cleanup inside the signal handler to get rid of old signals, the aggressive way. #[test] fn cleanup_inside_signal() { fn hook() { // Make sure we have some signal handler, not the default. unsafe { low_level::register(SIGTERM, || ()).unwrap() }; let shutdown_cond = Arc::new(AtomicBool::new(false)); // „disarmed“ shutdown flag::register_conditional_shutdown(SIGTERM, 0, Arc::clone(&shutdown_cond)).unwrap(); // But arm at the first SIGTERM flag::register(SIGTERM, shutdown_cond).unwrap(); } do_test(hook); } /// Manually remove the signal handler just after receiving the signal but before going into an /// infinite loop. #[test] fn cleanup_after_signal() { fn hook() { let mut signals = signal_hook::iterator::Signals::new(&[libc::SIGTERM]).unwrap(); assert_eq!(Some(SIGTERM), signals.into_iter().next()); flag::register_conditional_shutdown(SIGTERM, 0, Arc::new(AtomicBool::new(true))).unwrap(); } do_test(hook); }