├── .github ├── actions-rs │ └── grcov.yml ├── codecov.yml └── workflows │ ├── audit.yaml │ ├── coverage.yaml │ └── test.yaml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── build.rs ├── ci-check.sh ├── examples └── print.rs ├── rustfmt.toml ├── signal-hook-async-std ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ └── lib.rs └── tests │ └── async_std.rs ├── signal-hook-mio ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ └── lib.rs └── tests │ ├── mio_0_6.rs │ ├── mio_0_7.rs │ ├── mio_0_8.rs │ └── mio_1_0.rs ├── signal-hook-registry ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ ├── half_lock.rs │ └── lib.rs └── tests │ └── unregister_signal.rs ├── signal-hook-tokio ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ └── lib.rs └── tests │ └── tests.rs ├── src ├── flag.rs ├── iterator │ ├── backend.rs │ ├── exfiltrator │ │ ├── mod.rs │ │ ├── origin.rs │ │ └── raw.rs │ └── mod.rs ├── lib.rs └── low_level │ ├── channel.rs │ ├── extract.c │ ├── mod.rs │ ├── pipe.rs │ ├── siginfo.rs │ └── signal_details.rs └── tests ├── default.rs ├── iterator.rs └── shutdown.rs /.github/actions-rs/grcov.yml: -------------------------------------------------------------------------------- 1 | branch: true 2 | ignore-not-existing: true 3 | llvm: true 4 | filter: covered 5 | output-type: lcov 6 | output-path: ./lcov.info 7 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | comment: 2 | layout: "diff, flags, files" 3 | require_changes: true 4 | 5 | coverage: 6 | status: 7 | project: 8 | default: 9 | # Don't fail on coverage, only show 10 | informational: true 11 | -------------------------------------------------------------------------------- /.github/workflows/audit.yaml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | schedule: 8 | - cron: '0 0 * * 0' 9 | 10 | jobs: 11 | security_audit: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions-rs/audit-check@35b7b53b1e25b55642157ac01b4adceb5b9ebef3 16 | with: 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yaml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUST_BACKTRACE: full 12 | 13 | jobs: 14 | coverage: 15 | name: Coverage 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v2 20 | 21 | - name: Install Rust 22 | uses: actions-rs/toolchain@v1 23 | with: 24 | toolchain: nightly 25 | profile: minimal 26 | default: true 27 | 28 | - name: Pre-installing grcov 29 | uses: actions-rs/install@v0.1 30 | with: 31 | crate: grcov 32 | use-tool-cache: true 33 | 34 | - name: Restore cache 35 | uses: Swatinem/rust-cache@v1 36 | 37 | - name: Run tests 38 | run: cargo test --all --all-features 39 | env: 40 | CARGO_INCREMENTAL: '0' 41 | RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' 42 | RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' 43 | # 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 :-( 44 | #CFLAGS: --coverage 45 | #CXXFLAGS: --coverage 46 | 47 | - name: Generate coverage 48 | uses: actions-rs/grcov@v0.1 49 | 50 | - name: Upload to codecov.io 51 | uses: codecov/codecov-action@5a8bb4701eca7ba3673f21664b887f652c58d0a3 52 | with: 53 | token: ${{ secrets.CODECOV_TOKEN }} 54 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUST_BACKTRACE: full 12 | 13 | jobs: 14 | test: 15 | name: Build & test 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - macos-latest 22 | - windows-latest 23 | rust: 24 | - stable 25 | - beta 26 | - nightly 27 | # Introduction of self: Arc<..>, needed for the iterator module 28 | - 1.36.0 29 | # Introduction of non_exhaustive, used at certain exfiltrators 30 | - 1.40.0 31 | 32 | runs-on: ${{ matrix.os }} 33 | 34 | steps: 35 | - name: checkout 36 | uses: actions/checkout@v2 37 | 38 | - name: Install Rust 39 | uses: actions-rs/toolchain@v1 40 | with: 41 | toolchain: ${{ matrix.rust }} 42 | default: true 43 | profile: minimal 44 | 45 | - name: Restore cache 46 | uses: Swatinem/rust-cache@v1 47 | 48 | - name: Build & test 49 | env: 50 | RUST_VERSION: ${{ matrix.rust }} 51 | OS: ${{ matrix.os }} 52 | run: ./ci-check.sh 53 | 54 | rustfmt: 55 | name: Check formatting 56 | runs-on: ubuntu-latest 57 | steps: 58 | - name: checkout 59 | uses: actions/checkout@v2 60 | 61 | - name: Install Rust 62 | uses: actions-rs/toolchain@v1 63 | with: 64 | profile: minimal 65 | toolchain: stable 66 | default: true 67 | components: rustfmt 68 | 69 | - run: cargo fmt --all -- --check 70 | 71 | links: 72 | name: Check documentation links 73 | runs-on: ubuntu-latest 74 | steps: 75 | - name: checkout 76 | uses: actions/checkout@v2 77 | 78 | - name: Install Rust 79 | uses: actions-rs/toolchain@v1 80 | with: 81 | toolchain: stable 82 | default: true 83 | 84 | - name: Restore cache 85 | uses: Swatinem/rust-cache@v1 86 | 87 | - name: Install cargo-deadlinks 88 | uses: actions-rs/install@v0.1 89 | with: 90 | crate: cargo-deadlinks 91 | use-tool-cache: true 92 | 93 | - name: Check links 94 | run: | 95 | for package in $(cargo metadata --no-deps --format-version=1 | jq -r '.packages[] | .name'); do 96 | cargo rustdoc -p "$package" --all-features -- -D warnings 97 | dname=$(echo "$package" | tr '-' '_') 98 | cargo deadlinks --dir "target/doc/$dname" --check-http --ignore-fragments 99 | done 100 | 101 | ancient-registry: 102 | name: Check compilation of signal-hook-registry on 1.26.0 103 | strategy: 104 | matrix: 105 | os: 106 | - ubuntu-latest 107 | - macos-latest 108 | - windows-latest 109 | runs-on: ${{ matrix.os }} 110 | steps: 111 | - name: checkout 112 | uses: actions/checkout@v2 113 | 114 | - name: Install Rust 115 | uses: actions-rs/toolchain@v1 116 | with: 117 | toolchain: 1.26.0 118 | default: true 119 | profile: minimal 120 | 121 | - name: Restore cache 122 | uses: Swatinem/rust-cache@v1 123 | 124 | - name: Check compilation 125 | run: | 126 | rm Cargo.toml 127 | cd signal-hook-registry 128 | sed -i -e '/signal-hook =/d' Cargo.toml 129 | cargo check 130 | 131 | ancient: 132 | name: Check compilation on 1.31.0 133 | strategy: 134 | matrix: 135 | os: 136 | - ubuntu-latest 137 | - macos-latest 138 | - windows-latest 139 | runs-on: ${{ matrix.os }} 140 | steps: 141 | - name: checkout 142 | uses: actions/checkout@v2 143 | 144 | - name: Install Rust 145 | uses: actions-rs/toolchain@v1 146 | with: 147 | toolchain: 1.31.0 148 | default: true 149 | profile: minimal 150 | 151 | - name: Restore cache 152 | uses: Swatinem/rust-cache@v1 153 | 154 | - name: Check compilation 155 | run: | 156 | rm Cargo.lock 157 | cargo update 158 | cargo check --no-default-features 159 | 160 | 161 | clippy: 162 | name: Clippy lints 163 | runs-on: ubuntu-latest 164 | steps: 165 | - name: Checkout repository 166 | uses: actions/checkout@v2 167 | 168 | - name: Install Rust 169 | uses: actions-rs/toolchain@v1 170 | with: 171 | toolchain: stable 172 | profile: minimal 173 | default: true 174 | components: clippy 175 | 176 | - name: Restore cache 177 | uses: Swatinem/rust-cache@v1 178 | 179 | - name: Run clippy linter 180 | run: cargo clippy --all --all-features --tests -- -D clippy::all -D warnings 181 | 182 | # There's bunch of platforms that have some weird quirks (or we don't know 183 | # that they don't). While fully compiling and testing on them is a bit of a 184 | # challenge, running cargo check on them should be easy enough and should 185 | # catch at least some problems (like different sizes of types). 186 | weird_platforms: 187 | name: Check weird platforms 188 | strategy: 189 | fail-fast: false 190 | matrix: 191 | target: 192 | - x86_64-unknown-linux-musl 193 | - x86_64-pc-solaris 194 | - x86_64-linux-android 195 | - aarch64-linux-android 196 | - arm-linux-androideabi 197 | - mips-unknown-linux-musl 198 | - x86_64-unknown-netbsd 199 | - x86_64-unknown-freebsd 200 | extra-args: 201 | - "--all --all-features" 202 | include: 203 | # - wasm32-wasi (not yet? Or not ever?) 204 | # - x86_64-unknown-redox (Is that platform even usable on stable?) 205 | - { target: x86_64-fuchsia, extra-args: "--all-features" } 206 | - { target: asmjs-unknown-emscripten, extra-args: "--all-features" } 207 | # Seems we have some trouble with properly running C compiler on these. Might as well be problem with cross-compilation setup, but who knows 208 | # So we avoid the exfiltrator part/signal-hook-sys :-( 209 | - { target: x86_64-apple-darwin, extra-args: "--features=iterator --all --exclude signal-hook-sys" } 210 | - { target: x86_64-apple-ios, extra-args: "--features=iterator --all --exclude signal-hook-sys" } 211 | 212 | runs-on: ubuntu-latest 213 | steps: 214 | - name: checkout 215 | uses: actions/checkout@v2 216 | 217 | - name: Install Rust 218 | uses: actions-rs/toolchain@v1 219 | with: 220 | toolchain: stable 221 | profile: minimal 222 | default: true 223 | target: ${{ matrix.target }} 224 | 225 | - name: Restore cache 226 | uses: Swatinem/rust-cache@v1 227 | 228 | - name: Run the check 229 | uses: actions-rs/cargo@v1 230 | with: 231 | command: check 232 | args: ${{ matrix.extra-args }} --tests --target=${{ matrix.target }} 233 | use-cross: true 234 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | tags 4 | .ccls-cache 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # signal-hook-1.3.18 2 | 3 | * Release the special-case removal of AIX for top-level signal-hook too (#169, 4 | #176). 5 | 6 | # signal-hook-async-std-0.3.0 7 | 8 | * Bump async-std to 0.2 (#172). 9 | 10 | # signal-hook-registry-1.4.5 11 | 12 | * Fix windows build (#174). 13 | 14 | # signal-hook-registry-1.4.4 15 | 16 | * Get rid of a warning. 17 | 18 | # signal-hook-registry-1.4.3 19 | 20 | * Remove special-case for AIX (#169). 21 | 22 | # signal-hook-mio-0.2.4 23 | 24 | * Support for mio 1.0 25 | 26 | # signal-hook-registry-1.4.2 27 | 28 | * NQX support (experimental/not guaranteed to work) (#158) 29 | - By disabling `SA_RESTART` on that platform, not supported there. 30 | 31 | # 0.3.17 32 | 33 | * Fix race condition leading into a panic in SignalsInfo::forever (#148). 34 | 35 | # 0.3.16 36 | 37 | * Fix compilation on OpenBSD (#147). 38 | 39 | # 0.3.15 40 | # signal-hook-registry-1.4.1 41 | 42 | * AIX support (experimental/not guaranteed to work). 43 | 44 | # 0.3.14 45 | 46 | * Added the SIGINFO signal (where available). 47 | 48 | # signal-hook-mio-0.2.3 49 | 50 | * Support for mio 0.8 51 | 52 | # signal-hook-async-std-0.2.2 53 | # signal-hook-tokio-0.3.1 54 | 55 | * Fix support for SignalsInfo with non-default info extractors. 56 | 57 | # 0.3.13 58 | 59 | * Add haiku support. 60 | 61 | # 0.3.12 62 | 63 | * Fix accidentally broken windows build. 64 | 65 | # 0.3.11 66 | 67 | * Provide fallback sigaddset, sigemptyset on certain androids, as they are 68 | missing them. 69 | 70 | # 0.3.10 71 | 72 | * Doc link fixes. 73 | 74 | # 0.3.9 75 | 76 | * Deliver SIGCHLD even on stop/continue. 77 | 78 | # 0.3.8 79 | 80 | * Fix docs.rs build. 81 | 82 | # 0.3.7 83 | 84 | * Unmask a signal in default emulation if it is termination. 85 | 86 | # mio-0.2.2 87 | 88 | * The same fix, but for the 0.6 support 😇. 89 | 90 | # mio-0.2.1 91 | 92 | * Fix example: handle ErrorKind::Interrupted inside poll. It's very likely to 93 | happen, when we are waiting for signals there. 94 | 95 | # 0.3.6 96 | 97 | * Fix the labels on docs.rs :-|. 98 | 99 | # 0.3.5 100 | 101 | * Doc: include the features & these little labels inside docs. 102 | 103 | # signal-hook-async-std-0.2.1 104 | 105 | * Dependency updates ‒ no longer depends on the whole async-std, but only on 106 | some smaller dependencies of it (`async-io`, `futures-lite`). This might make 107 | it work even outside of async-std context. 108 | 109 | # signal-hook-tokio-0.3.0 110 | 111 | * Support for tokio 1.0. 112 | 113 | # 0.3.4 114 | 115 | * Fix feature dependencies (`iterator` depends on `channel`). 116 | 117 | # 0.3.3 118 | 119 | * `low_level::emulate_default_handler` to emulate whatever default handler would 120 | do. 121 | * `low_level::signal_name` to look up human readable name. 122 | * The `Origin`'s debug output now contains the human readable name of the 123 | signal. 124 | 125 | # 0.3.2 126 | 127 | * Allow extracting Origin from the raw `siginfo_t` structure by hand, without 128 | needing an iterator. 129 | * Folding the signal-hook-sys inline (but still compiling C code only 130 | conditionally). 131 | * `WithRawSiginfo` extractor (to get hands on the raw `siginfo_t`). 132 | * Bugfix: Don't leak on WithOrigin destruction. 133 | 134 | # 0.3.1 135 | 136 | * Use caret dependencies where appropriate (to allow upgrades on 137 | signal-hook-registry). 138 | 139 | # async-std-0.2.0 140 | 141 | * No longer depends on `futures`. 142 | 143 | # 0.3.0 144 | 145 | * The `cleanup` module is gone, it was not a good API. Replaced by conditional 146 | termination in `flag`. 147 | * Some abstractions/patterns are moved to `low_level` submodule, as they are 148 | considered building blocks, not for direct use (`register`, `pipe`, 149 | `channel`). 150 | * The signal constants are moved to a submodule (`consts`), together with few 151 | more constants, to not clutter the root. 152 | * The forever iterator no longer consumes. 153 | 154 | # registry-1.3.0 155 | 156 | * The `unregister_signal` in is deprecated, without a replacement. 157 | 158 | # 0.2.2 159 | 160 | * Extractor for the origin of a signal (PID, UID, what caused it). 161 | * Fixing some doc links on re-exports. 162 | 163 | # 0.2.1 164 | 165 | * Allow turning the iterator module off (the `iterator` feature, part of default 166 | features). This would allow compiling the crate on 1.31.0. 167 | 168 | # 0.2.0 169 | 170 | * Bump minimal rustc version to 1.36.0 (signal-hook-registry still builds with 171 | 1.26.0). 172 | * (Breaking) Support for exfiltrators ‒ ability to return more than just the 173 | signal number from the iterator and streams. Nothing more is implemented yet, 174 | but the place to put it is reserved in the API. 175 | * (Breaking) `pipe::register_raw` now takes ownership and tries to use send 176 | first, falls back to `O_NONBLOCK` and `write` on failure. 177 | * (Breaking) All async support is pulled out into separate crates, to decouple 178 | from the async runtime release cycles on the main `signal-hook` crate. 179 | * Inner parts of the `Iterator` are now exposed in 180 | `signal_hook::iterator::backend`, to support the async crates. 181 | 182 | # registry-1.2.2 183 | 184 | * Drop dependency on arc-swap (only very small subset used and arc-swap would 185 | like to drop that part anyway). 186 | 187 | # registry-1.2.1 188 | 189 | * Abort instead of panicking if the OS gives us NULL as siginfo (which is 190 | illegal). Panicking would be UB. 191 | 192 | # 0.1.16 193 | 194 | * Fix possible blocking in signal handler registered by `Signals`. 195 | 196 | # 0.1.15 197 | 198 | * Make `Signals` work in edge-triggered mode in mio too, by always draining 199 | everything from the socket. Needed, because mio 0.7 doesn't have 200 | level-triggered any more. 201 | 202 | # 0.1.14 203 | 204 | * `mio-0_7-support` feature for use with mio 0.7.0+. 205 | * Bump minimal rustc version to 1.31.0 (signal-hook-registry can still build 206 | with 1.26.0). 207 | 208 | # 0.1.13 209 | 210 | * Some doc clarifications. 211 | 212 | # 0.1.12 213 | 214 | * `cleanup` module to register resetting signals to default. 215 | 216 | # registry-1.2.0 217 | 218 | * `unregister_signal`, to remove all hooks of one signal. 219 | 220 | # 0.1.11 221 | 222 | * Docs improvements. 223 | * Fix registering pipes as well as sockets into the pipe module (#27). 224 | 225 | # registry-1.1.1 226 | 227 | * Update deps. 228 | 229 | # registry-1.1.0 230 | 231 | * Adding Windows support (thanks to @qnighy). 232 | 233 | # 0.1.10 234 | 235 | * Fix busy loop in Iterator::forever when the mio-support feature is enabled 236 | (#16). 237 | 238 | # registry-1.0.1 239 | 240 | * Include the registry files in the crates.io tarball. 241 | 242 | # 0.1.9 243 | # registry-1.0.0 244 | 245 | * Split into backend signal-hook-registry and the frontend. The backend is much 246 | less likely to have breaking changes so it contains the things that can be in 247 | the application just once. 248 | 249 | # 0.1.8 250 | 251 | * The `Signals` iterator can now be closed (from another instance or thread), 252 | which can be used to shut down the thread handling signals from the main 253 | thread. 254 | 255 | # 0.1.7 256 | 257 | * The `Signals` iterator allows adding signals after creation. 258 | * Fixed a bug where `Signals` registrations could be unregirestered too soon if 259 | the `Signals` was cloned previously. 260 | 261 | # 0.1.6 262 | 263 | * The internally used ArcSwap thing doesn't block other ArcSwaps now (has 264 | independent generation lock). 265 | 266 | # 0.1.5 267 | 268 | * Re-exported signal constants, so users no longer need libc. 269 | 270 | # 0.1.4 271 | 272 | * Compilation fix for android-aarch64 273 | 274 | # 0.1.3 275 | 276 | * Tokio support. 277 | * Mio support. 278 | * Dependency updates. 279 | 280 | # 0.1.2 281 | 282 | * Dependency updates. 283 | 284 | # 0.1.1 285 | 286 | * Get rid of `catch_unwind` inside the signal handler. 287 | * Link to the nix crate. 288 | 289 | # 0.1.0 290 | 291 | * Initial basic implementation. 292 | * Flag helpers. 293 | * Pipe helpers. 294 | * High-level iterator helper. 295 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-hook" 3 | version = "0.3.18" 4 | authors = [ 5 | "Michal 'vorner' Vaner ", 6 | "Thomas Himmelstoss ", 7 | ] 8 | description = "Unix signal handling" 9 | documentation = "https://docs.rs/signal-hook" 10 | repository = "https://github.com/vorner/signal-hook" 11 | readme = "README.md" 12 | keywords = ["signal", "unix", "daemon"] 13 | license = "Apache-2.0/MIT" 14 | edition = "2018" 15 | 16 | [badges] 17 | maintenance = { status = "actively-developed" } 18 | 19 | [features] 20 | channel = [] 21 | default = ["channel", "iterator"] 22 | iterator = ["channel"] 23 | # TODO: Unify them on the next breaking release. 24 | extended-siginfo = ["channel", "iterator", "extended-siginfo-raw"] 25 | extended-siginfo-raw = ["cc"] 26 | 27 | [workspace] 28 | members = [ 29 | "./", 30 | "signal-hook-registry", 31 | "signal-hook-tokio", 32 | "signal-hook-mio", 33 | "signal-hook-async-std", 34 | ] 35 | 36 | [dependencies] 37 | libc = "^0.2" 38 | signal-hook-registry = { version = "^1.4", path = "signal-hook-registry" } 39 | 40 | [dev-dependencies] 41 | serial_test = "^0.7" 42 | 43 | [package.metadata.docs.rs] 44 | all-features = true 45 | rustdoc-args = ["--cfg", "docsrs"] 46 | 47 | [build-dependencies] 48 | cc = { version = "^1", optional = true } 49 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 tokio-jsonrpc developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Signal-hook 2 | 3 | [![Actions Status](https://github.com/vorner/signal-hook/workflows/test/badge.svg)](https://github.com/vorner/signal-hook/actions) 4 | [![codecov](https://codecov.io/gh/vorner/signal-hook/branch/master/graph/badge.svg?token=3KA3R2D9fV)](https://codecov.io/gh/vorner/signal-hook) 5 | [![docs](https://docs.rs/signal-hook/badge.svg)](https://docs.rs/signal-hook) 6 | 7 | Library for safe and correct Unix signal handling in Rust. 8 | 9 | Unix signals are inherently hard to handle correctly, for several reasons: 10 | 11 | * They are a global resource. If a library wants to set its own signal handlers, 12 | it risks disturbing some other library. It is possible to chain the previous 13 | signal handler, but then it is impossible to remove the old signal handlers 14 | from the chains in any practical manner. 15 | * They can be called from whatever thread, requiring synchronization. Also, as 16 | they can interrupt a thread at any time, making most handling race-prone. 17 | * According to the POSIX standard, the set of functions one may call inside a 18 | signal handler is limited to very few of them. To highlight, mutexes (or other 19 | locking mechanisms) and memory allocation and deallocation are *not* allowed. 20 | 21 | This library aims to solve some of the problems. It provides a global registry 22 | of actions performed on arrival of signals. It is possible to register multiple 23 | actions for the same signal and it is possible to remove the actions later on. 24 | If there was a previous signal handler when the first action for a signal is 25 | registered, it is chained (but the original one can't be removed). 26 | 27 | Besides the basic registration of an arbitrary action, several helper actions 28 | are provided to cover the needs of the most common use cases, available from 29 | safe Rust. 30 | 31 | For further details, see the [documentation](https://docs.rs/signal-hook). 32 | 33 | ## Example 34 | 35 | (This likely does a lot more than you need in each individual application, it's 36 | more of a show-case of what everything is possible, not of what you need to do 37 | each time). 38 | 39 | ```rust 40 | use std::io::Error; 41 | use std::sync::Arc; 42 | use std::sync::atomic::{AtomicBool, Ordering}; 43 | 44 | fn main() -> Result<(), Error> { 45 | let term = Arc::new(AtomicBool::new(false)); 46 | signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term))?; 47 | while !term.load(Ordering::Relaxed) { 48 | // Do some time-limited stuff here 49 | // (if this could block forever, then there's no guarantee the signal will have any 50 | // effect). 51 | } 52 | Ok(()) 53 | } 54 | ``` 55 | 56 | ## License 57 | 58 | Licensed under either of 59 | 60 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 61 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 62 | 63 | at your option. 64 | 65 | ### Contribution 66 | 67 | Unless you explicitly state otherwise, any contribution intentionally 68 | submitted for inclusion in the work by you, as defined in the Apache-2.0 69 | license, shall be dual licensed as above, without any additional terms 70 | or conditions. 71 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "extended-siginfo-raw")] 2 | fn main() { 3 | cc::Build::new() 4 | .file("src/low_level/extract.c") 5 | .compile("extract"); 6 | } 7 | 8 | #[cfg(not(feature = "extended-siginfo-raw"))] 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /ci-check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # We try to support some older versions of rustc. However, the support is 4 | # tiered a bit. Our dev-dependencies do *not* guarantee that old minimal 5 | # version. So we don't do tests on the older ones. Also, the 6 | # signal-hook-registry supports older rustc than we signal-hook. 7 | 8 | set -ex 9 | 10 | rm -f Cargo.lock 11 | cargo build --all --exclude signal-hook-async-std --exclude signal-hook-tokio 12 | 13 | if [ "$RUST_VERSION" = 1.36.0 ] ; then 14 | exit 15 | fi 16 | 17 | if [ "$OS" = "windows-latest" ] || [ "$RUST_VERSION" = 1.40.0 ]; then 18 | # The async support crates rely on the iterator module 19 | # which isn't available for windows. So exclude them 20 | # from the build. 21 | 22 | # Also, some dependencies of the runtimes don't like 1.40. 23 | EXCLUDE_FROM_BUILD="--exclude signal-hook-mio --exclude signal-hook-tokio --exclude signal-hook-async-std" 24 | else 25 | EXCLUDE_FROM_BUILD="" 26 | fi 27 | 28 | export RUSTFLAGS="-D warnings" 29 | 30 | cargo test --all --all-features $EXCLUDE_FROM_BUILD 31 | cargo test --all $EXCLUDE_FROM_BUILD 32 | -------------------------------------------------------------------------------- /examples/print.rs: -------------------------------------------------------------------------------- 1 | use libc::c_int; 2 | use signal_hook::consts::signal::*; 3 | use signal_hook::low_level; 4 | 5 | use std::io::Error; 6 | 7 | #[cfg(feature = "extended-siginfo")] 8 | type Signals = 9 | signal_hook::iterator::SignalsInfo; 10 | 11 | #[cfg(not(feature = "extended-siginfo"))] 12 | use signal_hook::iterator::Signals; 13 | 14 | fn main() -> Result<(), Error> { 15 | const SIGNALS: &[c_int] = &[ 16 | SIGTERM, SIGQUIT, SIGINT, SIGTSTP, SIGWINCH, SIGHUP, SIGCHLD, SIGCONT, 17 | ]; 18 | let mut sigs = Signals::new(SIGNALS)?; 19 | for signal in &mut sigs { 20 | eprintln!("Received signal {:?}", signal); 21 | #[cfg(feature = "extended-siginfo")] 22 | let signal = signal.signal; 23 | // After printing it, do whatever the signal was supposed to do in the first place 24 | low_level::emulate_default_handler(signal)?; 25 | } 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vorner/signal-hook/1ff235c7398e5251a7289f99a71c2a6f315eaadd/rustfmt.toml -------------------------------------------------------------------------------- /signal-hook-async-std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-hook-async-std" 3 | version = "0.3.0" 4 | authors = [ 5 | "Michal 'vorner' Vaner ", 6 | "Thomas Himmelstoss ", 7 | ] 8 | 9 | description = "async-std support for signal-hook" 10 | documentation = "https://docs.rs/signal-hook-async-std" 11 | readme = "README.md" 12 | repository = "https://github.com/vorner/signal-hook" 13 | keywords = ["signal", "unix", "async-std"] 14 | license = "Apache-2.0/MIT" 15 | 16 | edition = "2018" 17 | 18 | [badges] 19 | travis-ci = { repository = "vorner/signal-hook" } 20 | maintenance = { status = "actively-developed" } 21 | 22 | [dependencies] 23 | libc = "~0.2" 24 | async-io = "~2" 25 | futures-lite = "~2" 26 | signal-hook = { version = "~0.3", path = ".." } 27 | 28 | [dev-dependencies] 29 | async-std = { version = "~1", features = ["attributes"] } 30 | serial_test = "~0.5" 31 | 32 | [package.metadata.docs.rs] 33 | all-features = true 34 | rustdoc-args = ["--cfg", "docsrs"] 35 | -------------------------------------------------------------------------------- /signal-hook-async-std/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /signal-hook-async-std/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /signal-hook-async-std/README.md: -------------------------------------------------------------------------------- 1 | # Signal-hook-async-std 2 | 3 | [![Travis Build Status](https://api.travis-ci.org/vorner/signal-hook.svg?branch=master)](https://travis-ci.org/vorner/signal-hook) 4 | 5 | This is a async-std adapter crate for the 6 | [signal-hook](https://crates.io/crates/signal-hook) crate. See the 7 | [documentation](https://docs.rs/signal-hook-async-std) for further details. 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 15 | 16 | at your option. 17 | 18 | ### Contribution 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual licensed as above, without any additional terms 23 | or conditions. 24 | -------------------------------------------------------------------------------- /signal-hook-async-std/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(test(attr(deny(warnings))))] 2 | #![warn(missing_docs)] 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | 5 | //! A module for integrating signal handling with the async-std runtime. 6 | //! 7 | //! This provides the [`Signals`] struct which acts as a 8 | //! [`Stream`] of signals. 9 | //! 10 | //! # Example 11 | //! 12 | //! ```rust 13 | //! use std::io::Error; 14 | //! 15 | //! use async_std::prelude::*; 16 | //! 17 | //! use signal_hook::consts::signal::*; 18 | //! use signal_hook_async_std::Signals; 19 | //! 20 | //! async fn handle_signals(mut signals: Signals) { 21 | //! while let Some(signal) = signals.next().await { 22 | //! match signal { 23 | //! SIGHUP => { 24 | //! // Reload configuration 25 | //! // Reopen the log file 26 | //! } 27 | //! SIGTERM | SIGINT | SIGQUIT => { 28 | //! // Shutdown the system; 29 | //! }, 30 | //! _ => unreachable!(), 31 | //! } 32 | //! } 33 | //! } 34 | //! 35 | //! #[async_std::main] 36 | //! async fn main() -> Result<(), Error> { 37 | //! let signals = Signals::new(&[SIGHUP, SIGTERM, SIGINT, SIGQUIT])?; 38 | //! let handle = signals.handle(); 39 | //! 40 | //! let signals_task = async_std::task::spawn(handle_signals(signals)); 41 | //! 42 | //! // Execute your main program logic 43 | //! 44 | //! // Terminate the signal stream. 45 | //! handle.close(); 46 | //! signals_task.await; 47 | //! 48 | //! Ok(()) 49 | //! } 50 | //! ``` 51 | 52 | use std::borrow::Borrow; 53 | use std::io::Error; 54 | use std::os::unix::net::UnixStream; 55 | use std::pin::Pin; 56 | use std::task::{Context, Poll}; 57 | 58 | use libc::c_int; 59 | 60 | pub use signal_hook::iterator::backend::Handle; 61 | use signal_hook::iterator::backend::{OwningSignalIterator, PollResult, SignalDelivery}; 62 | use signal_hook::iterator::exfiltrator::{Exfiltrator, SignalOnly}; 63 | 64 | use async_io::Async; 65 | use futures_lite::io::AsyncRead; 66 | use futures_lite::stream::Stream; 67 | 68 | /// An asynchronous [`Stream`] of arriving signals. 69 | /// 70 | /// The stream doesn't return the signals in the order they were recieved by 71 | /// the process and may merge signals received multiple times. 72 | pub struct SignalsInfo(OwningSignalIterator, E>); 73 | 74 | impl SignalsInfo { 75 | /// Create a `Signals` instance. 76 | /// 77 | /// This registers all the signals listed. The same restrictions (panics, errors) apply 78 | /// as with [`Handle::add_signal`]. 79 | pub fn new(signals: I) -> Result 80 | where 81 | I: IntoIterator, 82 | S: Borrow, 83 | E: Default, 84 | { 85 | Self::with_exfiltrator(signals, E::default()) 86 | } 87 | 88 | /// A constructor with explicit exfiltrator. 89 | pub fn with_exfiltrator(signals: I, exfiltrator: E) -> Result 90 | where 91 | I: IntoIterator, 92 | S: Borrow, 93 | { 94 | let (read, write) = Async::::pair()?; 95 | let inner = SignalDelivery::with_pipe(read, write, exfiltrator, signals)?; 96 | Ok(Self(OwningSignalIterator::new(inner))) 97 | } 98 | 99 | /// Get a shareable [`Handle`] for this `Signals` instance. 100 | /// 101 | /// This can be used to add further signals or close the [`Signals`] instance 102 | /// which terminates the whole signal stream. 103 | pub fn handle(&self) -> Handle { 104 | self.0.handle() 105 | } 106 | } 107 | 108 | impl SignalsInfo { 109 | fn has_signals(read: &mut Async, ctx: &mut Context<'_>) -> Result { 110 | match Pin::new(read).poll_read(ctx, &mut [0u8]) { 111 | Poll::Pending => Ok(false), 112 | Poll::Ready(Ok(num_read)) => Ok(num_read > 0), 113 | Poll::Ready(Err(error)) => Err(error), 114 | } 115 | } 116 | } 117 | 118 | impl Stream for SignalsInfo { 119 | type Item = E::Output; 120 | 121 | fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { 122 | match self.0.poll_signal(&mut |read| Self::has_signals(read, ctx)) { 123 | PollResult::Signal(sig) => Poll::Ready(Some(sig)), 124 | PollResult::Closed => Poll::Ready(None), 125 | PollResult::Pending => Poll::Pending, 126 | PollResult::Err(error) => panic!("Unexpected error: {}", error), 127 | } 128 | } 129 | } 130 | 131 | /// Simplified version of the signals stream. 132 | /// 133 | /// This one simply returns the signal numbers, while [`SignalsInfo`] can provide additional 134 | /// information. 135 | pub type Signals = SignalsInfo; 136 | -------------------------------------------------------------------------------- /signal-hook-async-std/tests/async_std.rs: -------------------------------------------------------------------------------- 1 | use async_std::stream::StreamExt; 2 | 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::sync::Arc; 5 | use std::time::Duration; 6 | 7 | use signal_hook::consts::SIGUSR1; 8 | use signal_hook::low_level::raise; 9 | use signal_hook_async_std::Signals; 10 | 11 | use serial_test::serial; 12 | 13 | #[async_std::test] 14 | #[serial] 15 | async fn next_returns_recieved_signal() { 16 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 17 | raise(SIGUSR1).unwrap(); 18 | 19 | let signal = signals.next().await; 20 | 21 | assert_eq!(signal, Some(SIGUSR1)); 22 | } 23 | 24 | #[async_std::test] 25 | #[serial] 26 | async fn close_signal_stream() { 27 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 28 | signals.handle().close(); 29 | 30 | let result = signals.next().await; 31 | 32 | assert_eq!(result, None); 33 | } 34 | 35 | #[async_std::test] 36 | #[serial] 37 | async fn delayed() { 38 | async fn get_signal(mut signals: Signals, recieved: Arc) { 39 | signals.next().await; 40 | recieved.store(true, Ordering::SeqCst); 41 | } 42 | 43 | let signals = Signals::new(&[SIGUSR1]).unwrap(); 44 | let recieved = Arc::new(AtomicBool::new(false)); 45 | 46 | let signals_task = async_std::task::spawn(get_signal(signals, Arc::clone(&recieved))); 47 | 48 | async_std::task::sleep(Duration::from_millis(100)).await; 49 | assert!(!recieved.load(Ordering::SeqCst)); 50 | 51 | raise(SIGUSR1).unwrap(); 52 | signals_task.await; 53 | assert!(recieved.load(Ordering::SeqCst)); 54 | } 55 | -------------------------------------------------------------------------------- /signal-hook-mio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-hook-mio" 3 | version = "0.2.4" 4 | authors = [ 5 | "Michal 'vorner' Vaner ", 6 | "Thomas Himmelstoss ", 7 | ] 8 | 9 | description = "MIO support for signal-hook" 10 | documentation = "https://docs.rs/signal-hook-mio" 11 | readme = "README.md" 12 | repository = "https://github.com/vorner/signal-hook" 13 | keywords = ["signal", "unix", "mio"] 14 | license = "Apache-2.0/MIT" 15 | 16 | edition = "2018" 17 | 18 | [badges] 19 | travis-ci = { repository = "vorner/signal-hook" } 20 | maintenance = { status = "actively-developed" } 21 | 22 | [features] 23 | support-v0_6 = ["mio-0_6", "mio-uds"] 24 | support-v0_7 = ["mio-0_7"] 25 | support-v0_8 = ["mio-0_8"] 26 | support-v1_0 = ["mio-1_0"] 27 | 28 | [dependencies] 29 | libc = "~0.2" 30 | signal-hook = { version = "~0.3", path = ".." } 31 | mio-1_0 = { package = "mio", version = "~1.0", features = ["net", "os-ext"], optional = true } 32 | mio-0_8 = { package = "mio", version = "~0.8", features = ["net", "os-ext"], optional = true } 33 | mio-0_7 = { package = "mio", version = "~0.7", features = ["os-util", "uds"], optional = true } 34 | mio-0_6 = { package = "mio", version = "~0.6", optional = true } 35 | mio-uds = { version = "~0.6", optional = true} 36 | 37 | [dev-dependencies] 38 | mio-0_7 = { package = "mio", version = "~0.7", features = ["os-util", "os-poll", "uds"] } 39 | serial_test = "~0.5" 40 | 41 | [package.metadata.docs.rs] 42 | all-features = true 43 | rustdoc-args = ["--cfg", "docsrs"] 44 | -------------------------------------------------------------------------------- /signal-hook-mio/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /signal-hook-mio/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /signal-hook-mio/README.md: -------------------------------------------------------------------------------- 1 | # signal-hook-mio 2 | 3 | [![Travis Build Status](https://api.travis-ci.org/vorner/signal-hook.svg?branch=master)](https://travis-ci.org/vorner/signal-hook) 4 | 5 | This is a mio adapter crate for the 6 | [signal-hook](https://crates.io/crates/signal-hook) crate. See the 7 | [documentation](https://docs.rs/signal-hook-mio) for further details. 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 15 | 16 | at your option. 17 | 18 | ### Contribution 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual licensed as above, without any additional terms 23 | or conditions. 24 | -------------------------------------------------------------------------------- /signal-hook-mio/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(test(attr(deny(warnings))))] 2 | #![warn(missing_docs)] 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | 5 | //! A crate offering integration with the MIO runtime. 6 | //! 7 | //! There are different sub modules for supporting different MIO 8 | //! versions. The support for a version must be activated by a 9 | //! feature flag: 10 | //! 11 | //! * `support-v0_6` for sub module [`v0_6`] 12 | //! * `support-v0_7` for sub module [`v0_7`] 13 | //! * `support-v0_8` for sub module [`v0_8`] 14 | //! * `support-v1_0` for sub module [`v1_0`] 15 | //! 16 | //! See the specific sub modules for usage examples. 17 | 18 | #[cfg(any( 19 | feature = "support-v0_6", 20 | feature = "support-v0_7", 21 | feature = "support-v0_8", 22 | feature = "support-v1_0" 23 | ))] 24 | macro_rules! implement_signals_with_pipe { 25 | ($pipe:path) => { 26 | use std::borrow::Borrow; 27 | use std::io::Error; 28 | 29 | use signal_hook::iterator::backend::{self, SignalDelivery}; 30 | use signal_hook::iterator::exfiltrator::{Exfiltrator, SignalOnly}; 31 | 32 | use $pipe as Pipe; 33 | 34 | use libc::c_int; 35 | 36 | /// A struct which mimics [`signal_hook::iterator::SignalsInfo`] 37 | /// but also allows usage together with MIO runtime. 38 | pub struct SignalsInfo(SignalDelivery); 39 | 40 | pub use backend::Pending; 41 | 42 | impl SignalsInfo { 43 | /// Create a `Signals` instance. 44 | /// 45 | /// This registers all the signals listed. The same restrictions (panics, errors) apply 46 | /// as with [`Handle::add_signal`][backend::Handle::add_signal]. 47 | pub fn new(signals: I) -> Result 48 | where 49 | I: IntoIterator, 50 | S: Borrow, 51 | E: Default, 52 | { 53 | Self::with_exfiltrator(signals, E::default()) 54 | } 55 | 56 | /// A constructor with specifying an exfiltrator to pass information out of the signal 57 | /// handlers. 58 | pub fn with_exfiltrator(signals: I, exfiltrator: E) -> Result 59 | where 60 | I: IntoIterator, 61 | S: Borrow, 62 | { 63 | let (read, write) = Pipe::pair()?; 64 | let delivery = SignalDelivery::with_pipe(read, write, exfiltrator, signals)?; 65 | Ok(Self(delivery)) 66 | } 67 | 68 | /// Registers another signal to the set watched by this [`Signals`] instance. 69 | /// 70 | /// The same restrictions (panics, errors) apply as with 71 | /// [`Handle::add_signal`][backend::Handle::add_signal]. 72 | pub fn add_signal(&self, signal: c_int) -> Result<(), Error> { 73 | self.0.handle().add_signal(signal) 74 | } 75 | 76 | /// Returns an iterator of already received signals. 77 | /// 78 | /// This returns an iterator over all the signal numbers of the signals received since last 79 | /// time they were read (out of the set registered by this `Signals` instance). Note that they 80 | /// are returned in arbitrary order and a signal number is returned only once even if it was 81 | /// received multiple times. 82 | /// 83 | /// This method returns immediately (does not block) and may produce an empty iterator if there 84 | /// are no signals ready. So you should register an instance of this struct at an instance of 85 | /// [`mio::Poll`] to query for readability of the underlying self pipe. 86 | pub fn pending(&mut self) -> Pending { 87 | self.0.pending() 88 | } 89 | } 90 | 91 | /// A simplified signal iterator. 92 | /// 93 | /// This is the [`SignalsInfo`], but returning only the signal numbers. This is likely the 94 | /// one you want to use. 95 | pub type Signals = SignalsInfo; 96 | }; 97 | } 98 | 99 | /// A module for integrating signal handling with the MIO 1.0 runtime. 100 | /// 101 | /// This provides the [`Signals`][v1_0::Signals] struct as an abstraction 102 | /// which can be used with [`mio::Poll`][mio_1_0::Poll]. 103 | /// 104 | /// # Examples 105 | /// 106 | /// ```rust 107 | /// # use mio_1_0 as mio; 108 | /// use std::io::{Error, ErrorKind}; 109 | /// 110 | /// use signal_hook::consts::signal::*; 111 | /// use signal_hook_mio::v1_0::Signals; 112 | /// 113 | /// use mio::{Events, Poll, Interest, Token}; 114 | /// 115 | /// fn main() -> Result<(), Error> { 116 | /// let mut poll = Poll::new()?; 117 | /// 118 | /// let mut signals = Signals::new(&[ 119 | /// SIGTERM, 120 | /// # SIGUSR1, 121 | /// ])?; 122 | /// 123 | /// let signal_token = Token(0); 124 | /// 125 | /// poll.registry().register(&mut signals, signal_token, Interest::READABLE)?; 126 | /// # signal_hook::low_level::raise(SIGUSR1).unwrap(); // Just for terminating the example 127 | /// 128 | /// let mut events = Events::with_capacity(10); 129 | /// 'outer: loop { 130 | /// poll.poll(&mut events, None) 131 | /// .or_else(|e| if e.kind() == ErrorKind::Interrupted { 132 | /// // We get interrupt when a signal happens inside poll. That's non-fatal, just 133 | /// // retry. 134 | /// events.clear(); 135 | /// Ok(()) 136 | /// } else { 137 | /// Err(e) 138 | /// })?; 139 | /// for event in events.iter() { 140 | /// match event.token() { 141 | /// Token(0) => { 142 | /// for signal in signals.pending() { 143 | /// match signal { 144 | /// SIGTERM => break 'outer, 145 | /// # SIGUSR1 => return Ok(()), 146 | /// _ => unreachable!(), 147 | /// } 148 | /// } 149 | /// }, 150 | /// _ => unreachable!("Register other sources and match for their tokens here"), 151 | /// } 152 | /// } 153 | /// } 154 | /// 155 | /// Ok(()) 156 | /// } 157 | /// ``` 158 | #[cfg(feature = "support-v1_0")] 159 | pub mod v1_0 { 160 | use mio::event::Source; 161 | use mio::{Interest, Registry, Token}; 162 | use mio_1_0 as mio; 163 | 164 | implement_signals_with_pipe!(mio::net::UnixStream); 165 | 166 | impl Source for Signals { 167 | fn register( 168 | &mut self, 169 | registry: &Registry, 170 | token: Token, 171 | interest: Interest, 172 | ) -> Result<(), Error> { 173 | self.0.get_read_mut().register(registry, token, interest) 174 | } 175 | 176 | fn reregister( 177 | &mut self, 178 | registry: &Registry, 179 | token: Token, 180 | interest: Interest, 181 | ) -> Result<(), Error> { 182 | self.0.get_read_mut().reregister(registry, token, interest) 183 | } 184 | 185 | fn deregister(&mut self, registry: &Registry) -> Result<(), Error> { 186 | self.0.get_read_mut().deregister(registry) 187 | } 188 | } 189 | } 190 | 191 | /// A module for integrating signal handling with the MIO 0.8 runtime. 192 | /// 193 | /// This provides the [`Signals`][v0_8::Signals] struct as an abstraction 194 | /// which can be used with [`mio::Poll`][mio_0_8::Poll]. 195 | /// 196 | /// # Examples 197 | /// 198 | /// ```rust 199 | /// # use mio_0_8 as mio; 200 | /// use std::io::{Error, ErrorKind}; 201 | /// 202 | /// use signal_hook::consts::signal::*; 203 | /// use signal_hook_mio::v0_8::Signals; 204 | /// 205 | /// use mio::{Events, Poll, Interest, Token}; 206 | /// 207 | /// fn main() -> Result<(), Error> { 208 | /// let mut poll = Poll::new()?; 209 | /// 210 | /// let mut signals = Signals::new(&[ 211 | /// SIGTERM, 212 | /// # SIGUSR1, 213 | /// ])?; 214 | /// 215 | /// let signal_token = Token(0); 216 | /// 217 | /// poll.registry().register(&mut signals, signal_token, Interest::READABLE)?; 218 | /// # signal_hook::low_level::raise(SIGUSR1).unwrap(); // Just for terminating the example 219 | /// 220 | /// let mut events = Events::with_capacity(10); 221 | /// 'outer: loop { 222 | /// poll.poll(&mut events, None) 223 | /// .or_else(|e| if e.kind() == ErrorKind::Interrupted { 224 | /// // We get interrupt when a signal happens inside poll. That's non-fatal, just 225 | /// // retry. 226 | /// events.clear(); 227 | /// Ok(()) 228 | /// } else { 229 | /// Err(e) 230 | /// })?; 231 | /// for event in events.iter() { 232 | /// match event.token() { 233 | /// Token(0) => { 234 | /// for signal in signals.pending() { 235 | /// match signal { 236 | /// SIGTERM => break 'outer, 237 | /// # SIGUSR1 => return Ok(()), 238 | /// _ => unreachable!(), 239 | /// } 240 | /// } 241 | /// }, 242 | /// _ => unreachable!("Register other sources and match for their tokens here"), 243 | /// } 244 | /// } 245 | /// } 246 | /// 247 | /// Ok(()) 248 | /// } 249 | /// ``` 250 | #[cfg(feature = "support-v0_8")] 251 | pub mod v0_8 { 252 | use mio::event::Source; 253 | use mio::{Interest, Registry, Token}; 254 | use mio_0_8 as mio; 255 | 256 | implement_signals_with_pipe!(mio::net::UnixStream); 257 | 258 | impl Source for Signals { 259 | fn register( 260 | &mut self, 261 | registry: &Registry, 262 | token: Token, 263 | interest: Interest, 264 | ) -> Result<(), Error> { 265 | self.0.get_read_mut().register(registry, token, interest) 266 | } 267 | 268 | fn reregister( 269 | &mut self, 270 | registry: &Registry, 271 | token: Token, 272 | interest: Interest, 273 | ) -> Result<(), Error> { 274 | self.0.get_read_mut().reregister(registry, token, interest) 275 | } 276 | 277 | fn deregister(&mut self, registry: &Registry) -> Result<(), Error> { 278 | self.0.get_read_mut().deregister(registry) 279 | } 280 | } 281 | } 282 | 283 | /// A module for integrating signal handling with the MIO 0.7 runtime. 284 | /// 285 | /// This provides the [`Signals`][v0_7::Signals] struct as an abstraction 286 | /// which can be used with [`mio::Poll`][mio_0_7::Poll]. 287 | /// 288 | /// # Examples 289 | /// 290 | /// ```rust 291 | /// # use mio_0_7 as mio; 292 | /// use std::io::{Error, ErrorKind}; 293 | /// 294 | /// use signal_hook::consts::signal::*; 295 | /// use signal_hook_mio::v0_7::Signals; 296 | /// 297 | /// use mio::{Events, Poll, Interest, Token}; 298 | /// 299 | /// fn main() -> Result<(), Error> { 300 | /// let mut poll = Poll::new()?; 301 | /// 302 | /// let mut signals = Signals::new(&[ 303 | /// SIGTERM, 304 | /// # SIGUSR1, 305 | /// ])?; 306 | /// 307 | /// let signal_token = Token(0); 308 | /// 309 | /// poll.registry().register(&mut signals, signal_token, Interest::READABLE)?; 310 | /// # signal_hook::low_level::raise(SIGUSR1).unwrap(); // Just for terminating the example 311 | /// 312 | /// let mut events = Events::with_capacity(10); 313 | /// 'outer: loop { 314 | /// poll.poll(&mut events, None) 315 | /// .or_else(|e| if e.kind() == ErrorKind::Interrupted { 316 | /// // We get interrupt when a signal happens inside poll. That's non-fatal, just 317 | /// // retry. 318 | /// events.clear(); 319 | /// Ok(()) 320 | /// } else { 321 | /// Err(e) 322 | /// })?; 323 | /// for event in events.iter() { 324 | /// match event.token() { 325 | /// Token(0) => { 326 | /// for signal in signals.pending() { 327 | /// match signal { 328 | /// SIGTERM => break 'outer, 329 | /// # SIGUSR1 => return Ok(()), 330 | /// _ => unreachable!(), 331 | /// } 332 | /// } 333 | /// }, 334 | /// _ => unreachable!("Register other sources and match for their tokens here"), 335 | /// } 336 | /// } 337 | /// } 338 | /// 339 | /// Ok(()) 340 | /// } 341 | /// ``` 342 | #[cfg(feature = "support-v0_7")] 343 | pub mod v0_7 { 344 | use mio::event::Source; 345 | use mio::{Interest, Registry, Token}; 346 | use mio_0_7 as mio; 347 | 348 | implement_signals_with_pipe!(mio::net::UnixStream); 349 | 350 | impl Source for Signals { 351 | fn register( 352 | &mut self, 353 | registry: &Registry, 354 | token: Token, 355 | interest: Interest, 356 | ) -> Result<(), Error> { 357 | self.0.get_read_mut().register(registry, token, interest) 358 | } 359 | 360 | fn reregister( 361 | &mut self, 362 | registry: &Registry, 363 | token: Token, 364 | interest: Interest, 365 | ) -> Result<(), Error> { 366 | self.0.get_read_mut().reregister(registry, token, interest) 367 | } 368 | 369 | fn deregister(&mut self, registry: &Registry) -> Result<(), Error> { 370 | self.0.get_read_mut().deregister(registry) 371 | } 372 | } 373 | } 374 | 375 | /// A module for integrating signal handling with the MIO 0.6 runtime. 376 | /// 377 | /// This provides the [`Signals`][v0_6::Signals] struct as an abstraction 378 | /// which can be used with [`mio::Poll`][mio_0_6::Poll]. 379 | /// 380 | /// # Examples 381 | /// 382 | /// ```rust 383 | /// # use mio_0_6 as mio; 384 | /// use std::io::{Error, ErrorKind}; 385 | /// 386 | /// use signal_hook::consts::signal::*; 387 | /// use signal_hook_mio::v0_6::Signals; 388 | /// 389 | /// use mio::{Events, Poll, PollOpt, Ready, Token}; 390 | /// 391 | /// fn main() -> Result<(), Error> { 392 | /// let poll = Poll::new()?; 393 | /// 394 | /// let mut signals = Signals::new(&[ 395 | /// SIGTERM, 396 | /// # SIGUSR1, 397 | /// ])?; 398 | /// 399 | /// let signal_token = Token(0); 400 | /// 401 | /// poll.register(&mut signals, signal_token, Ready::readable(), PollOpt::level())?; 402 | /// # signal_hook::low_level::raise(SIGUSR1).unwrap(); // Just for terminating the example 403 | /// 404 | /// let mut events = Events::with_capacity(10); 405 | /// 'outer: loop { 406 | /// poll.poll(&mut events, None) 407 | /// .or_else(|e| if e.kind() == ErrorKind::Interrupted { 408 | /// // We get interrupt when a signal happens inside poll. That's non-fatal, just 409 | /// // retry. 410 | /// events.clear(); 411 | /// Ok(0) 412 | /// } else { 413 | /// Err(e) 414 | /// })?; 415 | /// for event in events.iter() { 416 | /// match event.token() { 417 | /// Token(0) => { 418 | /// for signal in signals.pending() { 419 | /// match signal { 420 | /// SIGTERM => break 'outer, 421 | /// # SIGUSR1 => return Ok(()), 422 | /// _ => unreachable!(), 423 | /// } 424 | /// } 425 | /// }, 426 | /// _ => unreachable!("Register other sources and match for their tokens here"), 427 | /// } 428 | /// } 429 | /// } 430 | /// 431 | /// Ok(()) 432 | /// } 433 | /// ``` 434 | #[cfg(feature = "support-v0_6")] 435 | pub mod v0_6 { 436 | 437 | use mio::event::Evented; 438 | use mio::{Poll, PollOpt, Ready, Token}; 439 | use mio_0_6 as mio; 440 | 441 | implement_signals_with_pipe!(mio_uds::UnixStream); 442 | 443 | impl Evented for Signals { 444 | fn register( 445 | &self, 446 | poll: &Poll, 447 | token: Token, 448 | interest: Ready, 449 | opts: PollOpt, 450 | ) -> Result<(), Error> { 451 | self.0.get_read().register(poll, token, interest, opts) 452 | } 453 | 454 | fn reregister( 455 | &self, 456 | poll: &Poll, 457 | token: Token, 458 | interest: Ready, 459 | opts: PollOpt, 460 | ) -> Result<(), Error> { 461 | self.0.get_read().reregister(poll, token, interest, opts) 462 | } 463 | 464 | fn deregister(&self, poll: &Poll) -> Result<(), Error> { 465 | self.0.get_read().deregister(poll) 466 | } 467 | } 468 | } 469 | -------------------------------------------------------------------------------- /signal-hook-mio/tests/mio_0_6.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "support-v0_6")] 2 | 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::sync::Arc; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | use mio_0_6::{Events, Poll, PollOpt, Ready, Token}; 9 | use signal_hook::consts::{SIGUSR1, SIGUSR2}; 10 | use signal_hook::low_level::raise; 11 | use signal_hook_mio::v0_6::Signals; 12 | 13 | use serial_test::serial; 14 | 15 | use libc::c_int; 16 | 17 | #[test] 18 | #[serial] 19 | fn mio_wakeup() { 20 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 21 | let token = Token(0); 22 | let poll = Poll::new().unwrap(); 23 | poll.register(&signals, token, Ready::readable(), PollOpt::level()) 24 | .unwrap(); 25 | let mut events = Events::with_capacity(10); 26 | 27 | // The self pipe shouldn't be readable yet 28 | poll.poll(&mut events, Some(Duration::from_secs(0))) 29 | .unwrap(); 30 | assert!(events.is_empty()); 31 | 32 | raise(SIGUSR1).unwrap(); 33 | poll.poll(&mut events, Some(Duration::from_secs(10))) 34 | .unwrap(); 35 | let event = events.iter().next().unwrap(); 36 | assert!(event.readiness().is_readable()); 37 | assert_eq!(token, event.token()); 38 | let sig = signals.pending().next().unwrap(); 39 | assert_eq!(SIGUSR1, sig); 40 | 41 | // The self pipe shouldn't be readable after consuming signals 42 | poll.poll(&mut events, Some(Duration::from_secs(0))) 43 | .unwrap(); 44 | assert!(events.is_empty()); 45 | } 46 | 47 | #[test] 48 | #[serial] 49 | fn mio_multiple_signals() { 50 | let mut signals = Signals::new(&[SIGUSR1, SIGUSR2]).unwrap(); 51 | let poll = Poll::new().unwrap(); 52 | let token = Token(0); 53 | poll.register(&signals, token, Ready::readable(), PollOpt::level()) 54 | .unwrap(); 55 | 56 | let mut events = Events::with_capacity(10); 57 | 58 | raise(SIGUSR1).unwrap(); 59 | raise(SIGUSR2).unwrap(); 60 | 61 | poll.poll(&mut events, Some(Duration::from_secs(10))) 62 | .unwrap(); 63 | 64 | let event = events.iter().next().unwrap(); 65 | assert!(event.readiness().is_readable()); 66 | 67 | let sigs: Vec = signals.pending().collect(); 68 | assert_eq!(2, sigs.len()); 69 | assert!(sigs.contains(&SIGUSR1)); 70 | assert!(sigs.contains(&SIGUSR2)); 71 | 72 | // The self pipe shouldn't be completely empty after calling pending() 73 | poll.poll(&mut events, Some(Duration::from_secs(0))) 74 | .unwrap(); 75 | assert!(events.is_empty()); 76 | } 77 | 78 | #[test] 79 | #[serial] 80 | fn mio_parallel_multiple() { 81 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 82 | let poll = Poll::new().unwrap(); 83 | let token = Token(0); 84 | poll.register(&signals, token, Ready::readable(), PollOpt::level()) 85 | .unwrap(); 86 | 87 | let mut events = Events::with_capacity(10); 88 | 89 | let thread_done = Arc::new(AtomicBool::new(false)); 90 | 91 | let done = Arc::clone(&thread_done); 92 | thread::spawn(move || { 93 | for _ in 0..10 { 94 | // Wait some time to allow main thread to poll 95 | thread::sleep(Duration::from_millis(25)); 96 | raise(SIGUSR1).unwrap(); 97 | } 98 | done.store(true, Ordering::SeqCst); 99 | 100 | // Raise a final signal so the main thread wakes up 101 | // if it already called poll. 102 | raise(SIGUSR1).unwrap(); 103 | }); 104 | 105 | while !thread_done.load(Ordering::SeqCst) { 106 | poll.poll(&mut events, Some(Duration::from_secs(10))) 107 | .unwrap(); 108 | let event = events.iter().next().unwrap(); 109 | assert!(event.readiness().is_readable()); 110 | assert_eq!(SIGUSR1, signals.pending().next().unwrap()); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /signal-hook-mio/tests/mio_0_7.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "support-v0_7")] 2 | 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::sync::Arc; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | use mio_0_7::{Events, Interest, Poll, Token}; 9 | 10 | use signal_hook::consts::{SIGUSR1, SIGUSR2}; 11 | use signal_hook::low_level::raise; 12 | use signal_hook_mio::v0_7::Signals; 13 | 14 | use serial_test::serial; 15 | 16 | use libc::c_int; 17 | 18 | #[test] 19 | #[serial] 20 | fn mio_wakeup() { 21 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 22 | let mut poll = Poll::new().unwrap(); 23 | let token = Token(0); 24 | poll.registry() 25 | .register(&mut signals, token, Interest::READABLE) 26 | .unwrap(); 27 | 28 | let mut events = Events::with_capacity(10); 29 | 30 | // The self pipe shouldn't be readable yet 31 | poll.poll(&mut events, Some(Duration::from_secs(0))) 32 | .unwrap(); 33 | assert!(events.is_empty()); 34 | 35 | raise(SIGUSR1).unwrap(); 36 | poll.poll(&mut events, Some(Duration::from_secs(10))) 37 | .unwrap(); 38 | let event = events.iter().next().unwrap(); 39 | 40 | assert!(event.is_readable()); 41 | assert_eq!(token, event.token()); 42 | let sig = signals.pending().next().unwrap(); 43 | assert_eq!(SIGUSR1, sig); 44 | 45 | // The self pipe shouldn't be readable after consuming signals 46 | poll.poll(&mut events, Some(Duration::from_secs(0))) 47 | .unwrap(); 48 | assert!(events.is_empty()); 49 | } 50 | 51 | #[test] 52 | #[serial] 53 | fn mio_multiple_signals() { 54 | let mut signals = Signals::new(&[SIGUSR1, SIGUSR2]).unwrap(); 55 | let mut poll = Poll::new().unwrap(); 56 | let token = Token(0); 57 | poll.registry() 58 | .register(&mut signals, token, Interest::READABLE) 59 | .unwrap(); 60 | 61 | let mut events = Events::with_capacity(10); 62 | 63 | raise(SIGUSR1).unwrap(); 64 | raise(SIGUSR2).unwrap(); 65 | 66 | poll.poll(&mut events, Some(Duration::from_secs(10))) 67 | .unwrap(); 68 | 69 | let event = events.iter().next().unwrap(); 70 | assert!(event.is_readable()); 71 | 72 | let sigs: Vec = signals.pending().collect(); 73 | assert_eq!(2, sigs.len()); 74 | assert!(sigs.contains(&SIGUSR1)); 75 | assert!(sigs.contains(&SIGUSR2)); 76 | 77 | // The self pipe shouldn't be completely empty after calling pending() 78 | poll.poll(&mut events, Some(Duration::from_secs(0))) 79 | .unwrap(); 80 | assert!(events.is_empty()); 81 | } 82 | 83 | #[test] 84 | #[serial] 85 | fn mio_parallel_multiple() { 86 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 87 | let mut poll = Poll::new().unwrap(); 88 | let token = Token(0); 89 | poll.registry() 90 | .register(&mut signals, token, Interest::READABLE) 91 | .unwrap(); 92 | 93 | let mut events = Events::with_capacity(10); 94 | 95 | let thread_done = Arc::new(AtomicBool::new(false)); 96 | 97 | let done = Arc::clone(&thread_done); 98 | thread::spawn(move || { 99 | for _ in 0..10 { 100 | // Wait some time to allow main thread to poll 101 | thread::sleep(Duration::from_millis(25)); 102 | raise(SIGUSR1).unwrap(); 103 | } 104 | done.store(true, Ordering::SeqCst); 105 | 106 | // Raise a final signal so the main thread wakes up 107 | // if it already called poll. 108 | raise(SIGUSR1).unwrap(); 109 | }); 110 | 111 | while !thread_done.load(Ordering::SeqCst) { 112 | poll.poll(&mut events, Some(Duration::from_secs(10))) 113 | .unwrap(); 114 | let event = events.iter().next().unwrap(); 115 | assert!(event.is_readable()); 116 | assert_eq!(SIGUSR1, signals.pending().next().unwrap()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /signal-hook-mio/tests/mio_0_8.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "support-v0_8")] 2 | 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::sync::Arc; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | use mio_0_8::{Events, Interest, Poll, Token}; 9 | 10 | use signal_hook::consts::{SIGUSR1, SIGUSR2}; 11 | use signal_hook::low_level::raise; 12 | use signal_hook_mio::v0_8::Signals; 13 | 14 | use serial_test::serial; 15 | 16 | use libc::c_int; 17 | 18 | #[test] 19 | #[serial] 20 | fn mio_wakeup() { 21 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 22 | let mut poll = Poll::new().unwrap(); 23 | let token = Token(0); 24 | poll.registry() 25 | .register(&mut signals, token, Interest::READABLE) 26 | .unwrap(); 27 | 28 | let mut events = Events::with_capacity(10); 29 | 30 | // The self pipe shouldn't be readable yet 31 | poll.poll(&mut events, Some(Duration::from_secs(0))) 32 | .unwrap(); 33 | assert!(events.is_empty()); 34 | 35 | raise(SIGUSR1).unwrap(); 36 | poll.poll(&mut events, Some(Duration::from_secs(10))) 37 | .unwrap(); 38 | let event = events.iter().next().unwrap(); 39 | 40 | assert!(event.is_readable()); 41 | assert_eq!(token, event.token()); 42 | let sig = signals.pending().next().unwrap(); 43 | assert_eq!(SIGUSR1, sig); 44 | 45 | // The self pipe shouldn't be readable after consuming signals 46 | poll.poll(&mut events, Some(Duration::from_secs(0))) 47 | .unwrap(); 48 | assert!(events.is_empty()); 49 | } 50 | 51 | #[test] 52 | #[serial] 53 | fn mio_multiple_signals() { 54 | let mut signals = Signals::new(&[SIGUSR1, SIGUSR2]).unwrap(); 55 | let mut poll = Poll::new().unwrap(); 56 | let token = Token(0); 57 | poll.registry() 58 | .register(&mut signals, token, Interest::READABLE) 59 | .unwrap(); 60 | 61 | let mut events = Events::with_capacity(10); 62 | 63 | raise(SIGUSR1).unwrap(); 64 | raise(SIGUSR2).unwrap(); 65 | 66 | poll.poll(&mut events, Some(Duration::from_secs(10))) 67 | .unwrap(); 68 | 69 | let event = events.iter().next().unwrap(); 70 | assert!(event.is_readable()); 71 | 72 | let sigs: Vec = signals.pending().collect(); 73 | assert_eq!(2, sigs.len()); 74 | assert!(sigs.contains(&SIGUSR1)); 75 | assert!(sigs.contains(&SIGUSR2)); 76 | 77 | // The self pipe shouldn't be completely empty after calling pending() 78 | poll.poll(&mut events, Some(Duration::from_secs(0))) 79 | .unwrap(); 80 | assert!(events.is_empty()); 81 | } 82 | 83 | #[test] 84 | #[serial] 85 | fn mio_parallel_multiple() { 86 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 87 | let mut poll = Poll::new().unwrap(); 88 | let token = Token(0); 89 | poll.registry() 90 | .register(&mut signals, token, Interest::READABLE) 91 | .unwrap(); 92 | 93 | let mut events = Events::with_capacity(10); 94 | 95 | let thread_done = Arc::new(AtomicBool::new(false)); 96 | 97 | let done = Arc::clone(&thread_done); 98 | thread::spawn(move || { 99 | for _ in 0..10 { 100 | // Wait some time to allow main thread to poll 101 | thread::sleep(Duration::from_millis(25)); 102 | raise(SIGUSR1).unwrap(); 103 | } 104 | done.store(true, Ordering::SeqCst); 105 | 106 | // Raise a final signal so the main thread wakes up 107 | // if it already called poll. 108 | raise(SIGUSR1).unwrap(); 109 | }); 110 | 111 | while !thread_done.load(Ordering::SeqCst) { 112 | poll.poll(&mut events, Some(Duration::from_secs(10))) 113 | .unwrap(); 114 | let event = events.iter().next().unwrap(); 115 | assert!(event.is_readable()); 116 | assert_eq!(SIGUSR1, signals.pending().next().unwrap()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /signal-hook-mio/tests/mio_1_0.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "support-v1_0")] 2 | 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::sync::Arc; 5 | use std::thread; 6 | use std::time::Duration; 7 | 8 | use mio_1_0::{Events, Interest, Poll, Token}; 9 | 10 | use signal_hook::consts::{SIGUSR1, SIGUSR2}; 11 | use signal_hook::low_level::raise; 12 | use signal_hook_mio::v1_0::Signals; 13 | 14 | use serial_test::serial; 15 | 16 | use libc::c_int; 17 | 18 | #[test] 19 | #[serial] 20 | fn mio_wakeup() { 21 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 22 | let mut poll = Poll::new().unwrap(); 23 | let token = Token(0); 24 | poll.registry() 25 | .register(&mut signals, token, Interest::READABLE) 26 | .unwrap(); 27 | 28 | let mut events = Events::with_capacity(10); 29 | 30 | // The self pipe shouldn't be readable yet 31 | poll.poll(&mut events, Some(Duration::from_secs(0))) 32 | .unwrap(); 33 | assert!(events.is_empty()); 34 | 35 | raise(SIGUSR1).unwrap(); 36 | poll.poll(&mut events, Some(Duration::from_secs(10))) 37 | .unwrap(); 38 | let event = events.iter().next().unwrap(); 39 | 40 | assert!(event.is_readable()); 41 | assert_eq!(token, event.token()); 42 | let sig = signals.pending().next().unwrap(); 43 | assert_eq!(SIGUSR1, sig); 44 | 45 | // The self pipe shouldn't be readable after consuming signals 46 | poll.poll(&mut events, Some(Duration::from_secs(0))) 47 | .unwrap(); 48 | assert!(events.is_empty()); 49 | } 50 | 51 | #[test] 52 | #[serial] 53 | fn mio_multiple_signals() { 54 | let mut signals = Signals::new(&[SIGUSR1, SIGUSR2]).unwrap(); 55 | let mut poll = Poll::new().unwrap(); 56 | let token = Token(0); 57 | poll.registry() 58 | .register(&mut signals, token, Interest::READABLE) 59 | .unwrap(); 60 | 61 | let mut events = Events::with_capacity(10); 62 | 63 | raise(SIGUSR1).unwrap(); 64 | raise(SIGUSR2).unwrap(); 65 | 66 | poll.poll(&mut events, Some(Duration::from_secs(10))) 67 | .unwrap(); 68 | 69 | let event = events.iter().next().unwrap(); 70 | assert!(event.is_readable()); 71 | 72 | let sigs: Vec = signals.pending().collect(); 73 | assert_eq!(2, sigs.len()); 74 | assert!(sigs.contains(&SIGUSR1)); 75 | assert!(sigs.contains(&SIGUSR2)); 76 | 77 | // The self pipe shouldn't be completely empty after calling pending() 78 | poll.poll(&mut events, Some(Duration::from_secs(0))) 79 | .unwrap(); 80 | assert!(events.is_empty()); 81 | } 82 | 83 | #[test] 84 | #[serial] 85 | fn mio_parallel_multiple() { 86 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 87 | let mut poll = Poll::new().unwrap(); 88 | let token = Token(0); 89 | poll.registry() 90 | .register(&mut signals, token, Interest::READABLE) 91 | .unwrap(); 92 | 93 | let mut events = Events::with_capacity(10); 94 | 95 | let thread_done = Arc::new(AtomicBool::new(false)); 96 | 97 | let done = Arc::clone(&thread_done); 98 | thread::spawn(move || { 99 | for _ in 0..10 { 100 | // Wait some time to allow main thread to poll 101 | thread::sleep(Duration::from_millis(25)); 102 | raise(SIGUSR1).unwrap(); 103 | } 104 | done.store(true, Ordering::SeqCst); 105 | 106 | // Raise a final signal so the main thread wakes up 107 | // if it already called poll. 108 | raise(SIGUSR1).unwrap(); 109 | }); 110 | 111 | while !thread_done.load(Ordering::SeqCst) { 112 | poll.poll(&mut events, Some(Duration::from_secs(10))) 113 | .unwrap(); 114 | let event = events.iter().next().unwrap(); 115 | assert!(event.is_readable()); 116 | assert_eq!(SIGUSR1, signals.pending().next().unwrap()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /signal-hook-registry/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-hook-registry" 3 | version = "1.4.5" 4 | authors = [ 5 | "Michal 'vorner' Vaner ", 6 | "Masaki Hara ", 7 | ] 8 | 9 | description = "Backend crate for signal-hook" 10 | documentation = "https://docs.rs/signal-hook-registry" 11 | readme = "README.md" 12 | repository = "https://github.com/vorner/signal-hook" 13 | keywords = ["signal", "unix", "daemon"] 14 | license = "Apache-2.0/MIT" 15 | 16 | [badges] 17 | travis-ci = { repository = "vorner/signal-hook" } 18 | maintenance = { status = "actively-developed" } 19 | 20 | [dependencies] 21 | libc = "^0.2" 22 | 23 | [dev-dependencies] 24 | signal-hook = { version = "~0.3", path = ".." } 25 | -------------------------------------------------------------------------------- /signal-hook-registry/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /signal-hook-registry/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /signal-hook-registry/README.md: -------------------------------------------------------------------------------- 1 | # Signal-hook-registry 2 | 3 | [![Travis Build Status](https://api.travis-ci.org/vorner/signal-hook.svg?branch=master)](https://travis-ci.org/vorner/signal-hook) 4 | 5 | This is the backend crate for the 6 | [signal-hook](https://crates.io/crates/signal-hook) crate. The general direct use of 7 | this crate is discouraged. See the 8 | [documentation](https://docs.rs/signal-hook-registry) for further details. 9 | 10 | ## License 11 | 12 | Licensed under either of 13 | 14 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 15 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 16 | 17 | at your option. 18 | 19 | ### Contribution 20 | 21 | Unless you explicitly state otherwise, any contribution intentionally 22 | submitted for inclusion in the work by you, as defined in the Apache-2.0 23 | license, shall be dual licensed as above, without any additional terms 24 | or conditions. 25 | -------------------------------------------------------------------------------- /signal-hook-registry/src/half_lock.rs: -------------------------------------------------------------------------------- 1 | //! The half-lock structure 2 | //! 3 | //! We need a way to protect the structure with configured hooks ‒ a signal may happen in arbitrary 4 | //! thread and needs to read them while another thread might be manipulating the structure. 5 | //! 6 | //! Under ordinary circumstances we would be happy to just use `Mutex>`. However, 7 | //! as we use it in the signal handler, we are severely limited in what we can or can't use. So we 8 | //! choose to implement kind of spin-look thing with atomics. 9 | //! 10 | //! In the reader it is always simply locked and then unlocked, making sure it doesn't disappear 11 | //! while in use. 12 | //! 13 | //! The writer has a separate mutex (that prevents other writers; this is used outside of the 14 | //! signal handler), makes a copy of the data and swaps an atomic pointer to the data structure. 15 | //! But it waits until everything is unlocked (no signal handler has the old data) for dropping the 16 | //! old instance. There's a generation trick to make sure that new signal locks another instance. 17 | //! 18 | //! The downside is, this is an active spin lock at the writer end. However, we assume than: 19 | //! 20 | //! * Signals are one time setup before we actually have threads. We just need to make *sure* we 21 | //! are safe even if this is not true. 22 | //! * Signals are rare, happening at the same time as the write even rarer. 23 | //! * Signals are short, as there is mostly nothing allowed inside them anyway. 24 | //! * Our tool box is severely limited. 25 | //! 26 | //! Therefore this is hopefully reasonable trade-off. 27 | //! 28 | //! # Atomic orderings 29 | //! 30 | //! The whole code uses SeqCst conservatively. Atomics are not used because of performance here and 31 | //! are the minor price around signals anyway. But the comments state which orderings should be 32 | //! enough in practice in case someone wants to get inspired (but do make your own check through 33 | //! them anyway). 34 | 35 | use std::isize; 36 | use std::marker::PhantomData; 37 | use std::ops::Deref; 38 | use std::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; 39 | use std::sync::{Mutex, MutexGuard, PoisonError}; 40 | use std::thread; 41 | 42 | use libc; 43 | 44 | const YIELD_EVERY: usize = 16; 45 | const MAX_GUARDS: usize = (isize::MAX) as usize; 46 | 47 | pub(crate) struct ReadGuard<'a, T: 'a> { 48 | data: &'a T, 49 | lock: &'a AtomicUsize, 50 | } 51 | 52 | impl<'a, T> Deref for ReadGuard<'a, T> { 53 | type Target = T; 54 | fn deref(&self) -> &T { 55 | self.data 56 | } 57 | } 58 | 59 | impl<'a, T> Drop for ReadGuard<'a, T> { 60 | fn drop(&mut self) { 61 | // We effectively unlock; Release would be enough. 62 | self.lock.fetch_sub(1, Ordering::SeqCst); 63 | } 64 | } 65 | 66 | pub(crate) struct WriteGuard<'a, T: 'a> { 67 | _guard: MutexGuard<'a, ()>, 68 | lock: &'a HalfLock, 69 | data: &'a T, 70 | } 71 | 72 | impl<'a, T> WriteGuard<'a, T> { 73 | pub(crate) fn store(&mut self, val: T) { 74 | // Move to the heap and convert to raw pointer for AtomicPtr. 75 | let new = Box::into_raw(Box::new(val)); 76 | 77 | self.data = unsafe { &*new }; 78 | 79 | // We can just put the new value in here safely, we worry only about dropping the old one. 80 | // Release might (?) be enough, to "upload" the data. 81 | let old = self.lock.data.swap(new, Ordering::SeqCst); 82 | 83 | // Now we make sure there's no reader having the old data. 84 | self.lock.write_barrier(); 85 | 86 | drop(unsafe { Box::from_raw(old) }); 87 | } 88 | } 89 | 90 | impl<'a, T> Deref for WriteGuard<'a, T> { 91 | type Target = T; 92 | fn deref(&self) -> &T { 93 | // Protected by that mutex 94 | self.data 95 | } 96 | } 97 | 98 | pub(crate) struct HalfLock { 99 | // We conceptually contain an instance of T 100 | _t: PhantomData, 101 | // The actual data as a pointer. 102 | data: AtomicPtr, 103 | // The generation of the data. Influences which slot of the lock counter we use. 104 | generation: AtomicUsize, 105 | // How many active locks are there? 106 | lock: [AtomicUsize; 2], 107 | // Mutex for the writers; only one writer. 108 | write_mutex: Mutex<()>, 109 | } 110 | 111 | impl HalfLock { 112 | pub(crate) fn new(data: T) -> Self { 113 | // Move to the heap so we can safely point there. Then convert to raw pointer as AtomicPtr 114 | // operates on raw pointers. The AtomicPtr effectively acts like Box for us semantically. 115 | let ptr = Box::into_raw(Box::new(data)); 116 | Self { 117 | _t: PhantomData, 118 | data: AtomicPtr::new(ptr), 119 | generation: AtomicUsize::new(0), 120 | lock: [AtomicUsize::new(0), AtomicUsize::new(0)], 121 | write_mutex: Mutex::new(()), 122 | } 123 | } 124 | 125 | pub(crate) fn read(&self) -> ReadGuard { 126 | // Relaxed should be enough; we only pick one or the other slot and the writer observes 127 | // that both were 0 at some time. So the actual value doesn't really matter for safety, 128 | // only the changing improves the performance. 129 | let gen = self.generation.load(Ordering::SeqCst); 130 | let lock = &self.lock[gen % 2]; 131 | // Effectively locking something, acquire should be enough. 132 | let guard_cnt = lock.fetch_add(1, Ordering::SeqCst); 133 | 134 | // This is to prevent overflowing the counter in some degenerate cases, which could lead to 135 | // UB (freeing data while still in use). However, as this data structure is used only 136 | // internally and it's not possible to leak the guard and the guard itself takes some 137 | // memory, it should be really impossible to trigger this case. Still, we include it from 138 | // abundance of caution. 139 | // 140 | // This technically is not fully correct as enough threads being in between here and the 141 | // abort below could still overflow it and it could get freed for some *other* thread, but 142 | // that would mean having too many active threads to fit into RAM too and is even more 143 | // absurd corner case than the above. 144 | if guard_cnt > MAX_GUARDS { 145 | unsafe { libc::abort() }; 146 | } 147 | 148 | // Acquire should be enough; we need to "download" the data, paired with the swap on the 149 | // same pointer. 150 | let data = self.data.load(Ordering::SeqCst); 151 | // Safe: 152 | // * It did point to valid data when put in. 153 | // * Protected by lock, so still valid. 154 | let data = unsafe { &*data }; 155 | 156 | ReadGuard { data, lock } 157 | } 158 | 159 | fn update_seen(&self, seen_zero: &mut [bool; 2]) { 160 | for (seen, slot) in seen_zero.iter_mut().zip(&self.lock) { 161 | *seen = *seen || slot.load(Ordering::SeqCst) == 0; 162 | } 163 | } 164 | 165 | fn write_barrier(&self) { 166 | // Do a first check of seeing zeroes before we switch the generation. At least one of them 167 | // should be zero by now, due to having drained the generation before leaving the previous 168 | // writer. 169 | let mut seen_zero = [false; 2]; 170 | self.update_seen(&mut seen_zero); 171 | // By switching the generation to the other slot, we make sure the currently active starts 172 | // draining while the other will start filling up. 173 | self.generation.fetch_add(1, Ordering::SeqCst); // Overflow is fine. 174 | 175 | let mut iter = 0usize; 176 | while !seen_zero.iter().all(|s| *s) { 177 | iter = iter.wrapping_add(1); 178 | 179 | // Be somewhat less aggressive while looping, switch to the other threads if possible. 180 | if cfg!(not(miri)) { 181 | if iter % YIELD_EVERY == 0 { 182 | thread::yield_now(); 183 | } else { 184 | // Replaced by hint::spin_loop, but we want to support older compiler 185 | #[allow(deprecated)] 186 | atomic::spin_loop_hint(); 187 | } 188 | } 189 | 190 | self.update_seen(&mut seen_zero); 191 | } 192 | } 193 | 194 | pub(crate) fn write(&self) -> WriteGuard { 195 | // While it's possible the user code panics, our code in store doesn't and the data gets 196 | // swapped atomically. So if it panics, nothing gets changed, therefore poisons are of no 197 | // interest here. 198 | let guard = self 199 | .write_mutex 200 | .lock() 201 | .unwrap_or_else(PoisonError::into_inner); 202 | 203 | // Relaxed should be enough, as we are under the same mutex that was used to get the data 204 | // in. 205 | let data = self.data.load(Ordering::SeqCst); 206 | // Safe: 207 | // * Stored as valid data 208 | // * Only this method, protected by mutex, can change the pointer, so it didn't go away. 209 | let data = unsafe { &*data }; 210 | 211 | WriteGuard { 212 | data, 213 | _guard: guard, 214 | lock: self, 215 | } 216 | } 217 | } 218 | 219 | impl Drop for HalfLock { 220 | fn drop(&mut self) { 221 | // During drop we are sure there are no other borrows of the data so we are free to just 222 | // drop it. Also, the drop impl won't be called in practice in our case, as it is used 223 | // solely as a global variable, but we provide it for completeness and tests anyway. 224 | // 225 | // unsafe: the pointer in there is always valid, we just take the last instance out. 226 | unsafe { 227 | // Acquire should be enough. 228 | let data = Box::from_raw(self.data.load(Ordering::SeqCst)); 229 | drop(data); 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /signal-hook-registry/tests/unregister_signal.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the [unregister_signal] function. 2 | //! 3 | //! As a separate integration level test, so it doesn't clash with other tests on the signals. 4 | 5 | // The unregister_signal itself is deprecated. But we still want to test it, so it's not deprecated 6 | // and broken at the same time. 7 | #![allow(deprecated)] 8 | 9 | extern crate libc; 10 | extern crate signal_hook_registry; 11 | 12 | use std::sync::atomic::{AtomicUsize, Ordering}; 13 | use std::sync::Arc; 14 | 15 | use libc::{SIGINT, SIGTERM}; // We'll use these here because SIGUSR1 is not available on Windows. 16 | use signal_hook_registry::{register, unregister_signal}; 17 | 18 | #[test] 19 | fn register_unregister() { 20 | let called = Arc::new(AtomicUsize::new(0)); 21 | 22 | let hook = { 23 | let called = Arc::clone(&called); 24 | move || { 25 | called.fetch_add(1, Ordering::Relaxed); 26 | } 27 | }; 28 | 29 | unsafe { 30 | register(SIGTERM, hook.clone()).unwrap(); 31 | register(SIGTERM, hook.clone()).unwrap(); 32 | register(SIGINT, hook.clone()).unwrap(); 33 | 34 | libc::raise(SIGTERM); 35 | } 36 | 37 | // The closure is run twice. 38 | assert_eq!(2, called.load(Ordering::Relaxed)); 39 | 40 | assert!(unregister_signal(SIGTERM)); 41 | 42 | unsafe { libc::raise(SIGTERM) }; 43 | // Second one unregisters nothing. 44 | assert!(!unregister_signal(SIGTERM)); 45 | 46 | // After unregistering (both), it is no longer run at all. 47 | assert_eq!(2, called.load(Ordering::Relaxed)); 48 | 49 | // The SIGINT one is not disturbed. 50 | unsafe { libc::raise(SIGINT) }; 51 | assert_eq!(3, called.load(Ordering::Relaxed)); 52 | 53 | // But it's possible to register it again just fine. 54 | unsafe { 55 | register(SIGTERM, hook).unwrap(); 56 | libc::raise(SIGTERM); 57 | } 58 | assert_eq!(4, called.load(Ordering::Relaxed)); 59 | } 60 | -------------------------------------------------------------------------------- /signal-hook-tokio/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signal-hook-tokio" 3 | version = "0.3.1" 4 | authors = [ 5 | "Michal 'vorner' Vaner ", 6 | "Thomas Himmelstoss ", 7 | ] 8 | 9 | description = "Tokio support for signal-hook" 10 | documentation = "https://docs.rs/signal-hook-tokio" 11 | readme = "README.md" 12 | repository = "https://github.com/vorner/signal-hook" 13 | keywords = ["signal", "unix", "tokio"] 14 | license = "Apache-2.0/MIT" 15 | 16 | edition = "2018" 17 | 18 | [badges] 19 | travis-ci = { repository = "vorner/signal-hook" } 20 | maintenance = { status = "actively-developed" } 21 | 22 | [features] 23 | futures-v0_3 = ["futures-core-0_3"] 24 | 25 | [dependencies] 26 | libc = "~0.2" 27 | signal-hook = { version = "~0.3", path = ".." } 28 | futures-core-0_3 = { package = "futures-core", version = "~0.3", optional = true } 29 | tokio = { version = "~1", features = ["net"] } 30 | 31 | [dev-dependencies] 32 | tokio = { package = "tokio", version = "~1", features = ["full"] } 33 | futures = "~0.3" 34 | serial_test = "~0.5" 35 | 36 | [package.metadata.docs.rs] 37 | all-features = true 38 | rustdoc-args = ["--cfg", "docsrs"] 39 | -------------------------------------------------------------------------------- /signal-hook-tokio/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | ../LICENSE-APACHE -------------------------------------------------------------------------------- /signal-hook-tokio/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | ../LICENSE-MIT -------------------------------------------------------------------------------- /signal-hook-tokio/README.md: -------------------------------------------------------------------------------- 1 | # Signal-hook-tokio 2 | 3 | [![Travis Build Status](https://api.travis-ci.org/vorner/signal-hook.svg?branch=master)](https://travis-ci.org/vorner/signal-hook) 4 | 5 | This is a tokio adapter crate for the 6 | [signal-hook](https://crates.io/crates/signal-hook) crate. See the 7 | [documentation](https://docs.rs/signal-hook-tokio) for further details. 8 | 9 | ## License 10 | 11 | Licensed under either of 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 14 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 15 | 16 | at your option. 17 | 18 | ### Contribution 19 | 20 | Unless you explicitly state otherwise, any contribution intentionally 21 | submitted for inclusion in the work by you, as defined in the Apache-2.0 22 | license, shall be dual licensed as above, without any additional terms 23 | or conditions. 24 | -------------------------------------------------------------------------------- /signal-hook-tokio/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(test(attr(deny(warnings))))] 2 | #![warn(missing_docs)] 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | #![cfg_attr(not(feature = "futures-v0_3"), allow(dead_code, unused_imports))] 5 | 6 | //! A crate for integrating signal handling with the Tokio runtime. 7 | //! 8 | //! This provides the [`Signals`][Signals] struct which acts as a 9 | //! [`Stream`] of signals. 10 | //! 11 | //! Note that the `futures-v0_3` feature of this crate must be 12 | //! enabled for `Signals` to implement the `Stream` trait. 13 | //! 14 | //! # Example 15 | //! 16 | //! ```rust 17 | //! # #[cfg(feature = "futures-v0_3")] 18 | //! # mod test { 19 | //! use std::io::Error; 20 | //! 21 | //! use signal_hook::consts::signal::*; 22 | //! use signal_hook_tokio::Signals; 23 | //! 24 | //! use futures::stream::StreamExt; 25 | //! 26 | //! async fn handle_signals(mut signals: Signals) { 27 | //! while let Some(signal) = signals.next().await { 28 | //! match signal { 29 | //! SIGHUP => { 30 | //! // Reload configuration 31 | //! // Reopen the log file 32 | //! } 33 | //! SIGTERM | SIGINT | SIGQUIT => { 34 | //! // Shutdown the system; 35 | //! }, 36 | //! _ => unreachable!(), 37 | //! } 38 | //! } 39 | //! } 40 | //! 41 | //! #[tokio::main] 42 | //! # pub 43 | //! async fn main() -> Result<(), Error> { 44 | //! let signals = Signals::new(&[ 45 | //! SIGHUP, 46 | //! SIGTERM, 47 | //! SIGINT, 48 | //! SIGQUIT, 49 | //! ])?; 50 | //! let handle = signals.handle(); 51 | //! 52 | //! let signals_task = tokio::spawn(handle_signals(signals)); 53 | //! 54 | //! // Execute your main program logic 55 | //! 56 | //! // Terminate the signal stream. 57 | //! handle.close(); 58 | //! signals_task.await?; 59 | //! 60 | //! Ok(()) 61 | //! } 62 | //! # } 63 | //! # fn main() -> Result<(), std::io::Error> { 64 | //! # #[cfg(feature = "futures-v0_3")] 65 | //! # test::main()?; 66 | //! # Ok(()) 67 | //! # } 68 | //! ``` 69 | 70 | use std::borrow::Borrow; 71 | use std::io::Error; 72 | use std::pin::Pin; 73 | use std::task::{Context, Poll}; 74 | 75 | use libc::c_int; 76 | 77 | use tokio::io::{AsyncRead, ReadBuf}; 78 | use tokio::net::UnixStream; 79 | 80 | pub use signal_hook::iterator::backend::Handle; 81 | use signal_hook::iterator::backend::{OwningSignalIterator, PollResult, SignalDelivery}; 82 | use signal_hook::iterator::exfiltrator::{Exfiltrator, SignalOnly}; 83 | 84 | #[cfg(feature = "futures-v0_3")] 85 | use futures_core_0_3::Stream; 86 | 87 | /// An asynchronous [`Stream`] of arriving signals. 88 | /// 89 | /// The stream doesn't return the signals in the order they were recieved by 90 | /// the process and may merge signals received multiple times. 91 | pub struct SignalsInfo(OwningSignalIterator); 92 | 93 | impl SignalsInfo { 94 | /// Create a `SignalsInfo` instance. 95 | /// 96 | /// This registers all the signals listed. The same restrictions (panics, errors) apply 97 | /// as with [`Handle::add_signal`]. 98 | pub fn new(signals: I) -> Result 99 | where 100 | I: IntoIterator, 101 | S: Borrow, 102 | E: Default, 103 | { 104 | Self::with_exfiltrator(signals, E::default()) 105 | } 106 | 107 | /// A constructor with explicit exfiltrator. 108 | pub fn with_exfiltrator(signals: I, exfiltrator: E) -> Result 109 | where 110 | I: IntoIterator, 111 | S: Borrow, 112 | { 113 | let (read, write) = UnixStream::pair()?; 114 | let inner = SignalDelivery::with_pipe(read, write, exfiltrator, signals)?; 115 | Ok(Self(OwningSignalIterator::new(inner))) 116 | } 117 | 118 | /// Get a shareable [`Handle`] for this `Signals` instance. 119 | /// 120 | /// This can be used to add further signals or close the [`Signals`] instance 121 | /// which terminates the whole signal stream. 122 | pub fn handle(&self) -> Handle { 123 | self.0.handle() 124 | } 125 | } 126 | 127 | /// Simplified version of the signals stream. 128 | /// 129 | /// This one simply returns the signal numbers, while [`SignalsInfo`] can provide additional 130 | /// information. 131 | pub type Signals = SignalsInfo; 132 | 133 | impl SignalsInfo { 134 | fn has_signals(read: &mut UnixStream, ctx: &mut Context<'_>) -> Result { 135 | let mut buf = [0u8]; 136 | let mut read_buf = ReadBuf::new(&mut buf); 137 | match Pin::new(read).poll_read(ctx, &mut read_buf) { 138 | Poll::Pending => Ok(false), 139 | Poll::Ready(Ok(())) => Ok(true), 140 | Poll::Ready(Err(error)) => Err(error), 141 | } 142 | } 143 | } 144 | 145 | #[cfg_attr(docsrs, doc(cfg(feature = "futures-v0_3")))] 146 | #[cfg(feature = "futures-v0_3")] 147 | impl Stream for SignalsInfo { 148 | type Item = E::Output; 149 | 150 | fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { 151 | match self.0.poll_signal(&mut |read| Self::has_signals(read, ctx)) { 152 | PollResult::Signal(sig) => Poll::Ready(Some(sig)), 153 | PollResult::Closed => Poll::Ready(None), 154 | PollResult::Pending => Poll::Pending, 155 | PollResult::Err(error) => panic!("Unexpected error: {}", error), 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /signal-hook-tokio/tests/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "futures-v0_3")] 2 | 3 | use futures::stream::StreamExt; 4 | 5 | use std::sync::atomic::{AtomicBool, Ordering}; 6 | use std::sync::Arc; 7 | use std::time::Duration; 8 | 9 | use signal_hook::consts::SIGUSR1; 10 | use signal_hook::low_level::raise; 11 | use signal_hook_tokio::Signals; 12 | 13 | use serial_test::serial; 14 | 15 | #[tokio::test] 16 | #[serial] 17 | async fn next_returns_recieved_signal() { 18 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 19 | raise(SIGUSR1).unwrap(); 20 | 21 | let signal = signals.next().await; 22 | 23 | assert_eq!(signal, Some(SIGUSR1)); 24 | } 25 | 26 | #[tokio::test] 27 | #[serial] 28 | async fn close_signal_stream() { 29 | let mut signals = Signals::new(&[SIGUSR1]).unwrap(); 30 | signals.handle().close(); 31 | 32 | let result = signals.next().await; 33 | 34 | assert_eq!(result, None); 35 | } 36 | 37 | #[tokio::test] 38 | #[serial] 39 | async fn delayed() { 40 | async fn get_signal(mut signals: Signals, recieved: Arc) { 41 | signals.next().await; 42 | recieved.store(true, Ordering::SeqCst); 43 | } 44 | 45 | let signals = Signals::new(&[SIGUSR1]).unwrap(); 46 | let recieved = Arc::new(AtomicBool::new(false)); 47 | 48 | let signals_task = tokio::spawn(get_signal(signals, Arc::clone(&recieved))); 49 | 50 | tokio::time::sleep(Duration::from_millis(100)).await; 51 | assert!(!recieved.load(Ordering::SeqCst)); 52 | 53 | raise(SIGUSR1).unwrap(); 54 | signals_task.await.unwrap(); 55 | assert!(recieved.load(Ordering::SeqCst)); 56 | } 57 | -------------------------------------------------------------------------------- /src/flag.rs: -------------------------------------------------------------------------------- 1 | //! Module for actions setting flags. 2 | //! 3 | //! This contains helper functions to set flags whenever a signal happens. The flags are atomic 4 | //! bools or numbers and the library manipulates them with the `SeqCst` ordering, in case someone 5 | //! cares about relative order to some *other* atomic variables. If you don't care about the 6 | //! relative order, you are free to use `Ordering::Relaxed` when reading and resetting the flags. 7 | //! 8 | //! # When to use 9 | //! 10 | //! The flags in this module allow for polling if a signal arrived since the previous poll. The do 11 | //! not allow blocking until something arrives. 12 | //! 13 | //! Therefore, the natural way to use them is in applications that have some kind of iterative work 14 | //! with both some upper and lower time limit on one iteration. If one iteration could block for 15 | //! arbitrary time, the handling of the signal would be postponed for a long time. If the iteration 16 | //! didn't block at all, the checking for the signal would turn into a busy-loop. 17 | //! 18 | //! If what you need is blocking until a signal comes, you might find better tools in the 19 | //! [`pipe`][crate::low_level::pipe] and [`iterator`][crate::iterator] modules. 20 | //! 21 | //! # Examples 22 | //! 23 | //! Doing something until terminated. This also knows by which signal it was terminated. In case 24 | //! multiple termination signals arrive before it is handled, it recognizes the last one. 25 | //! 26 | //! ```rust 27 | //! use std::io::Error; 28 | //! use std::sync::Arc; 29 | //! use std::sync::atomic::{AtomicUsize, Ordering}; 30 | //! 31 | //! use signal_hook::consts::signal::*; 32 | //! use signal_hook::flag as signal_flag; 33 | //! 34 | //! fn main() -> Result<(), Error> { 35 | //! let term = Arc::new(AtomicUsize::new(0)); 36 | //! const SIGTERM_U: usize = SIGTERM as usize; 37 | //! const SIGINT_U: usize = SIGINT as usize; 38 | //! # #[cfg(not(windows))] 39 | //! const SIGQUIT_U: usize = SIGQUIT as usize; 40 | //! signal_flag::register_usize(SIGTERM, Arc::clone(&term), SIGTERM_U)?; 41 | //! signal_flag::register_usize(SIGINT, Arc::clone(&term), SIGINT_U)?; 42 | //! # #[cfg(not(windows))] 43 | //! signal_flag::register_usize(SIGQUIT, Arc::clone(&term), SIGQUIT_U)?; 44 | //! 45 | //! # // Hack to terminate the example when run as a doc-test. 46 | //! # term.store(SIGTERM_U, Ordering::Relaxed); 47 | //! loop { 48 | //! match term.load(Ordering::Relaxed) { 49 | //! 0 => { 50 | //! // Do some useful stuff here 51 | //! } 52 | //! SIGTERM_U => { 53 | //! eprintln!("Terminating on the TERM signal"); 54 | //! break; 55 | //! } 56 | //! SIGINT_U => { 57 | //! eprintln!("Terminating on the INT signal"); 58 | //! break; 59 | //! } 60 | //! # #[cfg(not(windows))] 61 | //! SIGQUIT_U => { 62 | //! eprintln!("Terminating on the QUIT signal"); 63 | //! break; 64 | //! } 65 | //! _ => unreachable!(), 66 | //! } 67 | //! } 68 | //! 69 | //! Ok(()) 70 | //! } 71 | //! ``` 72 | //! 73 | //! Sending a signal to self and seeing it arrived (not of a practical usage on itself): 74 | //! 75 | //! ```rust 76 | //! use std::io::Error; 77 | //! use std::sync::Arc; 78 | //! use std::sync::atomic::{AtomicBool, Ordering}; 79 | //! use std::thread; 80 | //! use std::time::Duration; 81 | //! 82 | //! use signal_hook::consts::signal::*; 83 | //! use signal_hook::low_level::raise; 84 | //! 85 | //! fn main() -> Result<(), Error> { 86 | //! let got = Arc::new(AtomicBool::new(false)); 87 | //! # #[cfg(not(windows))] 88 | //! signal_hook::flag::register(SIGUSR1, Arc::clone(&got))?; 89 | //! # #[cfg(windows)] 90 | //! # signal_hook::flag::register(SIGTERM, Arc::clone(&got))?; 91 | //! # #[cfg(not(windows))] 92 | //! raise(SIGUSR1).unwrap(); 93 | //! # #[cfg(windows)] 94 | //! # raise(SIGTERM).unwrap(); 95 | //! // A sleep here, because it could run the signal handler in another thread and we may not 96 | //! // see the flag right away. This is still a hack and not guaranteed to work, it is just an 97 | //! // example! 98 | //! thread::sleep(Duration::from_secs(1)); 99 | //! assert!(got.load(Ordering::Relaxed)); 100 | //! Ok(()) 101 | //! } 102 | //! ``` 103 | //! 104 | //! Reloading a configuration on `SIGHUP` (which is a common behaviour of many UNIX daemons, 105 | //! together with reopening the log file). 106 | //! 107 | //! ```rust 108 | //! use std::io::Error; 109 | //! use std::sync::Arc; 110 | //! use std::sync::atomic::{AtomicBool, Ordering}; 111 | //! 112 | //! use signal_hook::consts::signal::*; 113 | //! use signal_hook::flag as signal_flag; 114 | //! 115 | //! fn main() -> Result<(), Error> { 116 | //! // We start with true, to load the configuration in the very first iteration too. 117 | //! let reload = Arc::new(AtomicBool::new(true)); 118 | //! let term = Arc::new(AtomicBool::new(false)); 119 | //! # #[cfg(not(windows))] 120 | //! signal_flag::register(SIGHUP, Arc::clone(&reload))?; 121 | //! signal_flag::register(SIGINT, Arc::clone(&term))?; 122 | //! signal_flag::register(SIGTERM, Arc::clone(&term))?; 123 | //! # #[cfg(not(windows))] 124 | //! signal_flag::register(SIGQUIT, Arc::clone(&term))?; 125 | //! while !term.load(Ordering::Relaxed) { 126 | //! // Using swap here, not load, to reset it back to false once it is reloaded. 127 | //! if reload.swap(false, Ordering::Relaxed) { 128 | //! // Reload the config here 129 | //! # 130 | //! # // Hiden hack to make the example terminate when run as doc-test. Not part of the 131 | //! # // real code. 132 | //! # term.store(true, Ordering::Relaxed); 133 | //! } 134 | //! // Serve one request 135 | //! } 136 | //! Ok(()) 137 | //! } 138 | //! ``` 139 | 140 | use std::io::Error; 141 | use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; 142 | use std::sync::Arc; 143 | 144 | use libc::{c_int, EINVAL}; 145 | 146 | use crate::{low_level, SigId}; 147 | 148 | /// Registers an action to set the flag to `true` whenever the given signal arrives. 149 | /// 150 | /// # Panics 151 | /// 152 | /// If the signal is one of the forbidden. 153 | pub fn register(signal: c_int, flag: Arc) -> Result { 154 | // We use SeqCst for two reasons: 155 | // * Signals should not come very often, so the performance does not really matter. 156 | // * We promise the order of actions, but setting different atomics with Relaxed or similar 157 | // would not guarantee the effective order. 158 | unsafe { low_level::register(signal, move || flag.store(true, Ordering::SeqCst)) } 159 | } 160 | 161 | /// Registers an action to set the flag to the given value whenever the signal arrives. 162 | pub fn register_usize(signal: c_int, flag: Arc, value: usize) -> Result { 163 | unsafe { low_level::register(signal, move || flag.store(value, Ordering::SeqCst)) } 164 | } 165 | 166 | /// Terminate the application on a signal if the given condition is true. 167 | /// 168 | /// This can be used for different use cases. One of them (with the condition being always true) is 169 | /// just unconditionally terminate on the given signal. 170 | /// 171 | /// Another is being able to turn on and off the behaviour by the shared flag. 172 | /// 173 | /// The last one is handling double CTRL+C ‒ if the user presses CTRL+C, we would like to start a 174 | /// graceful shutdown. But if anything ever gets stuck in the shutdown, second CTRL+C (or other 175 | /// such termination signal) should terminate the application without further delay. 176 | /// 177 | /// To do that, one can combine this with [`register`]. On the first run, the flag is `false` and 178 | /// this doesn't terminate. But then the flag is set to true during the first run and „arms“ the 179 | /// shutdown on the second run. Note that it matters in which order the actions are registered (the 180 | /// shutdown must go first). And yes, this also allows asking the user „Do you want to terminate“ 181 | /// and disarming the abrupt shutdown if the user answers „No“. 182 | /// 183 | /// # Panics 184 | /// 185 | /// If the signal is one of the forbidden. 186 | pub fn register_conditional_shutdown( 187 | signal: c_int, 188 | status: c_int, 189 | condition: Arc, 190 | ) -> Result { 191 | let action = move || { 192 | if condition.load(Ordering::SeqCst) { 193 | low_level::exit(status); 194 | } 195 | }; 196 | unsafe { low_level::register(signal, action) } 197 | } 198 | 199 | /// Conditionally runs an emulation of the default action on the given signal. 200 | /// 201 | /// If the provided condition is true at the time of invoking the signal handler, the equivalent of 202 | /// the default action of the given signal is run. It is a bit similar to 203 | /// [`register_conditional_shutdown`], except that it doesn't terminate for non-termination 204 | /// signals, it runs their default handler. 205 | /// 206 | /// # Panics 207 | /// 208 | /// If the signal is one of the forbidden 209 | /// 210 | /// # Errors 211 | /// 212 | /// Similarly to the [`emulate_default_handler`][low_level::emulate_default_handler] function, this 213 | /// one looks the signal up in a table. If it is unknown, an error is returned. 214 | /// 215 | /// Additionally to that, any errors that can be caused by a registration of a handler can happen 216 | /// too. 217 | pub fn register_conditional_default( 218 | signal: c_int, 219 | condition: Arc, 220 | ) -> Result { 221 | // Verify we know about this particular signal. 222 | low_level::signal_name(signal).ok_or_else(|| Error::from_raw_os_error(EINVAL))?; 223 | let action = move || { 224 | if condition.load(Ordering::SeqCst) { 225 | let _ = low_level::emulate_default_handler(signal); 226 | } 227 | }; 228 | unsafe { low_level::register(signal, action) } 229 | } 230 | 231 | #[cfg(test)] 232 | mod tests { 233 | use std::sync::atomic; 234 | use std::time::{Duration, Instant}; 235 | 236 | use super::*; 237 | use crate::consts::signal::*; 238 | 239 | fn self_signal() { 240 | #[cfg(not(windows))] 241 | const SIG: c_int = SIGUSR1; 242 | #[cfg(windows)] 243 | const SIG: c_int = SIGTERM; 244 | crate::low_level::raise(SIG).unwrap(); 245 | } 246 | 247 | fn wait_flag(flag: &AtomicBool) -> bool { 248 | let start = Instant::now(); 249 | while !flag.load(Ordering::Relaxed) { 250 | // Replaced by hint::spin_loop, but we want to support older compiler 251 | #[allow(deprecated)] 252 | atomic::spin_loop_hint(); 253 | if Instant::now() - start > Duration::from_secs(1) { 254 | // We reached a timeout and nothing happened yet. 255 | // In theory, using timeouts for thread-synchronization tests is wrong, but a 256 | // second should be enough in practice. 257 | return false; 258 | } 259 | } 260 | true 261 | } 262 | 263 | #[test] 264 | fn register_unregister() { 265 | // When we register the action, it is active. 266 | let flag = Arc::new(AtomicBool::new(false)); 267 | #[cfg(not(windows))] 268 | let signal = register(SIGUSR1, Arc::clone(&flag)).unwrap(); 269 | #[cfg(windows)] 270 | let signal = register(crate::SIGTERM, Arc::clone(&flag)).unwrap(); 271 | self_signal(); 272 | assert!(wait_flag(&flag)); 273 | // But stops working after it is unregistered. 274 | assert!(crate::low_level::unregister(signal)); 275 | flag.store(false, Ordering::Relaxed); 276 | self_signal(); 277 | assert!(!wait_flag(&flag)); 278 | // And the unregistration actually dropped its copy of the Arc 279 | assert_eq!(1, Arc::strong_count(&flag)); 280 | } 281 | 282 | // The shutdown is tested in tests/shutdown.rs 283 | } 284 | -------------------------------------------------------------------------------- /src/iterator/backend.rs: -------------------------------------------------------------------------------- 1 | //! A backend module for implementing the iterator like 2 | //! [`iterator`][crate::iterator] module and the asynchronous 3 | //! adapter crates. 4 | //! 5 | //! This module contains generic types which abstract over the concrete 6 | //! IO type for the self-pipe. The motivation for having this abstraction 7 | //! are the adapter crates for different asynchronous runtimes. The runtimes 8 | //! provide their own wrappers for [`std::os::unix::net::UnixStream`] 9 | //! which should be used as the internal self pipe. But large parts of the 10 | //! remaining functionality doesn't depend directly onto the IO type and can 11 | //! be reused. 12 | //! 13 | //! See also the [`SignalDelivery::with_pipe`] method for more information 14 | //! about requirements the IO types have to fulfill. 15 | //! 16 | //! As a regular user you shouldn't need to use the types in this module. 17 | //! Use the [`Signals`][crate::iterator::Signals] struct or one of the types 18 | //! contained in the adapter libraries instead. 19 | 20 | use std::borrow::{Borrow, BorrowMut}; 21 | use std::fmt::{Debug, Formatter, Result as FmtResult}; 22 | use std::io::Error; 23 | use std::mem::MaybeUninit; 24 | use std::os::unix::io::AsRawFd; 25 | use std::ptr; 26 | use std::sync::atomic::{AtomicBool, Ordering}; 27 | use std::sync::{Arc, Mutex}; 28 | 29 | use libc::{self, c_int}; 30 | 31 | use super::exfiltrator::Exfiltrator; 32 | use crate::low_level::pipe::{self, WakeMethod}; 33 | use crate::SigId; 34 | 35 | /// Maximal signal number we support. 36 | const MAX_SIGNUM: usize = 128; 37 | 38 | trait SelfPipeWrite: Debug + Send + Sync { 39 | fn wake_readers(&self); 40 | } 41 | 42 | impl SelfPipeWrite for W { 43 | fn wake_readers(&self) { 44 | pipe::wake(self.as_raw_fd(), WakeMethod::Send); 45 | } 46 | } 47 | 48 | #[derive(Debug)] 49 | struct DeliveryState { 50 | closed: AtomicBool, 51 | registered_signal_ids: Mutex>>, 52 | } 53 | 54 | impl DeliveryState { 55 | fn new() -> Self { 56 | let ids = (0..MAX_SIGNUM).map(|_| None).collect(); 57 | Self { 58 | closed: AtomicBool::new(false), 59 | registered_signal_ids: Mutex::new(ids), 60 | } 61 | } 62 | } 63 | 64 | impl Drop for DeliveryState { 65 | fn drop(&mut self) { 66 | let lock = self.registered_signal_ids.lock().unwrap(); 67 | for id in lock.iter().filter_map(|s| *s) { 68 | crate::low_level::unregister(id); 69 | } 70 | } 71 | } 72 | 73 | struct PendingSignals { 74 | exfiltrator: E, 75 | slots: [E::Storage; MAX_SIGNUM], 76 | } 77 | 78 | impl PendingSignals { 79 | fn new(exfiltrator: E) -> Self { 80 | // Unfortunately, Default is not implemented for long arrays :-( 81 | // 82 | // Note that if the default impl panics, the already existing instances are leaked. 83 | let mut slots = MaybeUninit::<[E::Storage; MAX_SIGNUM]>::uninit(); 84 | for i in 0..MAX_SIGNUM { 85 | unsafe { 86 | let slot: *mut E::Storage = slots.as_mut_ptr() as *mut _; 87 | let slot = slot.add(i); 88 | ptr::write(slot, E::Storage::default()); 89 | } 90 | } 91 | 92 | Self { 93 | exfiltrator, 94 | slots: unsafe { slots.assume_init() }, 95 | } 96 | } 97 | } 98 | 99 | /// An internal trait to hide adding new signals into a Handle behind a dynamic dispatch. 100 | trait AddSignal: Debug + Send + Sync { 101 | fn add_signal( 102 | self: Arc, 103 | write: Arc, 104 | signal: c_int, 105 | ) -> Result; 106 | } 107 | 108 | // Implemented manually because 1.36.0 doesn't yet support Debug for [X; BIG_NUMBER]. 109 | impl Debug for PendingSignals { 110 | fn fmt(&self, fmt: &mut Formatter) -> FmtResult { 111 | fmt.debug_struct("PendingSignals") 112 | .field("exfiltrator", &self.exfiltrator) 113 | // While the array does not, the slice does implement Debug 114 | .field("slots", &&self.slots[..]) 115 | .finish() 116 | } 117 | } 118 | 119 | impl AddSignal for PendingSignals { 120 | fn add_signal( 121 | self: Arc, 122 | write: Arc, 123 | signal: c_int, 124 | ) -> Result { 125 | assert!(signal >= 0); 126 | assert!( 127 | (signal as usize) < MAX_SIGNUM, 128 | "Signal number {} too large. If your OS really supports such signal, file a bug", 129 | signal, 130 | ); 131 | assert!( 132 | self.exfiltrator.supports_signal(signal), 133 | "Signal {} not supported by exfiltrator {:?}", 134 | signal, 135 | self.exfiltrator, 136 | ); 137 | self.exfiltrator.init(&self.slots[signal as usize], signal); 138 | 139 | let action = move |act: &_| { 140 | let slot = &self.slots[signal as usize]; 141 | let ex = &self.exfiltrator; 142 | ex.store(slot, signal, act); 143 | write.wake_readers(); 144 | }; 145 | let id = unsafe { signal_hook_registry::register_sigaction(signal, action) }?; 146 | Ok(id) 147 | } 148 | } 149 | 150 | /// A struct to control an instance of an associated type 151 | /// (like for example [`Signals`][super::Signals]). 152 | /// 153 | /// It allows to register more signal handlers and to shutdown the signal 154 | /// delivery. You can [`clone`][Handle::clone] this type which isn't a 155 | /// very expensive operation. The cloned instances can be shared between 156 | /// multiple threads. 157 | #[derive(Debug, Clone)] 158 | pub struct Handle { 159 | pending: Arc, 160 | write: Arc, 161 | delivery_state: Arc, 162 | } 163 | 164 | impl Handle { 165 | fn new(write: W, pending: Arc) -> Self 166 | where 167 | W: 'static + SelfPipeWrite, 168 | { 169 | Self { 170 | pending, 171 | write: Arc::new(write), 172 | delivery_state: Arc::new(DeliveryState::new()), 173 | } 174 | } 175 | 176 | /// Registers another signal to the set watched by the associated instance. 177 | /// 178 | /// # Notes 179 | /// 180 | /// * This is safe to call concurrently from whatever thread. 181 | /// * This is *not* safe to call from within a signal handler. 182 | /// * If the signal number was already registered previously, this is a no-op. 183 | /// * If this errors, the original set of signals is left intact. 184 | /// 185 | /// # Panics 186 | /// 187 | /// * If the given signal is [forbidden][crate::FORBIDDEN]. 188 | /// * If the signal number is negative or larger than internal limit. The limit should be 189 | /// larger than any supported signal the OS supports. 190 | /// * If the relevant [`Exfiltrator`] does not support this particular signal. The default 191 | /// [`SignalOnly`] one supports all signals. 192 | pub fn add_signal(&self, signal: c_int) -> Result<(), Error> { 193 | let mut lock = self.delivery_state.registered_signal_ids.lock().unwrap(); 194 | // Already registered, ignoring 195 | if lock[signal as usize].is_some() { 196 | return Ok(()); 197 | } 198 | 199 | let id = Arc::clone(&self.pending).add_signal(Arc::clone(&self.write), signal)?; 200 | 201 | lock[signal as usize] = Some(id); 202 | 203 | Ok(()) 204 | } 205 | 206 | /// Closes the associated instance. 207 | /// 208 | /// This is meant to signalize termination of the signal delivery process. 209 | /// After calling close: 210 | /// 211 | /// * [`is_closed`][Handle::is_closed] will return true. 212 | /// * All currently blocking operations of associated instances 213 | /// are interrupted and terminate. 214 | /// * Any further operations will not block. 215 | /// * Further signals may or may not be returned from the iterators. However, if any are 216 | /// returned, these are real signals that happened. 217 | /// 218 | /// The goal is to be able to shut down any background thread that handles only the signals. 219 | pub fn close(&self) { 220 | self.delivery_state.closed.store(true, Ordering::SeqCst); 221 | self.write.wake_readers(); 222 | } 223 | 224 | /// Is it closed? 225 | /// 226 | /// See [`close`][Handle::close]. 227 | pub fn is_closed(&self) -> bool { 228 | self.delivery_state.closed.load(Ordering::SeqCst) 229 | } 230 | } 231 | 232 | /// A struct for delivering received signals to the main program flow. 233 | /// The self-pipe IO type is generic. See the 234 | /// [`with_pipe`][SignalDelivery::with_pipe] method for requirements 235 | /// for the IO type. 236 | #[derive(Debug)] 237 | pub struct SignalDelivery { 238 | read: R, 239 | handle: Handle, 240 | pending: Arc>, 241 | } 242 | 243 | impl SignalDelivery 244 | where 245 | R: 'static + AsRawFd + Send + Sync, 246 | { 247 | /// Creates the `SignalDelivery` structure. 248 | /// 249 | /// The read and write arguments must be the ends of a suitable pipe type. These are used 250 | /// for communication between the signal handler and main program flow. 251 | /// 252 | /// Registers all the signals listed. The same restrictions (panics, errors) apply as with 253 | /// [`add_signal`][Handle::add_signal]. 254 | /// 255 | /// # Requirements for the pipe type 256 | /// 257 | /// * Must support [`send`](https://man7.org/linux/man-pages/man2/send.2.html) for 258 | /// asynchronously writing bytes to the write end 259 | /// * Must support [`recv`](https://man7.org/linux/man-pages/man2/recv.2.html) for 260 | /// reading bytes from the read end 261 | /// 262 | /// So UnixStream is a good choice for this. 263 | pub fn with_pipe(read: R, write: W, exfiltrator: E, signals: I) -> Result 264 | where 265 | I: IntoIterator, 266 | S: Borrow, 267 | W: 'static + AsRawFd + Debug + Send + Sync, 268 | { 269 | let pending = Arc::new(PendingSignals::new(exfiltrator)); 270 | let pending_add_signal = Arc::clone(&pending); 271 | let handle = Handle::new(write, pending_add_signal); 272 | let me = Self { 273 | read, 274 | handle, 275 | pending, 276 | }; 277 | for sig in signals { 278 | me.handle.add_signal(*sig.borrow())?; 279 | } 280 | Ok(me) 281 | } 282 | 283 | /// Get a reference to the read end of the self pipe 284 | /// 285 | /// You may use this method to register the underlying file descriptor 286 | /// with an eventing system (e. g. epoll) to get notified if there are 287 | /// bytes in the pipe. If the event system reports the file descriptor 288 | /// ready for reading you can then call [`pending`][SignalDelivery::pending] 289 | /// to get the arrived signals. 290 | pub fn get_read(&self) -> &R { 291 | &self.read 292 | } 293 | 294 | /// Get a mutable reference to the read end of the self pipe 295 | /// 296 | /// See the [`get_read`][SignalDelivery::get_read] method for some additional 297 | /// information. 298 | pub fn get_read_mut(&mut self) -> &mut R { 299 | &mut self.read 300 | } 301 | 302 | /// Drains all data from the internal self-pipe. This method will never block. 303 | fn flush(&mut self) { 304 | const SIZE: usize = 1024; 305 | let mut buff = [0u8; SIZE]; 306 | 307 | unsafe { 308 | // Draining the data in the self pipe. We ignore all errors on purpose. This 309 | // should not be something like closed file descriptor. It could EAGAIN, but 310 | // that's OK in case we say MSG_DONTWAIT. If it's EINTR, then it's OK too, 311 | // it'll only create a spurious wakeup. 312 | #[cfg(target_os = "aix")] 313 | let nowait_flag = libc::MSG_NONBLOCK; 314 | #[cfg(not(target_os = "aix"))] 315 | let nowait_flag = libc::MSG_DONTWAIT; 316 | while libc::recv( 317 | self.read.as_raw_fd(), 318 | buff.as_mut_ptr() as *mut libc::c_void, 319 | SIZE, 320 | nowait_flag, 321 | ) > 0 322 | {} 323 | } 324 | } 325 | 326 | /// Returns an iterator of already received signals. 327 | /// 328 | /// This returns an iterator over all the signal numbers of the signals received since last 329 | /// time they were read (out of the set registered by this `SignalDelivery` instance). Note 330 | /// that they are returned in arbitrary order and a signal number is returned only once even 331 | /// if it was received multiple times. 332 | /// 333 | /// This method returns immediately (does not block) and may produce an empty iterator if 334 | /// there are no signals ready. 335 | pub fn pending(&mut self) -> Pending { 336 | self.flush(); 337 | Pending::new(Arc::clone(&self.pending)) 338 | } 339 | 340 | /// Checks the reading end of the self pipe for available signals. 341 | /// 342 | /// If there are no signals available or this instance was already closed it returns 343 | /// [`Option::None`]. If there are some signals it returns a [`Pending`] 344 | /// instance wrapped inside a [`Option::Some`]. However, due to implementation details, 345 | /// this still can produce an empty iterator. 346 | /// 347 | /// This method doesn't check the reading end by itself but uses the passed in callback. 348 | /// This method blocks if and only if the callback blocks trying to read some bytes. 349 | pub fn poll_pending(&mut self, has_signals: &mut F) -> Result>, Error> 350 | where 351 | F: FnMut(&mut R) -> Result, 352 | { 353 | if self.handle.is_closed() { 354 | return Ok(None); 355 | } 356 | 357 | match has_signals(self.get_read_mut()) { 358 | Ok(false) => Ok(None), 359 | Ok(true) => Ok(Some(self.pending())), 360 | Err(err) => Err(err), 361 | } 362 | } 363 | 364 | /// Get a [`Handle`] for this `SignalDelivery` instance. 365 | /// 366 | /// This can be used to add further signals or close the whole 367 | /// signal delivery mechanism. 368 | pub fn handle(&self) -> Handle { 369 | self.handle.clone() 370 | } 371 | } 372 | 373 | /// The iterator of one batch of signals. 374 | /// 375 | /// This is returned by the [`pending`][SignalDelivery::pending] method. 376 | #[derive(Debug)] 377 | pub struct Pending { 378 | pending: Arc>, 379 | position: usize, 380 | } 381 | 382 | impl Pending { 383 | fn new(pending: Arc>) -> Self { 384 | Self { 385 | pending, 386 | position: 0, 387 | } 388 | } 389 | } 390 | 391 | impl Iterator for Pending { 392 | type Item = E::Output; 393 | 394 | fn next(&mut self) -> Option { 395 | while self.position < self.pending.slots.len() { 396 | let sig = self.position; 397 | let slot = &self.pending.slots[sig]; 398 | let result = self.pending.exfiltrator.load(slot, sig as c_int); 399 | if result.is_some() { 400 | return result; 401 | } else { 402 | self.position += 1; 403 | } 404 | } 405 | 406 | None 407 | } 408 | } 409 | 410 | /// Possible results of the [`poll_signal`][SignalIterator::poll_signal] function. 411 | pub enum PollResult { 412 | /// A signal arrived 413 | Signal(O), 414 | /// There are no signals yet but there may arrive some in the future 415 | Pending, 416 | /// The iterator was closed. There won't be any signals reported from now on. 417 | Closed, 418 | /// An error happened during polling for arrived signals. 419 | Err(Error), 420 | } 421 | 422 | /// An infinite iterator of received signals. 423 | pub struct SignalIterator { 424 | signals: SD, 425 | iter: Pending, 426 | } 427 | 428 | impl SignalIterator { 429 | /// Create a new infinite iterator for signals registered with the passed 430 | /// in [`SignalDelivery`] instance. 431 | pub fn new(mut signals: SD) -> Self 432 | where 433 | SD: BorrowMut>, 434 | R: 'static + AsRawFd + Send + Sync, 435 | { 436 | let iter = signals.borrow_mut().pending(); 437 | Self { signals, iter } 438 | } 439 | 440 | /// Return a signal if there is one or tell the caller that there is none at the moment. 441 | /// 442 | /// You have to pass in a callback which checks the underlying reading end of the pipe if 443 | /// there may be any pending signals. This callback may or may not block. If the callback 444 | /// returns [`true`] this method will try to fetch the next signal and return it as a 445 | /// [`PollResult::Signal`]. If the callback returns [`false`] the method will return 446 | /// [`PollResult::Pending`] and assume it will be called again at a later point in time. 447 | /// The callback may be called any number of times by this function. 448 | /// 449 | /// If the iterator was closed by the [`close`][Handle::close] method of the associated 450 | /// [`Handle`] this method will return [`PollResult::Closed`]. 451 | pub fn poll_signal(&mut self, has_signals: &mut F) -> PollResult 452 | where 453 | SD: BorrowMut>, 454 | R: 'static + AsRawFd + Send + Sync, 455 | F: FnMut(&mut R) -> Result, 456 | { 457 | // The loop is necessary because it is possible that a signal was already consumed 458 | // by a previous pending iterator due to the asynchronous nature of signals and 459 | // always moving to the end of the iterator before calling has_more. 460 | while !self.signals.borrow_mut().handle.is_closed() { 461 | if let Some(result) = self.iter.next() { 462 | return PollResult::Signal(result); 463 | } 464 | 465 | match self.signals.borrow_mut().poll_pending(has_signals) { 466 | Ok(Some(pending)) => self.iter = pending, 467 | Ok(None) => return PollResult::Pending, 468 | Err(err) => return PollResult::Err(err), 469 | } 470 | } 471 | 472 | PollResult::Closed 473 | } 474 | 475 | /// Get a shareable [`Handle`] for this instance. 476 | /// 477 | /// This can be used to add further signals or terminate the whole 478 | /// signal iteration using the [`close`][Handle::close] method. 479 | pub fn handle(&self) -> Handle 480 | where 481 | SD: Borrow>, 482 | R: 'static + AsRawFd + Send + Sync, 483 | { 484 | self.signals.borrow().handle() 485 | } 486 | } 487 | 488 | /// A signal iterator which consumes a [`SignalDelivery`] instance and takes 489 | /// ownership of it. 490 | pub type OwningSignalIterator = SignalIterator, E>; 491 | 492 | /// A signal iterator which takes a mutable reference to a [`SignalDelivery`] 493 | /// instance. 494 | pub type RefSignalIterator<'a, R, E> = SignalIterator<&'a mut SignalDelivery, E>; 495 | -------------------------------------------------------------------------------- /src/iterator/exfiltrator/mod.rs: -------------------------------------------------------------------------------- 1 | //! An abstraction over exfiltrating information out of signal handlers. 2 | //! 3 | //! The [`Exfiltrator`] trait provides a way to abstract the information extracted from a signal 4 | //! handler and the way it is extracted out of it. 5 | //! 6 | //! The implementations can be used to parametrize the 7 | //! [`SignalsInfo`][crate::iterator::SignalsInfo] to specify what results are returned. 8 | //! 9 | //! # Sealed 10 | //! 11 | //! Currently, the trait is sealed and all methods hidden. This is likely temporary, until some 12 | //! experience with them is gained. 13 | 14 | #[cfg(feature = "extended-siginfo")] 15 | #[cfg_attr(docsrs, doc(cfg(feature = "extended-siginfo")))] 16 | pub mod origin; 17 | pub mod raw; 18 | 19 | #[cfg(feature = "extended-siginfo")] 20 | pub use origin::WithOrigin; 21 | pub use raw::WithRawSiginfo; 22 | 23 | use std::sync::atomic::{AtomicBool, Ordering}; 24 | 25 | use libc::{c_int, siginfo_t}; 26 | 27 | mod sealed { 28 | use std::fmt::Debug; 29 | 30 | use libc::{c_int, siginfo_t}; 31 | 32 | /// The actual implementation of the [`Exfiltrator`][super::Exfiltrator]. 33 | /// 34 | /// For now, this is hidden from the public API, but the intention is to move it to a public 35 | /// place so users can implement it eventually, once we verify that it works well. 36 | /// 37 | /// # Safety 38 | /// 39 | /// The trait is unsafe as the [`Exfiltrator::store`] is called inside the signal handler and 40 | /// must be async-signal-safe. Implementing this correctly may be difficult, therefore care 41 | /// needs to be taken. One method known to work is encoding the data into an atomic variable. 42 | /// Other, less limiting approaches, will be eventually explored. 43 | pub unsafe trait Exfiltrator: Debug + Send + Sync + 'static { 44 | /// One slot for storing the data. 45 | /// 46 | /// Each signal will get its one slot of this type, independent of other signals. It can 47 | /// store the information in there inside the signal handler and will be loaded from it in 48 | /// load. 49 | /// 50 | /// Each slot is initialized to the [`Default`] value. It is expected this value represents 51 | /// „no signal delivered“ state. 52 | type Storage: Debug + Default + Send + Sync + 'static; 53 | 54 | /// The type returned to the user. 55 | type Output; 56 | 57 | /// If the given signal is supported by this specific exfiltrator. 58 | /// 59 | /// Not all information is available to all signals, therefore not all exfiltrators must 60 | /// support all signals. If `false` is returned, the user is prevented for registering such 61 | /// signal number with the given exfiltrator. 62 | fn supports_signal(&self, sig: c_int) -> bool; 63 | 64 | /// Puts the signal information inside the slot. 65 | /// 66 | /// It needs to somehow store the relevant information and the fact that a signal happened. 67 | /// 68 | /// # Warning 69 | /// 70 | /// This will be called inside the signal handler. It needs to be async-signal-safe. In 71 | /// particular, very small amount of operations are allowed in there. This namely does 72 | /// *not* include any locking nor allocation. 73 | /// 74 | /// It is also possible that multiple store methods are called concurrently; it is up to 75 | /// the implementor to deal with that. 76 | fn store(&self, slot: &Self::Storage, signal: c_int, info: &siginfo_t); 77 | 78 | /// Loads the signal information from the given slot. 79 | /// 80 | /// The method shall check if the signal happened (it may be possible to be called without 81 | /// the signal previously being delivered; it is up to the implementer to recognize it). It 82 | /// is assumed the [`Default`] value is recognized as no signal delivered. 83 | /// 84 | /// If it was delivered, the method shall extract the relevant information *and reset the 85 | /// slot* to the no signal delivered state. 86 | /// 87 | /// It shall return `Some(value)` if the signal was successfully received and `None` in 88 | /// case no signal was delivered. 89 | /// 90 | /// No blocking shall happen inside this method. It may be called concurrently with 91 | /// [`store`][Exfiltrator::store] (due to how signals work, concurrently even inside the 92 | /// same thread ‒ a `store` may „interrupt“ a call to `load`). It is up to the implementer 93 | /// to deal with that. 94 | fn load(&self, slot: &Self::Storage, signal: c_int) -> Option; 95 | 96 | /// Initialize the given slot for the given signal before the first use. 97 | /// 98 | /// This is called before the first use of the given slot (and it is annotated with the 99 | /// corresponding signal). The default does nothing, this is just an opportunity to 100 | /// allocate data lazily (this is called outside of the signal handler, so it doesn't have 101 | /// to be async-signal-safe). It will be called at most once for each slot. 102 | /// 103 | /// Note that you can rely on this being called for correctness, but not for safety (this 104 | /// crate calls it before the first use, but a user abusing the trait might not and in such 105 | /// case it is OK to eg. lose signals, but not segfault). 106 | fn init(&self, slot: &Self::Storage, signal: c_int) { 107 | // Suppress unused variable warning without putting the underscores into public 108 | // signature. 109 | let _ = slot; 110 | let _ = signal; 111 | } 112 | } 113 | } 114 | 115 | /// A trait describing what and how is extracted from signal handlers. 116 | /// 117 | /// By choosing a specific implementor as the type parameter for 118 | /// [`SignalsInfo`][crate::iterator::SignalsInfo], one can pick how much and what information is 119 | /// returned from the iterator. 120 | pub trait Exfiltrator: sealed::Exfiltrator {} 121 | 122 | impl Exfiltrator for E {} 123 | 124 | /// An [`Exfiltrator`] providing just the signal numbers. 125 | /// 126 | /// This is the basic exfiltrator for most needs. For that reason, there's the 127 | /// [`crate::iterator::Signals`] type alias, to simplify the type names for usual needs. 128 | #[derive(Clone, Copy, Debug, Default)] 129 | pub struct SignalOnly; 130 | 131 | unsafe impl sealed::Exfiltrator for SignalOnly { 132 | type Storage = AtomicBool; 133 | fn supports_signal(&self, _: c_int) -> bool { 134 | true 135 | } 136 | type Output = c_int; 137 | 138 | fn store(&self, slot: &Self::Storage, _: c_int, _: &siginfo_t) { 139 | slot.store(true, Ordering::SeqCst); 140 | } 141 | 142 | fn load(&self, slot: &Self::Storage, signal: c_int) -> Option { 143 | if slot 144 | .compare_exchange(true, false, Ordering::SeqCst, Ordering::Relaxed) 145 | .is_ok() 146 | { 147 | Some(signal) 148 | } else { 149 | None 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/iterator/exfiltrator/origin.rs: -------------------------------------------------------------------------------- 1 | //! An exfiltrator providing the process that caused the signal. 2 | //! 3 | //! The [`WithOrigin`] is an [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that 4 | //! provides the information about sending process in addition to the signal number, through the 5 | //! [`Origin`] type. 6 | //! 7 | //! See the [`WithOrigin`] example. 8 | 9 | use libc::{c_int, siginfo_t}; 10 | 11 | pub use super::raw::Slot; 12 | use super::sealed::Exfiltrator; 13 | use super::WithRawSiginfo; 14 | pub use crate::low_level::siginfo::{Origin, Process}; 15 | 16 | /// The [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that produces [`Origin`] of 17 | /// signals. 18 | /// 19 | /// # Examples 20 | /// 21 | /// ```rust 22 | /// # use signal_hook::consts::SIGUSR1; 23 | /// # use signal_hook::iterator::SignalsInfo; 24 | /// # use signal_hook::iterator::exfiltrator::WithOrigin; 25 | /// # 26 | /// # fn main() -> Result<(), std::io::Error> { 27 | /// // Subscribe to SIGUSR1, with information about the process. 28 | /// let mut signals = SignalsInfo::::new(&[SIGUSR1])?; 29 | /// 30 | /// // Send a signal to ourselves. 31 | /// let my_pid = unsafe { libc::getpid() }; 32 | /// unsafe { libc::kill(my_pid, SIGUSR1) }; 33 | /// 34 | /// // Grab the signal and look into the details. 35 | /// let received = signals.wait().next().unwrap(); 36 | /// 37 | /// assert_eq!(SIGUSR1, received.signal); 38 | /// assert_eq!(my_pid, received.process.unwrap().pid); 39 | /// # Ok(()) } 40 | /// ``` 41 | #[derive(Copy, Clone, Debug, Default)] 42 | pub struct WithOrigin(WithRawSiginfo); 43 | 44 | // Safety: We need to be async-signal-safe. We delegate to other Exfiltrator, which already is and 45 | // call a function that promises to be (Origin::extract) 46 | unsafe impl Exfiltrator for WithOrigin { 47 | type Storage = Slot; 48 | type Output = Origin; 49 | fn supports_signal(&self, signal: c_int) -> bool { 50 | self.0.supports_signal(signal) 51 | } 52 | 53 | fn store(&self, slot: &Slot, signal: c_int, info: &siginfo_t) { 54 | self.0.store(slot, signal, info) 55 | } 56 | 57 | fn load(&self, slot: &Self::Storage, signal: c_int) -> Option { 58 | self.0 59 | .load(slot, signal) 60 | .map(|info| unsafe { Origin::extract(&info) }) 61 | } 62 | 63 | fn init(&self, slot: &Self::Storage, signal: c_int) { 64 | self.0.init(slot, signal) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/iterator/exfiltrator/raw.rs: -------------------------------------------------------------------------------- 1 | //! An exfiltrator providing the raw [`siginfo_t`]. 2 | 3 | // Note on unsafety in this module: 4 | // * Implementing an unsafe trait, that one needs to ensure at least store is async-signal-safe. 5 | // That's done by delegating to the Channel (and reading an atomic pointer, but that one is 6 | // primitive op). 7 | // * A bit of juggling with atomic and raw pointers. In effect, that is just late lazy 8 | // initialization, the Slot is in line with Option would be, except that it is set atomically 9 | // during the init. Lifetime is ensured by not dropping until the Drop of the whole slot and that 10 | // is checked by taking `&mut self`. 11 | 12 | use std::sync::atomic::{AtomicPtr, Ordering}; 13 | 14 | use libc::{c_int, siginfo_t}; 15 | 16 | use super::sealed::Exfiltrator; 17 | use crate::low_level::channel::Channel; 18 | 19 | #[doc(hidden)] 20 | #[derive(Default, Debug)] 21 | pub struct Slot(AtomicPtr>); 22 | 23 | impl Drop for Slot { 24 | fn drop(&mut self) { 25 | let ptr = self.0.load(Ordering::Acquire); 26 | if !ptr.is_null() { 27 | drop(unsafe { Box::from_raw(ptr) }); 28 | } 29 | } 30 | } 31 | 32 | /// The [`Exfiltrator`][crate::iterator::exfiltrator::Exfiltrator] that produces the raw 33 | /// [`libc::siginfo_t`]. Note that it might look differently on different OSes and its API is a 34 | /// little bit more limited than its C counterpart. 35 | /// 36 | /// You might prefer the [`WithOrigin`][super::WithOrigin] if you simply need information about the 37 | /// origin of the signal. 38 | /// 39 | /// # Examples 40 | /// 41 | /// ```rust 42 | /// # use signal_hook::consts::SIGUSR1; 43 | /// # use signal_hook::iterator::SignalsInfo; 44 | /// # use signal_hook::iterator::exfiltrator::WithRawSiginfo; 45 | /// # 46 | /// # fn main() -> Result<(), std::io::Error> { 47 | /// // Subscribe to SIGUSR1, with information about the process. 48 | /// let mut signals = SignalsInfo::::new(&[SIGUSR1])?; 49 | /// 50 | /// // Send ourselves a signal. 51 | /// signal_hook::low_level::raise(SIGUSR1)?; 52 | /// 53 | /// // Grab the signal and look into the details. 54 | /// let received = signals.wait().next().unwrap(); 55 | /// 56 | /// // Not much else is useful in a cross-platform way :-( 57 | /// assert_eq!(SIGUSR1, received.si_signo); 58 | /// # Ok(()) } 59 | /// ``` 60 | #[derive(Copy, Clone, Debug, Default)] 61 | pub struct WithRawSiginfo; 62 | 63 | unsafe impl Exfiltrator for WithRawSiginfo { 64 | type Storage = Slot; 65 | type Output = siginfo_t; 66 | 67 | fn supports_signal(&self, _: c_int) -> bool { 68 | true 69 | } 70 | 71 | fn store(&self, slot: &Slot, _: c_int, info: &siginfo_t) { 72 | let info = *info; 73 | // Condition just not to crash if someone forgot to call init. 74 | // 75 | // Lifetime is from init to our own drop, and drop needs &mut self. 76 | if let Some(slot) = unsafe { slot.0.load(Ordering::Acquire).as_ref() } { 77 | slot.send(info); 78 | } 79 | } 80 | 81 | fn load(&self, slot: &Slot, _: libc::c_int) -> Option { 82 | let slot = unsafe { slot.0.load(Ordering::Acquire).as_ref() }; 83 | // Condition just not to crash if someone forgot to call init. 84 | slot.and_then(|s| s.recv()) 85 | } 86 | 87 | fn init(&self, slot: &Self::Storage, _: c_int) { 88 | let new = Box::default(); 89 | let old = slot.0.swap(Box::into_raw(new), Ordering::Release); 90 | // We leak the pointer on purpose here. This is invalid state anyway and must not happen, 91 | // but if it still does, we can't drop that while some other thread might still be having 92 | // the raw pointer. 93 | assert!(old.is_null(), "Init called multiple times"); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/iterator/mod.rs: -------------------------------------------------------------------------------- 1 | //! An iterator over incoming signals. 2 | //! 3 | //! This provides a higher abstraction over the signals, providing 4 | //! the [`SignalsInfo`] structure which is able to iterate over the 5 | //! incoming signals. The structure is parametrized by an 6 | //! [`Exfiltrator`][self::exfiltrator::Exfiltrator], which specifies what information is returned 7 | //! for each delivered signal. Note that some exfiltrators are behind a feature flag. 8 | //! 9 | //! The [`Signals`] is a type alias for the common case when it is enough to get the signal number. 10 | //! 11 | //! This module (and everything in it) is turned by the `iterator` feature. It is **on** by 12 | //! default, the possibility to turn off is mostly possible for very special purposes (compiling on 13 | //! ` Result<(), Error> { 28 | //! let mut signals = Signals::new(&[ 29 | //! SIGHUP, 30 | //! SIGTERM, 31 | //! SIGINT, 32 | //! SIGQUIT, 33 | //! # SIGUSR1, 34 | //! ])?; 35 | //! # // A trick to terminate the example when run as doc-test. Not part of the real code. 36 | //! # signal_hook::low_level::raise(SIGUSR1).unwrap(); 37 | //! 'outer: loop { 38 | //! // Pick up signals that arrived since last time 39 | //! for signal in signals.pending() { 40 | //! match signal as libc::c_int { 41 | //! SIGHUP => { 42 | //! // Reload configuration 43 | //! // Reopen the log file 44 | //! } 45 | //! SIGTERM | SIGINT | SIGQUIT => { 46 | //! break 'outer; 47 | //! }, 48 | //! # SIGUSR1 => return Ok(()), 49 | //! _ => unreachable!(), 50 | //! } 51 | //! } 52 | //! // Do some bit of work ‒ something with upper limit on waiting, so we don't block 53 | //! // forever with a SIGTERM already waiting. 54 | //! } 55 | //! println!("Terminating. Bye bye"); 56 | //! Ok(()) 57 | //! } 58 | //! ``` 59 | 60 | pub mod backend; 61 | pub mod exfiltrator; 62 | 63 | use std::borrow::Borrow; 64 | use std::fmt::{Debug, Formatter, Result as FmtResult}; 65 | use std::io::{Error, ErrorKind, Read}; 66 | use std::os::unix::net::UnixStream; 67 | 68 | use libc::{self, c_int}; 69 | 70 | pub use self::backend::{Handle, Pending}; 71 | use self::backend::{PollResult, RefSignalIterator, SignalDelivery}; 72 | use self::exfiltrator::{Exfiltrator, SignalOnly}; 73 | 74 | /// The main structure of the module, representing interest in some signals. 75 | /// 76 | /// Unlike the helpers in other modules, this registers the signals when created and unregisters 77 | /// them on drop. It provides the pending signals during its lifetime, either in batches or as an 78 | /// infinite iterator. 79 | /// 80 | /// Most users will want to use it through the [`Signals`] type alias for simplicity. 81 | /// 82 | /// # Multiple threads 83 | /// 84 | /// Instances of this struct can be [sent][std::marker::Send] to other threads. In a multithreaded 85 | /// application this can be used to dedicate a separate thread for signal handling. In this case 86 | /// you should get a [`Handle`] using the [`handle`][Signals::handle] method before sending the 87 | /// `Signals` instance to a background thread. With the handle you will be able to shut down the 88 | /// background thread later, or to operatively add more signals. 89 | /// 90 | /// The controller handle can be shared between as many threads as you like using its 91 | /// [`clone`][Handle::clone] method. 92 | /// 93 | /// # Exfiltrators 94 | /// 95 | /// The [`SignalOnly`] provides only the signal number. There are further exfiltrators available in 96 | /// the [`exfiltrator`] module. Note that some of them are behind feature flags that need to be 97 | /// enabled. 98 | /// 99 | /// # Examples 100 | /// 101 | /// ```rust 102 | /// # extern crate signal_hook; 103 | /// # 104 | /// # use std::io::Error; 105 | /// # use std::thread; 106 | /// use signal_hook::consts::signal::*; 107 | /// use signal_hook::iterator::Signals; 108 | /// 109 | /// # 110 | /// # fn main() -> Result<(), Error> { 111 | /// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?; 112 | /// let handle = signals.handle(); 113 | /// let thread = thread::spawn(move || { 114 | /// for signal in &mut signals { 115 | /// match signal { 116 | /// SIGUSR1 => {}, 117 | /// SIGUSR2 => {}, 118 | /// _ => unreachable!(), 119 | /// } 120 | /// } 121 | /// }); 122 | /// 123 | /// // Some time later... 124 | /// handle.close(); 125 | /// thread.join().unwrap(); 126 | /// # Ok(()) 127 | /// # } 128 | /// ``` 129 | pub struct SignalsInfo(SignalDelivery); 130 | 131 | impl SignalsInfo { 132 | /// Creates the `Signals` structure. 133 | /// 134 | /// This registers all the signals listed. The same restrictions (panics, errors) apply as 135 | /// for the [`Handle::add_signal`] method. 136 | pub fn new(signals: I) -> Result 137 | where 138 | I: IntoIterator, 139 | S: Borrow, 140 | E: Default, 141 | { 142 | Self::with_exfiltrator(signals, E::default()) 143 | } 144 | 145 | /// An advanced constructor with explicit [`Exfiltrator`]. 146 | pub fn with_exfiltrator(signals: I, exfiltrator: E) -> Result 147 | where 148 | I: IntoIterator, 149 | S: Borrow, 150 | { 151 | let (read, write) = UnixStream::pair()?; 152 | Ok(SignalsInfo(SignalDelivery::with_pipe( 153 | read, 154 | write, 155 | exfiltrator, 156 | signals, 157 | )?)) 158 | } 159 | 160 | /// Registers another signal to the set watched by this [`Signals`] instance. 161 | /// 162 | /// The same restrictions (panics, errors) apply as for the [`Handle::add_signal`] 163 | /// method. 164 | pub fn add_signal(&self, signal: c_int) -> Result<(), Error> { 165 | self.handle().add_signal(signal) 166 | } 167 | 168 | /// Returns an iterator of already received signals. 169 | /// 170 | /// This returns an iterator over all the signal numbers of the signals received since last 171 | /// time they were read (out of the set registered by this `Signals` instance). Note that they 172 | /// are returned in arbitrary order and a signal instance may returned only once even if it was 173 | /// received multiple times. 174 | /// 175 | /// This method returns immediately (does not block) and may produce an empty iterator if there 176 | /// are no signals ready. 177 | pub fn pending(&mut self) -> Pending { 178 | self.0.pending() 179 | } 180 | 181 | /// Block until the stream contains some bytes. 182 | /// 183 | /// Returns true if it was possible to read a byte and false otherwise. 184 | fn has_signals(read: &mut UnixStream) -> Result { 185 | loop { 186 | match read.read(&mut [0u8]) { 187 | Ok(num_read) => break Ok(num_read > 0), 188 | // If we get an EINTR error it is fine to retry reading from the stream. 189 | // Otherwise we should pass on the error to the caller. 190 | Err(error) => { 191 | if error.kind() != ErrorKind::Interrupted { 192 | break Err(error); 193 | } 194 | } 195 | } 196 | } 197 | } 198 | 199 | /// Waits for some signals to be available and returns an iterator. 200 | /// 201 | /// This is similar to [`pending`][SignalsInfo::pending]. If there are no signals available, it 202 | /// tries to wait for some to arrive. However, due to implementation details, this still can 203 | /// produce an empty iterator. 204 | /// 205 | /// This can block for arbitrary long time. If the [`Handle::close`] method is used in 206 | /// another thread this method will return immediately. 207 | /// 208 | /// Note that the blocking is done in this method, not in the iterator. 209 | pub fn wait(&mut self) -> Pending { 210 | match self.0.poll_pending(&mut Self::has_signals) { 211 | Ok(Some(pending)) => pending, 212 | // Because of the blocking has_signals method the poll_pending method 213 | // only returns None if the instance is closed. But we want to return 214 | // a possibly empty pending object anyway. 215 | Ok(None) => self.pending(), 216 | // Users can't manipulate the internal file descriptors and the way we use them 217 | // shouldn't produce any errors. So it is OK to panic. 218 | Err(error) => panic!("Unexpected error: {}", error), 219 | } 220 | } 221 | 222 | /// Is it closed? 223 | /// 224 | /// See [`close`][Handle::close]. 225 | pub fn is_closed(&self) -> bool { 226 | self.handle().is_closed() 227 | } 228 | 229 | /// Get an infinite iterator over arriving signals. 230 | /// 231 | /// The iterator's `next()` blocks as necessary to wait for signals to arrive. This is adequate 232 | /// if you want to designate a thread solely to handling signals. If multiple signals come at 233 | /// the same time (between two values produced by the iterator), they will be returned in 234 | /// arbitrary order. Multiple instances of the same signal may be collated. 235 | /// 236 | /// This is also the iterator returned by `IntoIterator` implementation on `&mut Signals`. 237 | /// 238 | /// This iterator terminates only if explicitly [closed][Handle::close]. 239 | /// 240 | /// # Examples 241 | /// 242 | /// ```rust 243 | /// # extern crate libc; 244 | /// # extern crate signal_hook; 245 | /// # 246 | /// # use std::io::Error; 247 | /// # use std::thread; 248 | /// # 249 | /// use signal_hook::consts::signal::*; 250 | /// use signal_hook::iterator::Signals; 251 | /// 252 | /// # fn main() -> Result<(), Error> { 253 | /// let mut signals = Signals::new(&[SIGUSR1, SIGUSR2])?; 254 | /// let handle = signals.handle(); 255 | /// thread::spawn(move || { 256 | /// for signal in signals.forever() { 257 | /// match signal { 258 | /// SIGUSR1 => {}, 259 | /// SIGUSR2 => {}, 260 | /// _ => unreachable!(), 261 | /// } 262 | /// } 263 | /// }); 264 | /// handle.close(); 265 | /// # Ok(()) 266 | /// # } 267 | /// ``` 268 | pub fn forever(&mut self) -> Forever { 269 | Forever(RefSignalIterator::new(&mut self.0)) 270 | } 271 | 272 | /// Get a shareable handle to a [`Handle`] for this instance. 273 | /// 274 | /// This can be used to add further signals or close the [`Signals`] instance. 275 | pub fn handle(&self) -> Handle { 276 | self.0.handle() 277 | } 278 | } 279 | 280 | impl Debug for SignalsInfo 281 | where 282 | E: Debug + Exfiltrator, 283 | E::Storage: Debug, 284 | { 285 | fn fmt(&self, fmt: &mut Formatter) -> FmtResult { 286 | fmt.debug_tuple("Signals").field(&self.0).finish() 287 | } 288 | } 289 | 290 | impl<'a, E: Exfiltrator> IntoIterator for &'a mut SignalsInfo { 291 | type Item = E::Output; 292 | type IntoIter = Forever<'a, E>; 293 | fn into_iter(self) -> Self::IntoIter { 294 | self.forever() 295 | } 296 | } 297 | 298 | /// An infinite iterator of arriving signals. 299 | pub struct Forever<'a, E: Exfiltrator>(RefSignalIterator<'a, UnixStream, E>); 300 | 301 | impl<'a, E: Exfiltrator> Iterator for Forever<'a, E> { 302 | type Item = E::Output; 303 | 304 | fn next(&mut self) -> Option { 305 | loop { 306 | match self.0.poll_signal(&mut SignalsInfo::::has_signals) { 307 | PollResult::Signal(result) => break Some(result), 308 | PollResult::Closed => break None, 309 | // In theory, the poll_signal should not return PollResult::Pending. Nevertheless, 310 | // there's a race condition - if the other side closes the pipe/socket after 311 | // checking for it being closed, then the `read` there returns 0 as EOF. That 312 | // appears as pending here. Next time we should get Closed. 313 | PollResult::Pending => continue, 314 | // Users can't manipulate the internal file descriptors and the way we use them 315 | // shouldn't produce any errors. So it is OK to panic. 316 | PollResult::Err(error) => panic!("Unexpected error: {}", error), 317 | } 318 | } 319 | } 320 | } 321 | 322 | /// A type alias for an iterator returning just the signal numbers. 323 | /// 324 | /// This is the simplified version for most of the use cases. For advanced usages, the 325 | /// [`SignalsInfo`] with explicit [`Exfiltrator`] type can be used. 326 | pub type Signals = SignalsInfo; 327 | -------------------------------------------------------------------------------- /src/low_level/channel.rs: -------------------------------------------------------------------------------- 1 | //! A restricted channel to pass data from signal handler. 2 | //! 3 | //! When trying to communicate data from signal handler to the outside world, one can use an atomic 4 | //! variable (as it doesn't lock, so it can be made async-signal-safe). But this won't work for 5 | //! larger data. 6 | //! 7 | //! This module provides a channel that can be used for that purpose. It is used by certain 8 | //! [exfiltrators][crate::iterator::exfiltrator], but can be used as building block for custom 9 | //! actions. In general, this is not a ready-made end-user API. 10 | //! 11 | //! # How does it work 12 | //! 13 | //! Each channel has a fixed number of slots and two queues (one for empty slots, one for full 14 | //! slots). A signal handler takes a slot out of the empty one, fills it and passes it into the 15 | //! full one. Outside of signal handler, it can take the value out of the full queue and return the 16 | //! slot to the empty queue. 17 | //! 18 | //! The queues are implemented as bit-encoded indexes of the slots in the storage. The bits are 19 | //! stored in an atomic variable. 20 | //! 21 | //! Note that the algorithm allows for a slot to be in neither queue (when it is being emptied or 22 | //! filled). 23 | //! 24 | //! # Fallible allocation of a slot 25 | //! 26 | //! It is apparent that allocation of a new slot can fail (there's nothing in the empty slot). In 27 | //! such case, there's no way to send the new value out of the handler (there's no way to safely 28 | //! wait for a slot to appear, because the handler can be blocking the thread that is responsible 29 | //! for emptying them). But that's considered acceptable ‒ even the kernel collates the same kinds 30 | //! of signals together if they are not consumed by application fast enough and there are no free 31 | //! slots exactly because some are being filled, emptied or are full ‒ in particular, the whole 32 | //! system will yield a signal. 33 | //! 34 | //! This assumes that separate signals don't share the same buffer and that there's only one reader 35 | //! (using multiple readers is still safe, but it is possible that all slots would be inside the 36 | //! readers, but already empty, so the above argument would not hold). 37 | 38 | // TODO: Other sizes? Does anyone need more than 5 slots? 39 | 40 | use std::cell::UnsafeCell; 41 | use std::sync::atomic::{AtomicU16, Ordering}; 42 | 43 | const SLOTS: usize = 5; 44 | const BITS: u16 = 3; 45 | const MASK: u16 = 0b111; 46 | 47 | fn get(n: u16, idx: u16) -> u16 { 48 | (n >> (BITS * idx)) & MASK 49 | } 50 | 51 | fn set(n: u16, idx: u16, v: u16) -> u16 { 52 | let v = v << (BITS * idx); 53 | let mask = MASK << (BITS * idx); 54 | (n & !mask) | v 55 | } 56 | 57 | fn enqueue(q: &AtomicU16, val: u16) { 58 | let mut current = q.load(Ordering::Relaxed); 59 | loop { 60 | let empty = (0..SLOTS as u16) 61 | .find(|i| get(current, *i) == 0) 62 | .expect("No empty slot available"); 63 | let modified = set(current, empty, val); 64 | match q.compare_exchange_weak(current, modified, Ordering::Release, Ordering::Relaxed) { 65 | Ok(_) => break, 66 | Err(changed) => current = changed, // And retry with the changed value 67 | } 68 | } 69 | } 70 | 71 | fn dequeue(q: &AtomicU16) -> Option { 72 | let mut current = q.load(Ordering::Relaxed); 73 | loop { 74 | let val = current & MASK; 75 | // It's completely empty 76 | if val == 0 { 77 | break None; 78 | } 79 | let modified = current >> BITS; 80 | match q.compare_exchange_weak(current, modified, Ordering::Acquire, Ordering::Relaxed) { 81 | Ok(_) => break Some(val), 82 | Err(changed) => current = changed, 83 | } 84 | } 85 | } 86 | 87 | /// A restricted async-signal-safe channel 88 | /// 89 | /// This is a bit like the usual channel used for inter-thread communication, but with several 90 | /// restrictions: 91 | /// 92 | /// * There's a limited number of slots (currently 5). 93 | /// * There's no way to wait for a place in it or for a value. If value is not available, `None` is 94 | /// returned. If there's no space for a value, the value is silently dropped. 95 | /// 96 | /// In exchange for that, all the operations on that channel are async-signal-safe. That means it 97 | /// is possible to use it to communicate between a signal handler and the rest of the world with it 98 | /// (specifically, it's designed to send information from the handler to the rest of the 99 | /// application). The throwing out of values when full is in line with collating of the same type 100 | /// in kernel (you should not use the same channel for multiple different signals). 101 | /// 102 | /// Technically, this is a MPMC queue which preserves order, but it is expected to be used in MPSC 103 | /// mode mostly (in theory, multiple threads can be executing a signal handler for the same signal 104 | /// at the same time). The channel is not responsible for wakeups. 105 | /// 106 | /// While the channel is async-signal-safe, you still need to make sure *creating* of the values is 107 | /// too (it should not contain anything that allocates, for example ‒ so no `String`s inside, etc). 108 | /// 109 | /// The code was *not* tuned for performance (signals are not expected to happen often). 110 | pub struct Channel { 111 | storage: [UnsafeCell>; SLOTS], 112 | empty: AtomicU16, 113 | full: AtomicU16, 114 | } 115 | 116 | impl Channel { 117 | /// Creates a new channel with nothing in it. 118 | pub fn new() -> Self { 119 | let storage = Default::default(); 120 | let me = Self { 121 | storage, 122 | empty: AtomicU16::new(0), 123 | full: AtomicU16::new(0), 124 | }; 125 | 126 | for i in 1..SLOTS + 1 { 127 | enqueue(&me.empty, i as u16); 128 | } 129 | 130 | me 131 | } 132 | 133 | /// Inserts a value into the channel. 134 | /// 135 | /// If the value doesn't fit, it is silently dropped. Never blocks. 136 | pub fn send(&self, val: T) { 137 | if let Some(empty_idx) = dequeue(&self.empty) { 138 | unsafe { *self.storage[empty_idx as usize - 1].get() = Some(val) }; 139 | enqueue(&self.full, empty_idx); 140 | } 141 | } 142 | 143 | /// Takes a value from the channel. 144 | /// 145 | /// Or returns `None` if the channel is empty. Never blocks. 146 | pub fn recv(&self) -> Option { 147 | dequeue(&self.full).map(|idx| { 148 | let result = unsafe { &mut *self.storage[idx as usize - 1].get() } 149 | .take() 150 | .expect("Full slot with nothing in it"); 151 | enqueue(&self.empty, idx); 152 | result 153 | }) 154 | } 155 | } 156 | 157 | impl Default for Channel { 158 | fn default() -> Self { 159 | Self::new() 160 | } 161 | } 162 | 163 | unsafe impl Send for Channel {} 164 | 165 | // Yes, really Send -> Sync. Having a reference to Channel allows Sending Ts, but not having refs 166 | // on them. 167 | unsafe impl Sync for Channel {} 168 | 169 | #[cfg(test)] 170 | mod tests { 171 | use std::sync::Arc; 172 | use std::thread; 173 | 174 | use super::*; 175 | 176 | #[test] 177 | fn new_empty() { 178 | let channel = Channel::::new(); 179 | assert!(channel.recv().is_none()); 180 | assert!(channel.recv().is_none()); 181 | } 182 | 183 | #[test] 184 | fn pass_value() { 185 | let channel = Channel::new(); 186 | channel.send(42); 187 | assert_eq!(42, channel.recv().unwrap()); 188 | assert!(channel.recv().is_none()); 189 | } 190 | 191 | #[test] 192 | fn multiple() { 193 | let channel = Channel::new(); 194 | for i in 0..1000 { 195 | channel.send(i); 196 | assert_eq!(i, channel.recv().unwrap()); 197 | assert!(channel.recv().is_none()); 198 | } 199 | } 200 | 201 | #[test] 202 | fn overflow() { 203 | let channel = Channel::new(); 204 | for i in 0..10 { 205 | channel.send(i); 206 | } 207 | for i in 0..5 { 208 | assert_eq!(i, channel.recv().unwrap()); 209 | } 210 | assert!(channel.recv().is_none()); 211 | } 212 | 213 | #[test] 214 | fn multi_thread() { 215 | let channel = Arc::new(Channel::::new()); 216 | 217 | let sender = thread::spawn({ 218 | let channel = Arc::clone(&channel); 219 | move || { 220 | for i in 0..4 { 221 | channel.send(i); 222 | } 223 | } 224 | }); 225 | 226 | let mut results = Vec::new(); 227 | while results.len() < 4 { 228 | results.extend(channel.recv()); 229 | } 230 | 231 | assert_eq!(vec![0, 1, 2, 3], results); 232 | 233 | sender.join().unwrap(); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/low_level/extract.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Low-level extraction code to overcome rust's libc not having the best access 3 | * to siginfo_t details. 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | struct Const { 10 | int native; 11 | // The signal this applies to, or -1 if it applies to anything. 12 | int signal; 13 | uint8_t translated; 14 | }; 15 | 16 | // Warning: must be in sync with the rust source code 17 | struct Const consts[] = { 18 | #ifdef SI_KERNEL 19 | { SI_KERNEL, -1, 1 }, 20 | #endif 21 | { SI_USER, -1, 2 }, 22 | #ifdef SI_TKILL 23 | { SI_TKILL, -1, 3 }, 24 | #endif 25 | { SI_QUEUE, -1, 4 }, 26 | #ifdef SI_MESGQ 27 | { SI_MESGQ, -1, 5 }, 28 | #endif 29 | { CLD_EXITED, SIGCHLD, 6 }, 30 | { CLD_KILLED, SIGCHLD, 7 }, 31 | { CLD_DUMPED, SIGCHLD, 8 }, 32 | { CLD_TRAPPED, SIGCHLD, 9 }, 33 | { CLD_STOPPED, SIGCHLD, 10 }, 34 | { CLD_CONTINUED, SIGCHLD, 11 }, 35 | }; 36 | 37 | uint8_t sighook_signal_cause(const siginfo_t *info) { 38 | const size_t const_len = sizeof consts / sizeof *consts; 39 | size_t i; 40 | for (i = 0; i < const_len; i ++) { 41 | if ( 42 | consts[i].native == info->si_code && 43 | (consts[i].signal == -1 || consts[i].signal == info->si_signo) 44 | ) { 45 | return consts[i].translated; 46 | } 47 | } 48 | return 0; // The "Unknown" variant 49 | } 50 | 51 | pid_t sighook_signal_pid(const siginfo_t *info) { 52 | return info->si_pid; 53 | } 54 | 55 | uid_t sighook_signal_uid(const siginfo_t *info) { 56 | return info->si_uid; 57 | } 58 | -------------------------------------------------------------------------------- /src/low_level/mod.rs: -------------------------------------------------------------------------------- 1 | //! Some low level utilities 2 | //! 3 | //! More often to build other abstractions than used directly. 4 | 5 | use std::io::Error; 6 | 7 | use libc::c_int; 8 | 9 | #[cfg(feature = "channel")] 10 | #[cfg_attr(docsrs, doc(cfg(feature = "channel")))] 11 | pub mod channel; 12 | #[cfg(not(windows))] 13 | #[cfg_attr(docsrs, doc(cfg(not(windows))))] 14 | pub mod pipe; 15 | #[cfg(feature = "extended-siginfo-raw")] 16 | #[cfg_attr(docsrs, doc(cfg(feature = "extended-siginfo-raw")))] 17 | pub mod siginfo; 18 | mod signal_details; 19 | 20 | pub use signal_hook_registry::{register, unregister}; 21 | 22 | pub use self::signal_details::{emulate_default_handler, signal_name}; 23 | 24 | /// The usual raise, just the safe wrapper around it. 25 | /// 26 | /// This is async-signal-safe. 27 | pub fn raise(sig: c_int) -> Result<(), Error> { 28 | let result = unsafe { libc::raise(sig) }; 29 | if result == -1 { 30 | Err(Error::last_os_error()) 31 | } else { 32 | Ok(()) 33 | } 34 | } 35 | 36 | /// A bare libc abort. 37 | /// 38 | /// Unlike the [std::process::abort], this one is guaranteed to contain no additions or wrappers 39 | /// and therefore is async-signal-safe. You can use this to terminate the application from within a 40 | /// signal handler. 41 | pub fn abort() -> ! { 42 | unsafe { 43 | libc::abort(); 44 | } 45 | } 46 | 47 | /// A bare libc exit. 48 | /// 49 | /// Unlike the [std::process::exit], this one is guaranteed to contain no additions or wrappers and 50 | /// therefore is async-signal-safe. You can use this to terminate the application from within a 51 | /// signal handler. 52 | /// 53 | /// Also, see [`register_conditional_shutdown`][crate::flag::register_conditional_shutdown]. 54 | pub fn exit(status: c_int) -> ! { 55 | unsafe { 56 | // Yes, the one with underscore. That one doesn't call the at-exit hooks. 57 | libc::_exit(status); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/low_level/pipe.rs: -------------------------------------------------------------------------------- 1 | //! Module with the self-pipe pattern. 2 | //! 3 | //! One of the common patterns around signals is to have a pipe with both ends in the same program. 4 | //! Whenever there's a signal, the signal handler writes one byte of garbage data to the write end, 5 | //! unless the pipe's already full. The application then can handle the read end. 6 | //! 7 | //! This has two advantages. First, the real signal action moves outside of the signal handler 8 | //! where there are a lot less restrictions. Second, it fits nicely in all kinds of asynchronous 9 | //! loops and has less chance of race conditions. 10 | //! 11 | //! This module offers premade functions for the write end (and doesn't insist that it must be a 12 | //! pipe ‒ anything that can be written to is fine ‒ sockets too, therefore `UnixStream::pair` is a 13 | //! good candidate). 14 | //! 15 | //! If you want to integrate with some asynchronous library, plugging streams from `mio-uds` or 16 | //! `tokio-uds` libraries should work. 17 | //! 18 | //! If it looks too low-level for your needs, the [`iterator`][crate::iterator] module contains some 19 | //! higher-lever interface that also uses a self-pipe pattern under the hood. 20 | //! 21 | //! # Correct order of handling 22 | //! 23 | //! A care needs to be taken to avoid race conditions, especially when handling the same signal in 24 | //! a loop. Specifically, another signal might come when the action for the previous signal is 25 | //! being taken. The correct order is first to clear the content of the pipe (read some/all data 26 | //! from it) and then take the action. This way a spurious wakeup can happen (the pipe could wake 27 | //! up even when no signal came after the signal was taken, because ‒ it arrived between cleaning 28 | //! the pipe and taking the action). Note that some OS primitives (eg. `select`) suffer from 29 | //! spurious wakeups themselves (they can claim a FD is readable when it is not true) and blocking 30 | //! `read` might return prematurely (with eg. `EINTR`). 31 | //! 32 | //! The reverse order of first taking the action and then clearing the pipe might lose signals, 33 | //! which is usually worse. 34 | //! 35 | //! This is not a problem with blocking on reading from the pipe (because both the blocking and 36 | //! cleaning is the same action), but in case of asynchronous handling it matters. 37 | //! 38 | //! If you want to combine setting some flags with a self-pipe pattern, the flag needs to be set 39 | //! first, then the pipe written. On the read end, first the pipe needs to be cleaned, then the 40 | //! flag and then the action taken. This is what the [`SignalsInfo`][crate::iterator::SignalsInfo] 41 | //! structure does internally. 42 | //! 43 | //! # Write collating 44 | //! 45 | //! While unlikely if handled correctly, it is possible the write end is full when a signal comes. 46 | //! In such case the signal handler simply does nothing. If the write end is full, the read end is 47 | //! readable and therefore will wake up. On the other hand, blocking in the signal handler would 48 | //! definitely be a bad idea. 49 | //! 50 | //! However, this also means the number of bytes read from the end might be lower than the number 51 | //! of signals that arrived. This should not generally be a problem, since the OS already collates 52 | //! signals of the same kind together. 53 | //! 54 | //! # Examples 55 | //! 56 | //! This example waits for at last one `SIGUSR1` signal to come before continuing (and 57 | //! terminating). It sends the signal to itself, so it correctly terminates. 58 | //! 59 | //! ```rust 60 | //! use std::io::{Error, Read}; 61 | //! use std::os::unix::net::UnixStream; 62 | //! 63 | //! use signal_hook::consts::SIGUSR1; 64 | //! use signal_hook::low_level::{pipe, raise}; 65 | //! 66 | //! fn main() -> Result<(), Error> { 67 | //! let (mut read, write) = UnixStream::pair()?; 68 | //! pipe::register(SIGUSR1, write)?; 69 | //! // This will write into the pipe write end through the signal handler 70 | //! raise(SIGUSR1).unwrap(); 71 | //! let mut buff = [0]; 72 | //! read.read_exact(&mut buff)?; 73 | //! println!("Happily terminating"); 74 | //! Ok(()) 75 | //! } 76 | //! ``` 77 | 78 | use std::io::{Error, ErrorKind}; 79 | use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd}; 80 | 81 | use libc::{self, c_int}; 82 | 83 | use crate::SigId; 84 | 85 | #[cfg(target_os = "aix")] 86 | const MSG_NOWAIT: i32 = libc::MSG_NONBLOCK; 87 | #[cfg(not(target_os = "aix"))] 88 | const MSG_NOWAIT: i32 = libc::MSG_DONTWAIT; 89 | 90 | #[derive(Copy, Clone)] 91 | pub(crate) enum WakeMethod { 92 | Send, 93 | Write, 94 | } 95 | 96 | struct WakeFd { 97 | fd: RawFd, 98 | method: WakeMethod, 99 | } 100 | 101 | impl WakeFd { 102 | /// Sets close on exec and nonblock on the inner file descriptor. 103 | fn set_flags(&self) -> Result<(), Error> { 104 | unsafe { 105 | let flags = libc::fcntl(self.as_raw_fd(), libc::F_GETFL, 0); 106 | if flags == -1 { 107 | return Err(Error::last_os_error()); 108 | } 109 | let flags = flags | libc::O_NONBLOCK | libc::O_CLOEXEC; 110 | if libc::fcntl(self.as_raw_fd(), libc::F_SETFL, flags) == -1 { 111 | return Err(Error::last_os_error()); 112 | } 113 | } 114 | Ok(()) 115 | } 116 | fn wake(&self) { 117 | wake(self.fd, self.method); 118 | } 119 | } 120 | 121 | impl AsRawFd for WakeFd { 122 | fn as_raw_fd(&self) -> RawFd { 123 | self.fd 124 | } 125 | } 126 | 127 | impl Drop for WakeFd { 128 | fn drop(&mut self) { 129 | unsafe { 130 | libc::close(self.fd); 131 | } 132 | } 133 | } 134 | 135 | pub(crate) fn wake(pipe: RawFd, method: WakeMethod) { 136 | unsafe { 137 | // This writes some data into the pipe. 138 | // 139 | // There are two tricks: 140 | // * First, the crazy cast. The first part turns reference into pointer. The second part 141 | // turns pointer to u8 into a pointer to void, which is what write requires. 142 | // * Second, we ignore errors, on purpose. We don't have any means to handling them. The 143 | // two conceivable errors are EBADFD, if someone passes a non-existent file descriptor or 144 | // if it is closed. The second is EAGAIN, in which case the pipe is full ‒ there were 145 | // many signals, but the reader didn't have time to read the data yet. It'll still get 146 | // woken up, so not fitting another letter in it is fine. 147 | let data = b"X" as *const _ as *const _; 148 | match method { 149 | WakeMethod::Write => libc::write(pipe, data, 1), 150 | WakeMethod::Send => libc::send(pipe, data, 1, MSG_NOWAIT), 151 | }; 152 | } 153 | } 154 | 155 | /// Registers a write to a self-pipe whenever there's the signal. 156 | /// 157 | /// In this case, the pipe is taken as the `RawFd`. It'll be closed on deregistration. Effectively, 158 | /// the function takes ownership of the file descriptor. This includes feeling free to set arbitrary 159 | /// flags on it, including file status flags (that are shared across file descriptors created by 160 | /// `dup`). 161 | /// 162 | /// Note that passing the wrong file descriptor won't cause UB, but can still lead to severe bugs ‒ 163 | /// like data corruptions in files. Prefer using [`register`] if possible. 164 | /// 165 | /// Also, it is perfectly legal for multiple writes to be collated together (if not consumed) and 166 | /// to generate spurious wakeups (but will not generate spurious *bytes* in the pipe). 167 | /// 168 | /// # Internal details 169 | /// 170 | /// Internally, it *currently* does following. Note that this is *not* part of the stability 171 | /// guarantees and may change if necessary. 172 | /// 173 | /// * If the file descriptor can be used with [`send`][libc::send], it'll be used together with 174 | /// [`MSG_DONTWAIT`][libc::MSG_DONTWAIT]. This is tested by sending `0` bytes of data (depending 175 | /// on the socket type, this might wake the read end with an empty message). 176 | /// * If it is not possible, the [`O_NONBLOCK`][libc::O_NONBLOCK] will be set on the file 177 | /// descriptor and [`write`][libc::write] will be used instead. 178 | pub fn register_raw(signal: c_int, pipe: RawFd) -> Result { 179 | let res = unsafe { libc::send(pipe, &[] as *const _, 0, MSG_NOWAIT) }; 180 | let fd = match (res, Error::last_os_error().kind()) { 181 | (0, _) | (-1, ErrorKind::WouldBlock) => WakeFd { 182 | fd: pipe, 183 | method: WakeMethod::Send, 184 | }, 185 | _ => { 186 | let fd = WakeFd { 187 | fd: pipe, 188 | method: WakeMethod::Write, 189 | }; 190 | fd.set_flags()?; 191 | fd 192 | } 193 | }; 194 | let action = move || fd.wake(); 195 | unsafe { super::register(signal, action) } 196 | } 197 | 198 | /// Registers a write to a self-pipe whenever there's the signal. 199 | /// 200 | /// The ownership of pipe is taken and will be closed whenever the created action is unregistered. 201 | /// 202 | /// Note that if you want to register the same pipe for multiple signals, there's `try_clone` 203 | /// method on many unix socket primitives. 204 | /// 205 | /// See [`register_raw`] for further details. 206 | pub fn register

