1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//! Signal handling — SIGINT / SIGTERM set a graceful-shutdown flag
//! that long-running operations (`push` / `pull` / `clone` / `log`)
//! poll at natural checkpoints, so a `Ctrl-C` aborts cleanly with
//! `exit::TEMPFAIL` (75) rather than leaving a half-finished transfer.
//!
//! ## SIGPIPE is intentionally not registered here
//!
//! Rust's runtime sets `SIGPIPE` to `SIG_IGN` at process start since
//! 1.65, which means `write(2)` on a closed pipe returns `EPIPE`
//! instead of terminating the process. The CLI uses
//! `let _ = writeln!(stdout, …)` everywhere, so the `EPIPE` propagates
//! as a silently-dropped `io::Error` and the program exits at its
//! next natural completion point — exactly the pipeline-friendly
//! behaviour `docs/CLI.md` advertises.
//!
//! Registering a signal-hook handler over the runtime's `SIG_IGN`
//! would replace a clean kernel-level ignore with a userspace handler
//! that does an atomic store and returns — observationally
//! equivalent but strictly worse (extra wakeups, a window where a
//! different thread might briefly observe a flipped flag we never
//! consume). The integration test in `tests/sigpipe.rs` is the
//! regression guard: it pipes `mkit cat <large-blob>` through
//! `head -1` and asserts the left-hand exit code is `0`. If anyone
//! ever opts mkit out of Rust's default with `#[unix_sigpipe]`, that
//! test goes red.
//!
//! ## Implementation
//!
//! `signal-hook`'s `flag` module installs the handlers via
//! `sigaction(2)` and exposes a fully safe API (atomic-bool stores
//! are async-signal-safe; the `unsafe` lives inside the crate). The
//! CLI stays under its crate-level `#![deny(unsafe_code)]`.
use ;
use ;
/// Shared shutdown flag. Lazily initialised so tests that exercise the
/// flag without going through [`install`] still observe a coherent
/// value. The `Arc` is required because `signal_hook::flag::register`
/// takes an owned `Arc<AtomicBool>` — it does not accept a `&'static`.
static SHUTDOWN: = new;
/// Install SIGINT/SIGTERM handlers that flip the shared shutdown flag.
/// Idempotent on the `signal-hook` side: re-registering the same
/// signal layers another handler on top, but the cost is a few bytes
/// and the observable behaviour is unchanged, so callers can invoke
/// this more than once without harm.
///
/// On non-Unix targets this is a no-op (Windows signal semantics
/// differ; the CLI does not currently ship on Windows).
/// Returns `true` once a shutdown was requested via signal. Long-
/// running poll loops should call this at natural checkpoints and
/// return `exit::TEMPFAIL` when it flips.
/// Test hook — flips the shutdown flag so unit tests can verify that
/// long-running callers do honour it once it flips.