mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 12:11:56 +00:00
wait for whole session to exit when sigterm (#2620)
* wait for whole session to exit when sigterm * fix lint * rename poorly named variable
This commit is contained in:
@@ -91,7 +91,7 @@ export type ServiceInterfaceType = "ui" | "p2p" | "api"
|
|||||||
export type MainEffects = Effects & { _type: "main" }
|
export type MainEffects = Effects & { _type: "main" }
|
||||||
export type Signals = NodeJS.Signals
|
export type Signals = NodeJS.Signals
|
||||||
export const SIGTERM: Signals = "SIGTERM"
|
export const SIGTERM: Signals = "SIGTERM"
|
||||||
export const SIGKILL: Signals = "SIGTERM"
|
export const SIGKILL: Signals = "SIGKILL"
|
||||||
export const NO_TIMEOUT = -1
|
export const NO_TIMEOUT = -1
|
||||||
|
|
||||||
function removeConstType<E>() {
|
function removeConstType<E>() {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { NO_TIMEOUT, SIGTERM } from "../StartSdk"
|
import { NO_TIMEOUT, SIGKILL, SIGTERM } from "../StartSdk"
|
||||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||||
import { Effects, ValidIfNoStupidEscape } from "../types"
|
import { Effects, ValidIfNoStupidEscape } from "../types"
|
||||||
import { MountOptions, Overlay } from "../util/Overlay"
|
import { MountOptions, Overlay } from "../util/Overlay"
|
||||||
import { splitCommand } from "../util/splitCommand"
|
import { splitCommand } from "../util/splitCommand"
|
||||||
import { cpExecFile } from "./Daemons"
|
import { cpExecFile, cpExec } from "./Daemons"
|
||||||
|
|
||||||
export class CommandController {
|
export class CommandController {
|
||||||
private constructor(
|
private constructor(
|
||||||
@@ -74,11 +74,16 @@ export class CommandController {
|
|||||||
try {
|
try {
|
||||||
return await this.runningAnswer
|
return await this.runningAnswer
|
||||||
} finally {
|
} finally {
|
||||||
await cpExecFile("pkill", ["-9", "-s", String(this.pid)]).catch((_) => {})
|
if (this.pid !== undefined) {
|
||||||
|
await cpExecFile("pkill", ["-9", "-s", String(this.pid)]).catch(
|
||||||
|
(_) => {},
|
||||||
|
)
|
||||||
|
}
|
||||||
await this.overlay.destroy().catch((_) => {})
|
await this.overlay.destroy().catch((_) => {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) {
|
async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) {
|
||||||
|
if (this.pid === undefined) return
|
||||||
try {
|
try {
|
||||||
await cpExecFile("pkill", [
|
await cpExecFile("pkill", [
|
||||||
`-${signal.replace("SIG", "")}`,
|
`-${signal.replace("SIG", "")}`,
|
||||||
@@ -86,23 +91,58 @@ export class CommandController {
|
|||||||
String(this.pid),
|
String(this.pid),
|
||||||
])
|
])
|
||||||
|
|
||||||
if (timeout > NO_TIMEOUT) {
|
const didTimeout = await waitSession(this.pid, timeout)
|
||||||
const didTimeout = await Promise.race([
|
if (didTimeout) {
|
||||||
new Promise((resolve) => setTimeout(resolve, timeout)).then(
|
await cpExecFile("pkill", [`-9`, "-s", String(this.pid)]).catch(
|
||||||
() => true,
|
(_) => {},
|
||||||
),
|
)
|
||||||
this.runningAnswer.then(() => false),
|
|
||||||
])
|
|
||||||
if (didTimeout) {
|
|
||||||
await cpExecFile("pkill", [`-9`, "-s", String(this.pid)]).catch(
|
|
||||||
(_: any) => {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await this.runningAnswer
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await this.overlay.destroy()
|
await this.overlay.destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function waitSession(
|
||||||
|
sid: number,
|
||||||
|
timeout = NO_TIMEOUT,
|
||||||
|
interval = 100,
|
||||||
|
): Promise<boolean> {
|
||||||
|
let nextInterval = interval * 2
|
||||||
|
if (timeout >= 0 && timeout < nextInterval) {
|
||||||
|
nextInterval = timeout
|
||||||
|
}
|
||||||
|
let nextTimeout = timeout
|
||||||
|
if (timeout > 0) {
|
||||||
|
if (timeout >= interval) {
|
||||||
|
nextTimeout -= interval
|
||||||
|
} else {
|
||||||
|
nextTimeout = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let next: NodeJS.Timeout | null = null
|
||||||
|
if (timeout !== 0) {
|
||||||
|
next = setTimeout(() => {
|
||||||
|
waitSession(sid, nextTimeout, nextInterval).then(resolve, reject)
|
||||||
|
}, interval)
|
||||||
|
}
|
||||||
|
cpExecFile("ps", [`--sid=${sid}`, "-o", "--pid="]).then(
|
||||||
|
(_) => {
|
||||||
|
if (timeout === 0) {
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(e) => {
|
||||||
|
if (next) {
|
||||||
|
clearTimeout(next)
|
||||||
|
}
|
||||||
|
if (typeof e === "object" && e && "code" in e && e.code) {
|
||||||
|
resolve(false)
|
||||||
|
} else {
|
||||||
|
reject(e)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { HealthDaemon } from "./HealthDaemon"
|
|||||||
import { Daemon } from "./Daemon"
|
import { Daemon } from "./Daemon"
|
||||||
import { CommandController } from "./CommandController"
|
import { CommandController } from "./CommandController"
|
||||||
|
|
||||||
const cpExec = promisify(CP.exec)
|
export const cpExec = promisify(CP.exec)
|
||||||
export const cpExecFile = promisify(CP.execFile)
|
export const cpExecFile = promisify(CP.execFile)
|
||||||
export type Ready = {
|
export type Ready = {
|
||||||
display: string | null
|
display: string | null
|
||||||
|
|||||||
Reference in New Issue
Block a user