File size: 1,715 Bytes
3144483
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import type { ChildProcess } from "node:child_process";
import { hasErrnoCode } from "../infra/errno.js";
import { signalProcessTree } from "./kill-tree.js";
import { scheduleAdoptedChildZombieReapAfterExit } from "./scoped-child-reaper.js";

export function shouldDetachChildForProcessTree(): boolean {
  return process.platform !== "win32";
}

export function isChildProcessTreeAlive(child: Pick<ChildProcess, "pid">): boolean {
  if (typeof child.pid !== "number" || child.pid <= 0) {
    return false;
  }
  const target = shouldDetachChildForProcessTree() ? -child.pid : child.pid;
  try {
    process.kill(target, 0);
    return true;
  } catch (error) {
    // Only ESRCH proves absence. Permission or probe failures must retain cleanup.
    return !hasErrnoCode(error, "ESRCH");
  }
}

export function signalChildProcessTree(
  child: Pick<ChildProcess, "kill" | "pid" | "exitCode" | "signalCode" | "once">,
  signal: "SIGTERM" | "SIGKILL",
): void {
  if (typeof child.pid === "number" && child.pid > 0) {
    const usedProcessGroup = shouldDetachChildForProcessTree();
    signalProcessTree(child.pid, signal, {
      detached: usedProcessGroup,
    });
    // Tree kills can leave already-exited descendants reparented to us as
    // untracked zombies (#97616). Reap after the Node-tracked root exits so
    // libuv keeps ownership of its wait status — never waitpid the root, and
    // never waitpid(-1).
    scheduleAdoptedChildZombieReapAfterExit(child, usedProcessGroup);
    return;
  }

  child.kill(signal);
}

export function forceKillChildProcessTree(
  child: Pick<ChildProcess, "kill" | "pid" | "exitCode" | "signalCode" | "once">,
): void {
  signalChildProcessTree(child, "SIGKILL");
}