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
168
169
170
171
172
173
174
175
176
177
178
179
//! Tiny Unix FFI helpers shared across modules.
//!
//! We only need two libc symbols (`kill`, `setsid`), so depending on the
//! `libc` crate would be overkill. This module hosts the single source of
//! truth for both bindings; previously they were duplicated across
//! `agent.rs` and `session.rs`.
//!
//! All entry points are gated on `#[cfg(unix)]` at the call sites; the
//! module itself is only compiled on Unix targets.
extern "C"
/// Send `sig` to `pid`. Returns the libc return value: 0 on success, -1
/// on error (with `errno` set).
///
/// Pass `0` for `sig` to query process existence without delivering a
/// signal. Pass a negative `pid` to target the entire process group of
/// `-pid` (the standard kill(2) idiom).
///
/// SAFETY: `kill` is a thin syscall wrapper. No state is mutated in the
/// caller's address space.
/// Create a new session and process group. Returns the new session id on
/// success, -1 on error.
///
/// Used inside `Command::pre_exec` so the spawned child becomes its own
/// process-group leader. `setsid` is listed as async-signal-safe by
/// POSIX, which is what makes it valid in a `pre_exec` hook.
///
/// SAFETY: must only be called between fork() and exec(); calling it in
/// the parent process would detach the parent from its controlling
/// terminal. `Command::pre_exec` enforces that constraint by contract.
pub unsafe
/// Session id of the current process (`getsid(0)`), or -1 on error.
///
/// Used by `reap_session` as a guard so we never sweep our own session,
/// which would SIGKILL Mezame itself.
///
/// SAFETY: `getsid` is a thin syscall wrapper that reads kernel state and
/// mutates nothing in the caller's address space.
/// Parse the session id (field 6 of `/proc/<pid>/stat`) for `pid`.
///
/// Returns `None` if the entry is gone or unparseable. The `comm` field
/// (field 2) is wrapped in parentheses and may itself contain spaces and
/// parentheses, so we split on the LAST `)` and count from there: the
/// tokens following it are `state ppid pgrp session ...`, so the session
/// id is the 4th whitespace-separated token after the final `)`.
/// SIGKILL every process belonging to session `sid`.
///
/// The agent is spawned as its own session leader (via `setsid` in
/// `spawn_agent`), so its session id equals its pid. A `kill(-pgid)` on the
/// agent's process group reaps the agent and `kiro-cli`, but MCP servers
/// launched through `npx`/`npm` place themselves in their OWN process
/// groups, so the group kill never reaches them; they only inherit the
/// agent's SESSION. Walking `/proc` for processes whose session id matches
/// and SIGKILLing them reaps those escapees before they orphan to PID 1
/// and accumulate inside the service cgroup.
///
/// Best-effort and defensive:
/// - A `sid` of 0 or 1, or one equal to our own session, is a no-op so a
/// test harness or a stray misparse can never sweep unrelated
/// processes (or Mezame itself).
/// - pid <= 1 and our own pid are always skipped (in `sweep_session`).
/// - Unreadable or vanished `/proc` entries are silently ignored.
///
/// The walk is Linux-specific (procfs); on other Unix targets it degrades
/// to a no-op via `sweep_session`, where the process-group kill in
/// `kill_process_group` remains the primary teardown.
/// Walk `/proc` and SIGKILL every process whose session id equals `sid`.
/// Skips pid <= 1 and our own pid; unreadable or vanished entries are
/// ignored. The caller (`reap_session`) has already applied the sid
/// guards. Linux-only (procfs).
/// No `/proc` to walk on non-Linux Unix targets, so the sweep is a no-op;
/// the process-group kill in `kill_process_group` remains the teardown.