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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! [`SoftStopScope`] — the reach of a **soft stop** (a `SIGTERM`/`SIGINT`-class
//! request that asks a tree to exit cleanly rather than hard-killing it) on the
//! **group axis**, queried by
//! [`ProcessGroup::soft_stop_scope`](crate::ProcessGroup::soft_stop_scope)
//! *before* a [`signal`](crate::ProcessGroup::signal) is attempted.
/// How far a **soft stop** on the group axis reaches on *this* group, right now
/// — the honest answer to "if I ask this group to stop gracefully
/// ([`signal(Signal::Term)`](crate::ProcessGroup::signal) /
/// [`Signal::Int`](crate::Signal::Int)), which of its members will actually
/// receive that request?"
///
/// This is a **capability report**, not an action: query it with
/// [`ProcessGroup::soft_stop_scope`](crate::ProcessGroup::soft_stop_scope) so a
/// caller (for example a CLI wrapping this crate) can decide *up front* whether a
/// soft stop is worth attempting — and state the real reach to its own user —
/// instead of firing a `signal`, parsing an [`ErrorReason::Unsupported`](crate::ErrorReason::Unsupported)
/// back, and guessing at the scope from a hard-coded platform assumption. It is
/// the group-axis analogue of
/// [`Command::kill_on_parent_death_scope`](crate::Command::kill_on_parent_death_scope)'s
/// [`ParentDeathCleanup`](crate::ParentDeathCleanup) report, but for the
/// deliberate-stop axis rather than the abrupt-owner-death axis.
///
/// # It reports the *soft* tier only
///
/// This describes only the graceful `Int`/`Term` soft-stop tier. The
/// unconditional **hard** kill — [`Signal::Kill`](crate::Signal::Kill),
/// [`kill_all`](crate::ProcessGroup::kill_all), and dropping the group — always
/// tears the whole tree down on every platform regardless of this value; that
/// guarantee is unchanged and never `Unsupported`.
///
/// # Runtime, per-group — not a fixed platform constant
///
/// Unlike [`ParentDeathCleanup`](crate::ParentDeathCleanup) (fixed per target at
/// build time), this is read from the group's **live membership** each call, so
/// the *same* build can report different scopes for different groups — most
/// visibly on Windows, where a group with an opt-in console-CTRL leader or a
/// windowed member reports [`OptInMembers`](Self::OptInMembers) while a group
/// with neither reports [`Unsupported`](Self::Unsupported). The read is
/// **side-effect-free**: it delivers no signal, posts no `WM_CLOSE`, spawns
/// nothing, and does not mutate the group — asking never changes the answer to a
/// later [`signal`](crate::ProcessGroup::signal).
///
/// | Mechanism | Scope | Why |
/// |---|---|---|
/// | Linux cgroup v2 | [`WholeTree`](Self::WholeTree) | `signal(Int/Term)` writes to every process in the cgroup — the whole tree receives it. |
/// | POSIX process group (macOS / the BSDs / Linux cgroup-less fallback) | [`WholeTree`](Self::WholeTree) | `killpg` reaches every tracked group leader **and its descendants** — the whole tracked tree (a child that `setsid`s away escapes, exactly as it does the kill-on-drop guarantee). |
/// | Windows Job Object | [`OptInMembers`](Self::OptInMembers) or [`Unsupported`](Self::Unsupported) | A Job Object has no POSIX signal; a soft stop reaches only members it can *trigger* — a console-CTRL leader (opted in via [`Command::windows_graceful_ctrl_break`](crate::Command::windows_graceful_ctrl_break)) or any live windowed member (`WM_CLOSE`). [`OptInMembers`](Self::OptInMembers) when at least one such member is live, else [`Unsupported`](Self::Unsupported). |
///
/// Consistent with [`signal`](crate::ProcessGroup::signal) by construction: it is
/// read from the very same live-membership primitives `signal(Int/Term)` acts on,
/// so what it reports matches what a real soft stop would then reach.
///
/// Non-exhaustive: a future platform — or a future soft-stop mechanism — may
/// report a scope not listed here, so match it with a wildcard arm.