mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
kill process by session, and add timeout (#2608)
This commit is contained in:
@@ -15,13 +15,6 @@ import * as CP from "node:child_process"
|
||||
|
||||
const cpExec = promisify(CP.exec)
|
||||
const cpExecFile = promisify(CP.execFile)
|
||||
async function psTree(pid: number, overlay: Overlay): Promise<number[]> {
|
||||
const { stdout } = await cpExec(`pstree -p ${pid}`)
|
||||
const regex: RegExp = /\((\d+)\)/g
|
||||
return [...stdout.toString().matchAll(regex)].map(([_all, pid]) =>
|
||||
parseInt(pid),
|
||||
)
|
||||
}
|
||||
type Daemon<
|
||||
Manifest extends SDKManifest,
|
||||
Ids extends string,
|
||||
@@ -81,19 +74,15 @@ export const runDaemon =
|
||||
const pid = childProcess.pid
|
||||
return {
|
||||
async wait() {
|
||||
const pids = pid ? await psTree(pid, overlay) : []
|
||||
try {
|
||||
return await answer
|
||||
} finally {
|
||||
for (const process of pids) {
|
||||
cpExecFile("kill", [`-9`, String(process)]).catch((_) => {})
|
||||
}
|
||||
await cpExecFile("pkill", ["-9", "-s", String(pid)]).catch((_) => {})
|
||||
}
|
||||
},
|
||||
async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) {
|
||||
const pids = pid ? await psTree(pid, overlay) : []
|
||||
try {
|
||||
childProcess.kill(signal)
|
||||
await cpExecFile("pkill", [`-${signal}`, "-s", String(pid)])
|
||||
|
||||
if (timeout > NO_TIMEOUT) {
|
||||
const didTimeout = await Promise.race([
|
||||
@@ -103,7 +92,9 @@ export const runDaemon =
|
||||
answer.then(() => false),
|
||||
])
|
||||
if (didTimeout) {
|
||||
childProcess.kill(SIGKILL)
|
||||
await cpExecFile("pkill", [`-9`, "-s", String(pid)]).catch(
|
||||
(_) => {},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
await answer
|
||||
@@ -111,16 +102,6 @@ export const runDaemon =
|
||||
} finally {
|
||||
await overlay.destroy()
|
||||
}
|
||||
|
||||
try {
|
||||
for (const process of pids) {
|
||||
await cpExecFile("kill", [`-${signal}`, String(process)])
|
||||
}
|
||||
} finally {
|
||||
for (const process of pids) {
|
||||
cpExecFile("kill", [`-9`, String(process)]).catch((_) => {})
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,13 @@ export class Overlay {
|
||||
async exec(
|
||||
command: string[],
|
||||
options?: CommandOptions,
|
||||
): Promise<{ stdout: string | Buffer; stderr: string | Buffer }> {
|
||||
timeoutMs: number | null = 30000,
|
||||
): Promise<{
|
||||
exitCode: number | null
|
||||
exitSignal: NodeJS.Signals | null
|
||||
stdout: string | Buffer
|
||||
stderr: string | Buffer
|
||||
}> {
|
||||
const imageMeta = await fs
|
||||
.readFile(`/media/startos/images/${this.imageId}.json`, {
|
||||
encoding: "utf8",
|
||||
@@ -87,7 +93,7 @@ export class Overlay {
|
||||
workdir = options.cwd
|
||||
delete options.cwd
|
||||
}
|
||||
return await execFile(
|
||||
const child = cp.spawn(
|
||||
"start-cli",
|
||||
[
|
||||
"chroot",
|
||||
@@ -97,8 +103,44 @@ export class Overlay {
|
||||
this.rootfs,
|
||||
...command,
|
||||
],
|
||||
options,
|
||||
options || {},
|
||||
)
|
||||
const pid = child.pid
|
||||
const stdout = { data: "" as string | Buffer }
|
||||
const stderr = { data: "" as string | Buffer }
|
||||
const appendData =
|
||||
(appendTo: { data: string | Buffer }) =>
|
||||
(chunk: string | Buffer | any) => {
|
||||
if (typeof appendTo.data === "string" && typeof chunk === "string") {
|
||||
appendTo.data += chunk
|
||||
} else if (typeof chunk === "string" || chunk instanceof Buffer) {
|
||||
appendTo.data = Buffer.concat([
|
||||
Buffer.from(appendTo.data),
|
||||
Buffer.from(chunk),
|
||||
])
|
||||
} else {
|
||||
console.error("received unexpected chunk", chunk)
|
||||
}
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
child.on("error", reject)
|
||||
if (timeoutMs !== null && pid) {
|
||||
setTimeout(
|
||||
() => execFile("pkill", ["-9", "-s", String(pid)]).catch((_) => {}),
|
||||
timeoutMs,
|
||||
)
|
||||
}
|
||||
child.stdout.on("data", appendData(stdout))
|
||||
child.stderr.on("data", appendData(stderr))
|
||||
child.on("exit", (code, signal) =>
|
||||
resolve({
|
||||
exitCode: code,
|
||||
exitSignal: signal,
|
||||
stdout: stdout.data,
|
||||
stderr: stderr.data,
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
async spawn(
|
||||
|
||||
Reference in New Issue
Block a user