(signal: c_int, pipe: P) -> Result 207 | where 208 | P: IntoRawFd + 'static, 209 | { 210 | register_raw(signal, pipe.into_raw_fd()) 211 | } 212 | 213 | #[cfg(test)] 214 | mod tests { 215 | use std::io::Read; 216 | use std::os::unix::net::{UnixDatagram, UnixStream}; 217 | 218 | use super::*; 219 | 220 | // Note: multiple tests share the SIGUSR1 signal. This is fine, we only need to know the signal 221 | // arrives. It's OK to arrive multiple times, from multiple tests. 222 | fn wakeup() { 223 | crate::low_level::raise(libc::SIGUSR1).unwrap(); 224 | } 225 | 226 | #[test] 227 | fn register_with_socket() -> Result<(), Error> { 228 | let (mut read, write) = UnixStream::pair()?; 229 | register(libc::SIGUSR1, write)?; 230 | wakeup(); 231 | let mut buff = [0; 1]; 232 | read.read_exact(&mut buff)?; 233 | assert_eq!(b"X", &buff); 234 | Ok(()) 235 | } 236 | 237 | #[test] 238 | #[cfg(not(target_os = "haiku"))] 239 | fn register_dgram_socket() -> Result<(), Error> { 240 | let (read, write) = UnixDatagram::pair()?; 241 | register(libc::SIGUSR1, write)?; 242 | wakeup(); 243 | let mut buff = [0; 1]; 244 | // The attempt to detect if it is socket can generate an empty message. Therefore, do a few 245 | // retries. 246 | for _ in 0..3 { 247 | let len = read.recv(&mut buff)?; 248 | if len == 1 && &buff == b"X" { 249 | return Ok(()); 250 | } 251 | } 252 | panic!("Haven't received the right data"); 253 | } 254 | 255 | #[test] 256 | fn register_with_pipe() -> Result<(), Error> { 257 | let mut fds = [0; 2]; 258 | unsafe { assert_eq!(0, libc::pipe(fds.as_mut_ptr())) }; 259 | register_raw(libc::SIGUSR1, fds[1])?; 260 | wakeup(); 261 | let mut buff = [0; 1]; 262 | unsafe { assert_eq!(1, libc::read(fds[0], buff.as_mut_ptr() as *mut _, 1)) } 263 | assert_eq!(b"X", &buff); 264 | Ok(()) 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/low_level/siginfo.rs: -------------------------------------------------------------------------------- 1 | //! Extracting more information from the C [`siginfo_t`] structure. 2 | //! 3 | //! See [`Origin`]. 4 | 5 | use std::fmt::{Debug, Formatter, Result as FmtResult}; 6 | 7 | use libc::{c_int, pid_t, siginfo_t, uid_t}; 8 | 9 | use crate::low_level; 10 | 11 | // Careful: make sure the signature and the constants match the C source 12 | extern "C" { 13 | fn sighook_signal_cause(info: &siginfo_t) -> ICause; 14 | fn sighook_signal_pid(info: &siginfo_t) -> pid_t; 15 | fn sighook_signal_uid(info: &siginfo_t) -> uid_t; 16 | } 17 | 18 | // Warning: must be in sync with the C code 19 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 20 | #[non_exhaustive] 21 | #[repr(u8)] 22 | // For some reason, the fact it comes from the C makes rustc emit warning that *some* of these are 23 | // not constructed. No idea why only some of them. 24 | #[allow(dead_code)] 25 | enum ICause { 26 | Unknown = 0, 27 | Kernel = 1, 28 | User = 2, 29 | TKill = 3, 30 | Queue = 4, 31 | MesgQ = 5, 32 | Exited = 6, 33 | Killed = 7, 34 | Dumped = 8, 35 | Trapped = 9, 36 | Stopped = 10, 37 | Continued = 11, 38 | } 39 | 40 | impl ICause { 41 | // The MacOs doesn't use the SI_* constants and leaves si_code at 0. But it doesn't use an 42 | // union, it has a good-behaved struct with fields and therefore we *can* read the values, 43 | // even though they'd contain nonsense (zeroes). We wipe that out later. 44 | #[cfg(target_os = "macos")] 45 | fn has_process(self) -> bool { 46 | true 47 | } 48 | 49 | #[cfg(not(target_os = "macos"))] 50 | fn has_process(self) -> bool { 51 | use ICause::*; 52 | match self { 53 | Unknown | Kernel => false, 54 | User | TKill | Queue | MesgQ | Exited | Killed | Dumped | Trapped | Stopped 55 | | Continued => true, 56 | } 57 | } 58 | } 59 | 60 | /// Information about process, as presented in the signal metadata. 61 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 62 | #[non_exhaustive] 63 | pub struct Process { 64 | /// The process ID. 65 | pub pid: pid_t, 66 | 67 | /// The user owning the process. 68 | pub uid: uid_t, 69 | } 70 | 71 | impl Process { 72 | /** 73 | * Extract the process information. 74 | * 75 | * # Safety 76 | * 77 | * The `info` must have a `si_code` corresponding to some situation that has the `si_pid` 78 | * and `si_uid` filled in. 79 | */ 80 | unsafe fn extract(info: &siginfo_t) -> Self { 81 | Self { 82 | pid: sighook_signal_pid(info), 83 | uid: sighook_signal_uid(info), 84 | } 85 | } 86 | } 87 | 88 | /// The means by which a signal was sent by other process. 89 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 90 | #[non_exhaustive] 91 | pub enum Sent { 92 | /// The `kill` call. 93 | User, 94 | 95 | /// The `tkill` call. 96 | /// 97 | /// This is likely linux specific. 98 | TKill, 99 | 100 | /// `sigqueue`. 101 | Queue, 102 | 103 | /// `mq_notify`. 104 | MesgQ, 105 | } 106 | 107 | /// A child changed its state. 108 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 109 | #[non_exhaustive] 110 | pub enum Chld { 111 | /// The child exited normally. 112 | Exited, 113 | 114 | /// It got killed by a signal. 115 | Killed, 116 | 117 | /// It got killed by a signal and dumped core. 118 | Dumped, 119 | 120 | /// The child was trapped by a `SIGTRAP` signal. 121 | Trapped, 122 | 123 | /// The child got stopped. 124 | Stopped, 125 | 126 | /// The child continued (after being stopped). 127 | Continued, 128 | } 129 | 130 | /// What caused a signal. 131 | /// 132 | /// This is a best-effort (and possibly incomplete) representation of the C `siginfo_t::si_code`. 133 | /// It may differ between OSes and may be extended in future versions. 134 | /// 135 | /// Note that this doesn't contain all the „fault“ signals (`SIGILL`, `SIGSEGV` and similar). 136 | /// There's no reasonable way to use the exfiltrators with them, since the handler either needs to 137 | /// terminate the process or somehow recover from the situation. Things based on exfiltrators do 138 | /// neither, which would cause an UB and therefore these values just don't make sense. 139 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 140 | #[non_exhaustive] 141 | pub enum Cause { 142 | /// The cause is unknown. 143 | /// 144 | /// Some systems don't fill this in. Some systems have values we don't understand. Some signals 145 | /// don't have specific reasons to come to being. 146 | Unknown, 147 | 148 | /// Sent by the kernel. 149 | /// 150 | /// This probably exists only on Linux. 151 | Kernel, 152 | 153 | /// The signal was sent by other process. 154 | Sent(Sent), 155 | 156 | /// A `SIGCHLD`, caused by a child process changing state. 157 | Chld(Chld), 158 | } 159 | 160 | impl From for Cause { 161 | fn from(c: ICause) -> Cause { 162 | match c { 163 | ICause::Kernel => Cause::Kernel, 164 | ICause::User => Cause::Sent(Sent::User), 165 | ICause::TKill => Cause::Sent(Sent::TKill), 166 | ICause::Queue => Cause::Sent(Sent::Queue), 167 | ICause::MesgQ => Cause::Sent(Sent::MesgQ), 168 | ICause::Exited => Cause::Chld(Chld::Exited), 169 | ICause::Killed => Cause::Chld(Chld::Killed), 170 | ICause::Dumped => Cause::Chld(Chld::Dumped), 171 | ICause::Trapped => Cause::Chld(Chld::Trapped), 172 | ICause::Stopped => Cause::Chld(Chld::Stopped), 173 | ICause::Continued => Cause::Chld(Chld::Continued), 174 | // Unknown and possibly others if the underlying lib is updated 175 | _ => Cause::Unknown, 176 | } 177 | } 178 | } 179 | 180 | /// Information about a signal and its origin. 181 | /// 182 | /// This is produced by the [`WithOrigin`] exfiltrator (or can be [extracted][Origin::extract] from 183 | /// `siginfo_t` by hand). 184 | #[derive(Clone, Eq, PartialEq)] 185 | #[non_exhaustive] 186 | pub struct Origin { 187 | /// The signal that happened. 188 | pub signal: c_int, 189 | 190 | /// Information about the process that caused the signal. 191 | /// 192 | /// Note that not all signals are caused by a specific process or have the information 193 | /// available („fault“ signals like `SIGBUS` don't have, any signal may be sent by the kernel 194 | /// instead of a specific process). 195 | /// 196 | /// This is filled in whenever available. For most signals, this is the process that sent the 197 | /// signal (by `kill` or similar), for `SIGCHLD` it is the child that caused the signal. 198 | pub process: Option, 199 | 200 | /// How the signal happened. 201 | /// 202 | /// This is a best-effort value. In particular, some systems may have causes not known to this 203 | /// library. Some other systems (MacOS) does not fill the value in so there's no way to know. 204 | /// In all these cases, this will contain [`Cause::Unknown`]. 205 | /// 206 | /// Some values are platform specific and not available on other systems. 207 | /// 208 | /// Future versions may enrich the enum by further values. 209 | pub cause: Cause, 210 | } 211 | 212 | impl Debug for Origin { 213 | fn fmt(&self, fmt: &mut Formatter) -> FmtResult { 214 | fn named_signal(sig: c_int) -> String { 215 | low_level::signal_name(sig) 216 | .map(|n| format!("{} ({})", n, sig)) 217 | .unwrap_or_else(|| sig.to_string()) 218 | } 219 | fmt.debug_struct("Origin") 220 | .field("signal", &named_signal(self.signal)) 221 | .field("process", &self.process) 222 | .field("cause", &self.cause) 223 | .finish() 224 | } 225 | } 226 | 227 | impl Origin { 228 | /// Extracts the Origin from a raw `siginfo_t` structure. 229 | /// 230 | /// This function is async-signal-safe, can be called inside a signal handler. 231 | /// 232 | /// # Safety 233 | /// 234 | /// On systems where the structure is backed by an union on the C side, this requires the 235 | /// `si_code` and `si_signo` fields must be set properly according to what fields are 236 | /// available. 237 | /// 238 | /// The value passed by kernel satisfies this, care must be taken only when constructed 239 | /// manually. 240 | pub unsafe fn extract(info: &siginfo_t) -> Self { 241 | let cause = sighook_signal_cause(info); 242 | let process = if cause.has_process() { 243 | let process = Process::extract(info); 244 | // On macos we don't have the si_code to go by, but we can go by the values being 245 | // empty there. 246 | if cfg!(target_os = "macos") && process.pid == 0 && process.uid == 0 { 247 | None 248 | } else { 249 | Some(process) 250 | } 251 | } else { 252 | None 253 | }; 254 | let signal = info.si_signo; 255 | Origin { 256 | cause: cause.into(), 257 | signal, 258 | process, 259 | } 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/low_level/signal_details.rs: -------------------------------------------------------------------------------- 1 | //! Providing auxiliary information for signals. 2 | 3 | use std::io::Error; 4 | use std::mem; 5 | use std::ptr; 6 | 7 | use libc::{c_int, EINVAL}; 8 | #[cfg(not(windows))] 9 | use libc::{sigset_t, SIG_UNBLOCK}; 10 | 11 | use crate::consts::signal::*; 12 | use crate::low_level; 13 | 14 | #[derive(Clone, Copy, Debug)] 15 | enum DefaultKind { 16 | Ignore, 17 | #[cfg(not(windows))] 18 | Stop, 19 | Term, 20 | } 21 | 22 | struct Details { 23 | signal: c_int, 24 | name: &'static str, 25 | default_kind: DefaultKind, 26 | } 27 | 28 | macro_rules! s { 29 | ($name: expr, $kind: ident) => { 30 | Details { 31 | signal: $name, 32 | name: stringify!($name), 33 | default_kind: DefaultKind::$kind, 34 | } 35 | }; 36 | } 37 | 38 | #[cfg(not(windows))] 39 | const DETAILS: &[Details] = &[ 40 | s!(SIGABRT, Term), 41 | s!(SIGALRM, Term), 42 | s!(SIGBUS, Term), 43 | s!(SIGCHLD, Ignore), 44 | // Technically, continue the process... but this is not done *by* the process. 45 | s!(SIGCONT, Ignore), 46 | s!(SIGFPE, Term), 47 | s!(SIGHUP, Term), 48 | s!(SIGILL, Term), 49 | s!(SIGINT, Term), 50 | #[cfg(any( 51 | target_os = "freebsd", 52 | target_os = "dragonfly", 53 | target_os = "netbsd", 54 | target_os = "openbsd", 55 | target_os = "macos" 56 | ))] 57 | s!(SIGINFO, Ignore), 58 | #[cfg(not(target_os = "haiku"))] 59 | s!(SIGIO, Ignore), 60 | // Can't override anyway, but... 61 | s!(SIGKILL, Term), 62 | s!(SIGPIPE, Term), 63 | s!(SIGPROF, Term), 64 | s!(SIGQUIT, Term), 65 | s!(SIGSEGV, Term), 66 | // Can't override anyway, but... 67 | s!(SIGSTOP, Stop), 68 | s!(SIGSYS, Term), 69 | s!(SIGTERM, Term), 70 | s!(SIGTRAP, Term), 71 | s!(SIGTSTP, Stop), 72 | s!(SIGTTIN, Stop), 73 | s!(SIGTTOU, Stop), 74 | s!(SIGURG, Ignore), 75 | s!(SIGUSR1, Term), 76 | s!(SIGUSR2, Term), 77 | s!(SIGVTALRM, Term), 78 | s!(SIGWINCH, Ignore), 79 | s!(SIGXCPU, Term), 80 | s!(SIGXFSZ, Term), 81 | ]; 82 | 83 | #[cfg(windows)] 84 | const DETAILS: &[Details] = &[ 85 | s!(SIGABRT, Term), 86 | s!(SIGFPE, Term), 87 | s!(SIGILL, Term), 88 | s!(SIGINT, Term), 89 | s!(SIGSEGV, Term), 90 | s!(SIGTERM, Term), 91 | ]; 92 | 93 | /// Provides a human-readable name of a signal. 94 | /// 95 | /// Note that the name does not have to be known (in case it is some less common, or non-standard 96 | /// signal). 97 | /// 98 | /// # Examples 99 | /// 100 | /// ``` 101 | /// # use signal_hook::low_level::signal_name; 102 | /// assert_eq!("SIGKILL", signal_name(9).unwrap()); 103 | /// assert!(signal_name(142).is_none()); 104 | /// ``` 105 | pub fn signal_name(signal: c_int) -> Option<&'static str> { 106 | DETAILS.iter().find(|d| d.signal == signal).map(|d| d.name) 107 | } 108 | 109 | #[cfg(not(windows))] 110 | fn restore_default(signal: c_int) -> Result<(), Error> { 111 | unsafe { 112 | // A C structure, supposed to be memset to 0 before use. 113 | let mut action: libc::sigaction = mem::zeroed(); 114 | action.sa_sigaction = libc::SIG_DFL as _; 115 | if libc::sigaction(signal, &action, ptr::null_mut()) == 0 { 116 | Ok(()) 117 | } else { 118 | Err(Error::last_os_error()) 119 | } 120 | } 121 | } 122 | 123 | #[cfg(windows)] 124 | fn restore_default(signal: c_int) -> Result<(), Error> { 125 | unsafe { 126 | // SIG_DFL = 0, but not in libc :-( 127 | if libc::signal(signal, 0) == 0 { 128 | Ok(()) 129 | } else { 130 | Err(Error::last_os_error()) 131 | } 132 | } 133 | } 134 | 135 | /// Emulates the behaviour of a default handler for the provided signal. 136 | /// 137 | /// This function does its best to provide the same action as the default handler would do, without 138 | /// disrupting the rest of the handling of such signal in the application. It is also 139 | /// async-signal-safe. 140 | /// 141 | /// This function necessarily looks up the appropriate action in a table. That means it is possible 142 | /// your system has a signal that is not known to this function. In such case an error is returned 143 | /// (equivalent of `EINVAL`). 144 | /// 145 | /// See also the [`register_conditional_default`][crate::flag::register_conditional_default]. 146 | /// 147 | /// # Warning 148 | /// 149 | /// There's a short race condition in case of signals that terminate (either with or without a core 150 | /// dump). The emulation first resets the signal handler back to default (as the application is 151 | /// going to end, it's not a problem) and invokes it. But if some other thread installs a signal 152 | /// handler in the meantime (without assistance from `signal-hook`), it can happen this will be 153 | /// invoked by the re-raised signal. 154 | /// 155 | /// This function will still terminate the application (there's a fallback on `abort`), the risk is 156 | /// invoking the newly installed signal handler. Note that manipulating the low-level signals is 157 | /// always racy in a multi-threaded program, therefore the described situation is already 158 | /// discouraged. 159 | /// 160 | /// If you are uneasy about such race condition, the recommendation is to run relevant termination 161 | /// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they 162 | /// say, but slightly differ in externally observable behaviour from termination by a signal (the 163 | /// exit code will specify that the application exited, not that it terminated with a signal in the 164 | /// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be 165 | /// different). 166 | pub fn emulate_default_handler(signal: c_int) -> Result<(), Error> { 167 | #[cfg(not(windows))] 168 | { 169 | if signal == SIGSTOP || signal == SIGKILL { 170 | return low_level::raise(signal); 171 | } 172 | } 173 | let kind = DETAILS 174 | .iter() 175 | .find(|d| d.signal == signal) 176 | .map(|d| d.default_kind) 177 | .ok_or_else(|| Error::from_raw_os_error(EINVAL))?; 178 | match kind { 179 | DefaultKind::Ignore => Ok(()), 180 | #[cfg(not(windows))] 181 | DefaultKind::Stop => low_level::raise(SIGSTOP), 182 | DefaultKind::Term => { 183 | if let Ok(()) = restore_default(signal) { 184 | #[cfg(not(windows))] 185 | unsafe { 186 | #[allow(deprecated)] 187 | let mut newsigs: sigset_t = mem::zeroed(); 188 | 189 | // Some android versions don't have the sigemptyset and sigaddset. 190 | // Unfortunately, we don't have an access to the android _version_. We just 191 | // know that 64bit versions are all OK, so this is a best-effort guess. 192 | // 193 | // For the affected/guessed versions, we provide our own implementation. We 194 | // hope it to be correct (it's inspired by a libc implementation and we assume 195 | // the kernel uses the same format ‒ it's unlikely to be different both because 196 | // of compatibility and because there's really nothing to invent about a 197 | // bitarray). 198 | // 199 | // We use the proper way for other systems. 200 | #[cfg(all(target_os = "android", target_pointer_width = "32"))] 201 | unsafe fn prepare_sigset(set: *mut sigset_t, mut signal: c_int) { 202 | signal -= 1; 203 | let set_raw: *mut libc::c_ulong = set.cast(); 204 | let size = mem::size_of::(); 205 | assert_eq!(set_raw as usize % mem::align_of::(), 0); 206 | let pos = signal as usize / size; 207 | assert!(pos < mem::size_of::() / size); 208 | let bit = 1 << (signal as usize % size); 209 | set_raw.add(pos).write(bit); 210 | } 211 | 212 | #[cfg(not(all(target_os = "android", target_pointer_width = "32")))] 213 | unsafe fn prepare_sigset(set: *mut sigset_t, signal: c_int) { 214 | libc::sigemptyset(set); 215 | libc::sigaddset(set, signal); 216 | } 217 | 218 | prepare_sigset(&mut newsigs, signal); 219 | // Ignore the result, if it doesn't work, we try anyway 220 | // Also, sigprocmask is unspecified, but available on more systems. And we want 221 | // to just enable _something_. And if it doesn't work, we'll terminate 222 | // anyway... It's not UB, so we are good. 223 | libc::sigprocmask(SIG_UNBLOCK, &newsigs, ptr::null_mut()); 224 | } 225 | let _ = low_level::raise(signal); 226 | } 227 | // Fallback if anything failed or someone managed to put some other action in in 228 | // between. 229 | unsafe { libc::abort() } 230 | } 231 | } 232 | } 233 | 234 | #[cfg(test)] 235 | mod test { 236 | use super::*; 237 | 238 | #[test] 239 | fn existing() { 240 | assert_eq!("SIGTERM", signal_name(SIGTERM).unwrap()); 241 | } 242 | 243 | #[test] 244 | fn unknown() { 245 | assert!(signal_name(128).is_none()); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /tests/default.rs: -------------------------------------------------------------------------------- 1 | //! Check the hack of SIG_DFL for windows. 2 | //! 3 | //! Libc doesn't export SIG_DFL on windows. It seems to be 0 on all platforms, though, but just to 4 | //! make sure, we observe it is so. We try to read the previous signal on startup and it must be 5 | //! the default. 6 | 7 | extern crate libc; 8 | 9 | use libc::{sighandler_t, signal, SIGTERM}; 10 | 11 | const SIG_DFL: sighandler_t = 0; 12 | 13 | #[test] 14 | fn sig_dfl() { 15 | unsafe { 16 | let prev = signal(SIGTERM, SIG_DFL); 17 | assert_eq!(SIG_DFL, prev); 18 | } 19 | } 20 | 21 | #[cfg(not(windows))] 22 | #[test] 23 | fn sig_dfl_static() { 24 | assert_eq!(::libc::SIG_DFL, SIG_DFL); 25 | } 26 | -------------------------------------------------------------------------------- /tests/iterator.rs: -------------------------------------------------------------------------------- 1 | #![cfg(not(windows))] 2 | 3 | extern crate signal_hook; 4 | 5 | use std::collections::HashSet; 6 | use std::sync::atomic::{AtomicBool, Ordering}; 7 | use std::sync::mpsc::{self, RecvTimeoutError}; 8 | use std::sync::Arc; 9 | use std::thread::{self, JoinHandle}; 10 | use std::time::Duration; 11 | 12 | use signal_hook::consts::{SIGUSR1, SIGUSR2}; 13 | use signal_hook::iterator::{Handle, Signals}; 14 | use signal_hook::low_level::raise; 15 | 16 | use serial_test::serial; 17 | 18 | fn send_sigusr1() { 19 | raise(SIGUSR1).unwrap(); 20 | } 21 | 22 | fn send_sigusr2() { 23 | raise(SIGUSR2).unwrap(); 24 | } 25 | 26 | fn setup_without_any_signals() -> (Signals, Handle) { 27 | let signals = Signals::new(&[]).unwrap(); 28 | let controller = signals.handle(); 29 | (signals, controller) 30 | } 31 | 32 | fn setup_for_sigusr2() -> (Signals, Handle) { 33 | let signals = Signals::new(&[SIGUSR2]).unwrap(); 34 | let controller = signals.handle(); 35 | (signals, controller) 36 | } 37 | 38 | macro_rules! assert_signals { 39 | ($actual:expr, $($expected:expr),+ $(,)?) => { 40 | let actual = $actual.collect::>(); 41 | let expected = vec!($($expected),+).into_iter().collect::>(); 42 | assert_eq!(actual, expected); 43 | }; 44 | } 45 | 46 | macro_rules! assert_no_signals { 47 | ($signals:expr) => { 48 | assert_eq!($signals.next(), None); 49 | }; 50 | } 51 | 52 | #[test] 53 | #[serial] 54 | fn forever_terminates_when_closed() { 55 | let (mut signals, controller) = setup_for_sigusr2(); 56 | 57 | // Detect early terminations. 58 | let stopped = Arc::new(AtomicBool::new(false)); 59 | 60 | let stopped_bg = Arc::clone(&stopped); 61 | let thread = thread::spawn(move || { 62 | // Eat all the signals there are (might come from a concurrent test, in theory). 63 | // Would wait forever, but it should be terminated by the close below. 64 | for _sig in &mut signals {} 65 | 66 | stopped_bg.store(true, Ordering::SeqCst); 67 | }); 68 | 69 | // Wait a bit to see if the thread terminates by itself. 70 | thread::sleep(Duration::from_millis(100)); 71 | assert!(!stopped.load(Ordering::SeqCst)); 72 | 73 | controller.close(); 74 | 75 | thread.join().unwrap(); 76 | 77 | assert!(stopped.load(Ordering::SeqCst)); 78 | } 79 | 80 | // A reproducer for #16: if we had the mio-support enabled (which is enabled also by the 81 | // tokio-support feature), blocking no longer works. The .wait() would return immediately (an empty 82 | // iterator, possibly), .forever() would do a busy loop. 83 | // flag) 84 | #[test] 85 | #[serial] 86 | fn signals_block_wait() { 87 | let mut signals = Signals::new(&[SIGUSR2]).unwrap(); 88 | let (s, r) = mpsc::channel(); 89 | let finish = Arc::new(AtomicBool::new(false)); 90 | let thread_id = thread::spawn({ 91 | let finish = Arc::clone(&finish); 92 | move || { 93 | // Technically, it may spuriously return early. But it shouldn't be doing it too much, 94 | // so we just try to wait multiple times ‒ if they *all* return right away, it is 95 | // broken. 96 | for _ in 0..10 { 97 | for _ in signals.wait() { 98 | if finish.load(Ordering::SeqCst) { 99 | // Asked to terminate at the end of the thread. Do so (but without 100 | // signalling the receipt). 101 | return; 102 | } else { 103 | panic!("Someone really did send us SIGUSR2, which breaks the test"); 104 | } 105 | } 106 | } 107 | let _ = s.send(()); 108 | } 109 | }); 110 | 111 | // A RAII guard to make sure we shut down the thread even if the test fails. 112 | struct ThreadGuard { 113 | thread: Option>, 114 | finish: Arc, 115 | } 116 | 117 | impl ThreadGuard { 118 | fn shutdown(&mut self) { 119 | // Tell it to shut down 120 | self.finish.store(true, Ordering::SeqCst); 121 | // Wake it up 122 | send_sigusr2(); 123 | // Wait for it to actually terminate. 124 | if let Some(thread) = self.thread.take() { 125 | thread.join().unwrap(); // Propagate panics 126 | } 127 | } 128 | } 129 | 130 | impl Drop for ThreadGuard { 131 | fn drop(&mut self) { 132 | self.shutdown(); // OK if done twice, won't have the thread any more. 133 | } 134 | } 135 | 136 | let mut bg_thread = ThreadGuard { 137 | thread: Some(thread_id), 138 | finish, 139 | }; 140 | 141 | let err = r 142 | .recv_timeout(Duration::from_millis(100)) 143 | .expect_err("Wait didn't wait properly"); 144 | assert_eq!(err, RecvTimeoutError::Timeout); 145 | 146 | bg_thread.shutdown(); 147 | } 148 | 149 | #[test] 150 | #[serial] 151 | fn pending_doesnt_block() { 152 | let (mut signals, _) = setup_for_sigusr2(); 153 | 154 | let mut recieved_signals = signals.pending(); 155 | 156 | assert_no_signals!(recieved_signals); 157 | } 158 | 159 | #[test] 160 | #[serial] 161 | fn wait_returns_recieved_signals() { 162 | let (mut signals, _) = setup_for_sigusr2(); 163 | send_sigusr2(); 164 | 165 | let recieved_signals = signals.wait(); 166 | 167 | assert_signals!(recieved_signals, SIGUSR2); 168 | } 169 | 170 | #[test] 171 | #[serial] 172 | fn forever_returns_recieved_signals() { 173 | let (mut signals, _) = setup_for_sigusr2(); 174 | send_sigusr2(); 175 | 176 | let signal = signals.forever().take(1); 177 | 178 | assert_signals!(signal, SIGUSR2); 179 | } 180 | 181 | #[test] 182 | #[serial] 183 | fn wait_doesnt_block_when_closed() { 184 | let (mut signals, controller) = setup_for_sigusr2(); 185 | controller.close(); 186 | 187 | let mut recieved_signals = signals.wait(); 188 | 189 | assert_no_signals!(recieved_signals); 190 | } 191 | 192 | #[test] 193 | #[serial] 194 | fn wait_unblocks_when_closed() { 195 | let (mut signals, controller) = setup_without_any_signals(); 196 | 197 | let thread = thread::spawn(move || { 198 | signals.wait(); 199 | }); 200 | 201 | controller.close(); 202 | 203 | thread.join().unwrap(); 204 | } 205 | 206 | #[test] 207 | #[serial] 208 | fn forever_doesnt_block_when_closed() { 209 | let (mut signals, controller) = setup_for_sigusr2(); 210 | controller.close(); 211 | 212 | let mut signal = signals.forever(); 213 | 214 | assert_no_signals!(signal); 215 | } 216 | 217 | #[test] 218 | #[serial] 219 | fn add_signal_after_creation() { 220 | let (mut signals, _) = setup_without_any_signals(); 221 | signals.add_signal(SIGUSR1).unwrap(); 222 | 223 | send_sigusr1(); 224 | 225 | assert_signals!(signals.pending(), SIGUSR1); 226 | } 227 | 228 | #[test] 229 | #[serial] 230 | fn delayed_signal_consumed() { 231 | let (mut signals, _) = setup_for_sigusr2(); 232 | signals.add_signal(SIGUSR1).unwrap(); 233 | 234 | send_sigusr1(); 235 | let mut recieved_signals = signals.wait(); 236 | send_sigusr2(); 237 | 238 | assert_signals!(recieved_signals, SIGUSR1, SIGUSR2); 239 | 240 | // The pipe still contains the byte from the second 241 | // signal and so wait won't block but won't return 242 | // a signal. 243 | recieved_signals = signals.wait(); 244 | assert_no_signals!(recieved_signals); 245 | } 246 | 247 | #[test] 248 | #[serial] 249 | fn is_closed_initially_returns_false() { 250 | let (_, controller) = setup_for_sigusr2(); 251 | 252 | assert!(!controller.is_closed()); 253 | } 254 | 255 | #[test] 256 | #[serial] 257 | fn is_closed_returns_true_when_closed() { 258 | let (_, controller) = setup_for_sigusr2(); 259 | controller.close(); 260 | 261 | assert!(controller.is_closed()); 262 | } 263 | -------------------------------------------------------------------------------- /tests/shutdown.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the shutdown. 2 | //! 3 | //! The tests work like this: 4 | //! 5 | //! * The register an alarm, to fail if anything takes too long (which is very much possible here). 6 | //! * A fork is done, with the child registering a signal with a NOP and cleanup operation (one or 7 | //! the other). 8 | //! * The child puts some kind of infinite loop or sleep inside itself, so it never actually 9 | //! terminates on the first, but would terminate after the signal. 10 | 11 | #![cfg(not(windows))] // Forks don't work on Windows, but windows has the same implementation. 12 | 13 | use std::io::Error; 14 | use std::ptr; 15 | use std::sync::atomic::AtomicBool; 16 | use std::sync::Arc; 17 | use std::thread; 18 | use std::time::Duration; 19 | 20 | use signal_hook::consts::signal::*; 21 | use signal_hook::flag; 22 | use signal_hook::low_level; 23 | 24 | fn do_test(child: C) { 25 | unsafe { 26 | libc::alarm(10); // Time out the test after 10 seconds and get it killed. 27 | match libc::fork() { 28 | -1 => panic!("Fork failed: {}", Error::last_os_error()), 29 | 0 => { 30 | child(); 31 | loop { 32 | thread::sleep(Duration::from_secs(1)); 33 | } 34 | } 35 | pid => { 36 | // Give the child some time to register signals and stuff 37 | // We could actually signal that the child is ready by it eg. closing STDOUT, but 38 | // this is just a test so we don't really bother. 39 | thread::sleep(Duration::from_millis(250)); 40 | libc::kill(pid, libc::SIGTERM); 41 | // Wait a small bit to make sure the signal got delivered. 42 | thread::sleep(Duration::from_millis(50)); 43 | // The child is still running, because the first signal got "handled" by being 44 | // ignored. 45 | let terminated = libc::waitpid(pid, ptr::null_mut(), libc::WNOHANG); 46 | assert_eq!(0, terminated, "Process {} terminated prematurely", pid); 47 | // But it terminates on the second attempt (we do block on wait here). 48 | libc::kill(pid, libc::SIGTERM); 49 | let terminated = libc::waitpid(pid, ptr::null_mut(), 0); 50 | assert_eq!(pid, terminated); 51 | } 52 | } 53 | } 54 | } 55 | 56 | /// Use automatic cleanup inside the signal handler to get rid of old signals, the aggressive way. 57 | #[test] 58 | fn cleanup_inside_signal() { 59 | fn hook() { 60 | // Make sure we have some signal handler, not the default. 61 | unsafe { low_level::register(SIGTERM, || ()).unwrap() }; 62 | let shutdown_cond = Arc::new(AtomicBool::new(false)); 63 | // „disarmed“ shutdown 64 | flag::register_conditional_shutdown(SIGTERM, 0, Arc::clone(&shutdown_cond)).unwrap(); 65 | // But arm at the first SIGTERM 66 | flag::register(SIGTERM, shutdown_cond).unwrap(); 67 | } 68 | do_test(hook); 69 | } 70 | 71 | /// Manually remove the signal handler just after receiving the signal but before going into an 72 | /// infinite loop. 73 | #[test] 74 | fn cleanup_after_signal() { 75 | fn hook() { 76 | let mut signals = signal_hook::iterator::Signals::new(&[libc::SIGTERM]).unwrap(); 77 | assert_eq!(Some(SIGTERM), signals.into_iter().next()); 78 | flag::register_conditional_shutdown(SIGTERM, 0, Arc::new(AtomicBool::new(true))).unwrap(); 79 | } 80 | do_test(hook); 81 | } 82 | --------------------------------------------------------------------------------