pidnap 0.0.0-dev.3 → 0.0.0-dev.5

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.
package/src/tree-kill.ts DELETED
@@ -1,131 +0,0 @@
1
- import { spawn } from "node:child_process";
2
-
3
- type ProcessTree = Record<number, number[]>;
4
-
5
- /**
6
- * Kill a process and all its descendants (children, grandchildren, etc.)
7
- * @param pid - The process ID to kill
8
- * @param signal - The signal to send (default: SIGTERM)
9
- * @returns Promise that resolves when all processes have been signaled
10
- */
11
- export async function treeKill(pid: number, signal: NodeJS.Signals = "SIGTERM"): Promise<void> {
12
- if (Number.isNaN(pid)) {
13
- throw new Error("pid must be a number");
14
- }
15
-
16
- const tree: ProcessTree = { [pid]: [] };
17
- const pidsToProcess = new Set<number>([pid]);
18
-
19
- // Build the process tree
20
- await buildProcessTree(pid, tree, pidsToProcess);
21
-
22
- // Kill all processes in the tree (children first, then parents)
23
- killAll(tree, signal);
24
- }
25
-
26
- /**
27
- * Build a tree of all child processes recursively
28
- */
29
- async function buildProcessTree(
30
- parentPid: number,
31
- tree: ProcessTree,
32
- pidsToProcess: Set<number>,
33
- ): Promise<void> {
34
- return new Promise((resolve) => {
35
- const ps = spawn("ps", ["-o", "pid", "--no-headers", "--ppid", String(parentPid)]);
36
-
37
- let allData = "";
38
-
39
- ps.stdout.on("data", (data: Buffer) => {
40
- allData += data.toString("ascii");
41
- });
42
-
43
- ps.on("close", async (code) => {
44
- pidsToProcess.delete(parentPid);
45
-
46
- if (code !== 0) {
47
- // No child processes found for this parent
48
- if (pidsToProcess.size === 0) {
49
- resolve();
50
- }
51
- return;
52
- }
53
-
54
- const childPids = allData.match(/\d+/g);
55
- if (!childPids) {
56
- if (pidsToProcess.size === 0) {
57
- resolve();
58
- }
59
- return;
60
- }
61
-
62
- // Process all child PIDs concurrently
63
- const promises: Promise<void>[] = [];
64
-
65
- for (const pidStr of childPids) {
66
- const childPid = parseInt(pidStr, 10);
67
- tree[parentPid].push(childPid);
68
- tree[childPid] = [];
69
- pidsToProcess.add(childPid);
70
- promises.push(buildProcessTree(childPid, tree, pidsToProcess));
71
- }
72
-
73
- await Promise.all(promises);
74
-
75
- if (pidsToProcess.size === 0) {
76
- resolve();
77
- }
78
- });
79
-
80
- ps.on("error", () => {
81
- pidsToProcess.delete(parentPid);
82
- if (pidsToProcess.size === 0) {
83
- resolve();
84
- }
85
- });
86
- });
87
- }
88
-
89
- /**
90
- * Kill all processes in the tree
91
- * Kills children before parents to ensure clean shutdown
92
- */
93
- function killAll(tree: ProcessTree, signal: NodeJS.Signals): void {
94
- const killed = new Set<number>();
95
-
96
- // Get all PIDs and sort by depth (deepest first)
97
- const allPids = Object.keys(tree).map(Number);
98
-
99
- // Kill children first, then parents
100
- for (const pid of allPids) {
101
- // Kill all children of this pid
102
- for (const childPid of tree[pid]) {
103
- if (!killed.has(childPid)) {
104
- killPid(childPid, signal);
105
- killed.add(childPid);
106
- }
107
- }
108
- }
109
-
110
- // Kill all parent pids
111
- for (const pid of allPids) {
112
- if (!killed.has(pid)) {
113
- killPid(pid, signal);
114
- killed.add(pid);
115
- }
116
- }
117
- }
118
-
119
- /**
120
- * Kill a single process, ignoring ESRCH errors (process already dead)
121
- */
122
- function killPid(pid: number, signal: NodeJS.Signals): void {
123
- try {
124
- process.kill(pid, signal);
125
- } catch (err) {
126
- // ESRCH = No such process (already dead) - ignore this
127
- if ((err as NodeJS.ErrnoException).code !== "ESRCH") {
128
- throw err;
129
- }
130
- }
131
- }