@sema-agent/core 1.433.0 → 1.434.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
const DEFAULT_GRACE_MS = 3000;
|
|
@@ -25,8 +25,18 @@ export function killProcessTree(pid, opts) {
|
|
|
25
25
|
return;
|
|
26
26
|
}
|
|
27
27
|
const graceMs = normalizeGraceMs(opts?.graceMs);
|
|
28
|
-
signalProcessTreeUnix(pid, "SIGTERM", useGroupKill);
|
|
28
|
+
const termDescendants = signalProcessTreeUnix(pid, "SIGTERM", useGroupKill);
|
|
29
29
|
setTimeout(() => {
|
|
30
|
+
const rediscovered = enumerateDescendantsSync(pid);
|
|
31
|
+
const verifiedCarriedForward = new Map();
|
|
32
|
+
for (const [p, originalEtime] of termDescendants) {
|
|
33
|
+
const currentEtime = rediscovered.etimeOf.get(p);
|
|
34
|
+
if (currentEtime !== undefined && verifyStillSameProcess(originalEtime, currentEtime)) {
|
|
35
|
+
verifiedCarriedForward.set(p, currentEtime);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const knownDescendants = new Map([...verifiedCarriedForward, ...rediscovered.descendants]);
|
|
39
|
+
killEach(knownDescendants.keys(), "SIGKILL");
|
|
30
40
|
if (escalationCancelled(opts?.stillRunning)) {
|
|
31
41
|
if (useGroupKill && isProcessAlive(-pid)) {
|
|
32
42
|
try {
|
|
@@ -43,7 +53,19 @@ export function killProcessTree(pid, opts) {
|
|
|
43
53
|
if (!stillAlive) {
|
|
44
54
|
return;
|
|
45
55
|
}
|
|
46
|
-
|
|
56
|
+
if (useGroupKill) {
|
|
57
|
+
try {
|
|
58
|
+
process.kill(-pid, "SIGKILL");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
process.kill(pid, "SIGKILL");
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
}
|
|
47
69
|
}, graceMs).unref();
|
|
48
70
|
}
|
|
49
71
|
function escalationCancelled(stillRunning) {
|
|
@@ -82,19 +104,107 @@ function isProcessAlive(pid) {
|
|
|
82
104
|
}
|
|
83
105
|
}
|
|
84
106
|
function signalProcessTreeUnix(pid, signal, useGroupKill) {
|
|
107
|
+
const descendants = enumerateDescendantsSync(pid).descendants;
|
|
85
108
|
if (useGroupKill) {
|
|
86
109
|
try {
|
|
87
110
|
process.kill(-pid, signal);
|
|
88
|
-
return;
|
|
89
111
|
}
|
|
90
112
|
catch {
|
|
113
|
+
try {
|
|
114
|
+
process.kill(pid, signal);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
}
|
|
91
118
|
}
|
|
119
|
+
killEach(descendants.keys(), signal);
|
|
120
|
+
return descendants;
|
|
92
121
|
}
|
|
93
122
|
try {
|
|
94
123
|
process.kill(pid, signal);
|
|
95
124
|
}
|
|
96
125
|
catch {
|
|
97
126
|
}
|
|
127
|
+
killEach(descendants.keys(), signal);
|
|
128
|
+
return descendants;
|
|
129
|
+
}
|
|
130
|
+
function killEach(pids, signal) {
|
|
131
|
+
for (const pid of pids) {
|
|
132
|
+
try {
|
|
133
|
+
process.kill(pid, signal);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const PS_ENUMERATION_TIMEOUT_MS = 500;
|
|
140
|
+
const EMPTY_SNAPSHOT = { descendants: new Map(), etimeOf: new Map() };
|
|
141
|
+
function enumerateDescendantsSync(rootPid) {
|
|
142
|
+
if (process.platform === "win32")
|
|
143
|
+
return EMPTY_SNAPSHOT;
|
|
144
|
+
let out;
|
|
145
|
+
try {
|
|
146
|
+
const result = spawnSync("ps", ["-A", "-o", "pid=", "-o", "ppid=", "-o", "etime="], {
|
|
147
|
+
timeout: PS_ENUMERATION_TIMEOUT_MS,
|
|
148
|
+
encoding: "utf8",
|
|
149
|
+
windowsHide: true,
|
|
150
|
+
});
|
|
151
|
+
if (result.error || typeof result.stdout !== "string")
|
|
152
|
+
return EMPTY_SNAPSHOT;
|
|
153
|
+
out = result.stdout;
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
return EMPTY_SNAPSHOT;
|
|
157
|
+
}
|
|
158
|
+
const childrenOf = new Map();
|
|
159
|
+
const etimeOf = new Map();
|
|
160
|
+
for (const line of out.split("\n")) {
|
|
161
|
+
const m = /^\s*(\d+)\s+(\d+)\s+(\S+)\s*$/.exec(line);
|
|
162
|
+
if (!m)
|
|
163
|
+
continue;
|
|
164
|
+
const p = Number(m[1]);
|
|
165
|
+
const pp = Number(m[2]);
|
|
166
|
+
etimeOf.set(p, m[3]);
|
|
167
|
+
const list = childrenOf.get(pp);
|
|
168
|
+
if (list)
|
|
169
|
+
list.push(p);
|
|
170
|
+
else
|
|
171
|
+
childrenOf.set(pp, [p]);
|
|
172
|
+
}
|
|
173
|
+
const seen = new Set();
|
|
174
|
+
const queue = [rootPid];
|
|
175
|
+
while (queue.length > 0) {
|
|
176
|
+
const cur = queue.shift();
|
|
177
|
+
for (const kid of childrenOf.get(cur) ?? []) {
|
|
178
|
+
if (kid <= 1 || kid === rootPid || seen.has(kid))
|
|
179
|
+
continue;
|
|
180
|
+
seen.add(kid);
|
|
181
|
+
queue.push(kid);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const descendants = new Map();
|
|
185
|
+
for (const p of seen) {
|
|
186
|
+
const et = etimeOf.get(p);
|
|
187
|
+
if (et !== undefined)
|
|
188
|
+
descendants.set(p, et);
|
|
189
|
+
}
|
|
190
|
+
return { descendants, etimeOf };
|
|
191
|
+
}
|
|
192
|
+
function verifyStillSameProcess(originalEtime, currentEtime) {
|
|
193
|
+
const orig = parseEtimeSeconds(originalEtime);
|
|
194
|
+
const cur = parseEtimeSeconds(currentEtime);
|
|
195
|
+
if (orig === undefined || cur === undefined)
|
|
196
|
+
return false;
|
|
197
|
+
return cur >= orig;
|
|
198
|
+
}
|
|
199
|
+
function parseEtimeSeconds(etime) {
|
|
200
|
+
const m = /^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/.exec(etime.trim());
|
|
201
|
+
if (!m)
|
|
202
|
+
return undefined;
|
|
203
|
+
const days = m[1] !== undefined ? Number(m[1]) : 0;
|
|
204
|
+
const hours = m[2] !== undefined ? Number(m[2]) : 0;
|
|
205
|
+
const minutes = Number(m[3]);
|
|
206
|
+
const seconds = Number(m[4]);
|
|
207
|
+
return days * 86400 + hours * 3600 + minutes * 60 + seconds;
|
|
98
208
|
}
|
|
99
209
|
function runTaskkill(args) {
|
|
100
210
|
try {
|