#!/bin/env -S deno run -A --unstable import { daemon_listen, Runner } from "./src/lib.ts"; import { ContainerConfig } from "./src/lib/config.ts"; import { socket_path, state_path } from "./src/lib/paths.ts"; import { log_from, sleep } from "./src/lib/utils.ts"; const log = log_from("daemon"); if (import.meta.main) await main(); async function main() { const state = await create_state(); const server = await daemon_listen(socket_path()); log("listening to", socket_path()); async function finish() { log("stopping"); server.server.close(); for (const runner of state.enabled.values()) { try { await runner.stop(); } catch (_) { /* on s'en fou */ } } await Deno.remove(socket_path()); Deno.exit(0); } Deno.addSignalListener("SIGINT", finish); for await (const { cmd, respond } of server) { log("received", cmd); if (cmd.kind === "status") { await respond(JSON.stringify(state.list())); continue; } if (cmd.kind === "enable") { (async () => { try { await state.enable(cmd.name); await state.save(); await respond("enabled"); } catch (error) { log("experienced failure", error); await respond(`${error}`); } })(); continue; } if (cmd.kind === "disable") { (async () => { try { await state.disable(cmd.name); await state.save(); await respond("disabled"); } catch (error) { log("experienced failure", error); await respond(`${error}`); } })(); continue; } if (cmd.kind === "reload") { (async () => { try { await state.disable(cmd.name); await sleep(500); await state.enable(cmd.name); await respond("reloaded"); } catch (error) { log("experienced failure", error); await respond(`${error}`); } })(); continue; } if (cmd.kind === "stop") { await state.save(); await respond("adios"); await finish(); } await respond("unknown command"); } } async function create_state() { const self = { enabled: new Map(), async enable(name: string) { if (self.enabled.has(name)) throw new Error("Container already enabled"); log("loading container", name); const config = await ContainerConfig.load(name); // TODO if (config === null) throw new Error("can't read config"); log("starting container", name); const runner = new Runner(config); runner.start(); self.enabled.set(name, runner); log("container", name, "started"); }, async disable(name: string) { const container = self.enabled.get(name); if (container === undefined) throw new Error("Container not found"); await container.stop(); self.enabled.delete(name); }, list() { return Array.from(self.enabled.keys()); }, async save() { const content = JSON.stringify({ enabled: self.list() }); await Deno.writeTextFile(state_path(), content); }, }; try { log("trying to recover state from", state_path()); const loaded = JSON.parse(await Deno.readTextFile(state_path())); for (const name of loaded.enabled ?? []) { await self.enable(name); } log("successfully loaded from", state_path()); } catch (error) { log("experienced failure", error); } return self; }