Files
start-os/sdk/lib/mainFn/CommandController.ts
Jade 0b8a142de0 fix: Making the daemons keep up the status. (#2617)
* complete get_primary_url fn

* complete clear_network_interfaces fn

* formatting

* complete remove_address fn

* get_system_smtp wip

* complete get_system_smtp and set_system_smtp

* add SetSystemSmtpParams struct

* add set_system_smtp subcommand

* Remove 'Copy' implementation from `HostAddress`

Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>

* Refactor `get_host_primary` fn and clone  resulting `HostAddress`

Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>

* misc fixes and debug info

* seed hosts with a tor address

* fix: Making the daemons keep up the status.

* wipFix: Making a service start

* fix: Both the start + stop of the service.

* fix: Weird edge case of failure and kids

---------

Co-authored-by: Shadowy Super Coder <musashidisciple@proton.me>
Co-authored-by: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com>
Co-authored-by: Aiden McClelland <me@drbonez.dev>
2024-05-13 16:50:25 +00:00

109 lines
3.1 KiB
TypeScript

import { NO_TIMEOUT, SIGTERM } from "../StartSdk"
import { SDKManifest } from "../manifest/ManifestTypes"
import { Effects, ValidIfNoStupidEscape } from "../types"
import { MountOptions, Overlay } from "../util/Overlay"
import { splitCommand } from "../util/splitCommand"
import { cpExecFile } from "./Daemons"
export class CommandController {
private constructor(
readonly runningAnswer: Promise<unknown>,
readonly overlay: Overlay,
readonly pid: number | undefined,
) {}
static of<Manifest extends SDKManifest>() {
return async <A extends string>(
effects: Effects,
imageId: {
id: Manifest["images"][number]
sharedRun?: boolean
},
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
options: {
mounts?: { path: string; options: MountOptions }[]
overlay?: Overlay
env?:
| {
[variable: string]: string
}
| undefined
cwd?: string | undefined
user?: string | undefined
onStdout?: (x: Buffer) => void
onStderr?: (x: Buffer) => void
},
) => {
const commands = splitCommand(command)
const overlay = options.overlay || (await Overlay.of(effects, imageId))
for (let mount of options.mounts || []) {
await overlay.mount(mount.options, mount.path)
}
const childProcess = await overlay.spawn(commands, {
env: options.env,
})
const answer = new Promise<null>((resolve, reject) => {
childProcess.stdout.on(
"data",
options.onStdout ??
((data: any) => {
console.log(data.toString())
}),
)
childProcess.stderr.on(
"data",
options.onStderr ??
((data: any) => {
console.error(data.toString())
}),
)
childProcess.on("exit", (code: any) => {
if (code === 0) {
return resolve(null)
}
return reject(new Error(`${commands[0]} exited with code ${code}`))
})
})
const pid = childProcess.pid
return new CommandController(answer, overlay, pid)
}
}
async wait() {
try {
return await this.runningAnswer
} finally {
await cpExecFile("pkill", ["-9", "-s", String(this.pid)]).catch((_) => {})
await this.overlay.destroy().catch((_) => {})
}
}
async term({ signal = SIGTERM, timeout = NO_TIMEOUT } = {}) {
try {
await cpExecFile("pkill", [
`-${signal.replace("SIG", "")}`,
"-s",
String(this.pid),
])
if (timeout > NO_TIMEOUT) {
const didTimeout = await Promise.race([
new Promise((resolve) => setTimeout(resolve, timeout)).then(
() => true,
),
this.runningAnswer.then(() => false),
])
if (didTimeout) {
await cpExecFile("pkill", [`-9`, "-s", String(this.pid)]).catch(
(_: any) => {},
)
}
} else {
await this.runningAnswer
}
} finally {
await this.overlay.destroy()
}
}
}