export type { Base, BaseContext } from "./lib/create.ts"; import { toText } from "https://deno.land/std@0.208.0/streams/to_text.ts"; import { lines, log_from, loop_process, LoopProcess, run } from "./lib/utils.ts"; import { ContainerConfig } from "./lib/config.ts"; import { container_paths } from "./lib/paths.ts"; import { container_command, stop_container } from "./lib/nspawn.ts"; import { NginxController } from "./lib/nginx.ts"; const log = log_from("lib"); export type CmdStatus = ReturnType; export function new_cmd_status() { return { kind: "status" as const }; } export type CmdEnable = ReturnType; export function new_cmd_enable(name: string) { return { kind: "enable" as const, name }; } export type CmdDisable = ReturnType; export function new_cmd_disable(name: string) { return { kind: "disable" as const, name }; } export type CmdReload = ReturnType; export function new_cmd_reload(name: string) { return { kind: "reload" as const, name }; } export type CmdStop = ReturnType; export function new_cmd_stop() { return { kind: "stop" as const }; } export type Cmd = CmdStatus | CmdEnable | CmdDisable | CmdReload | CmdStop; export async function daemon_listen(sock_path: string) { const server = Deno.listen({ transport: "unix", path: sock_path }); await Deno.chmod(sock_path, 0o775); await run("chgrp", "wheel", sock_path); const generator = async function* () { for await (const request of server) { const respond = async (message: string) => { try { await request.write(new TextEncoder().encode(message)); } catch (_) { /* bof mais bon */ } }; try { for await (const line of lines(request.readable)) { const cmd = JSON.parse(line) as Cmd; yield { cmd, respond }; } } catch (_) { /* ok tier */ } } }; const result = generator() as ReturnType & { server: typeof server }; result.server = server; return result; } export async function daemon_send(sock_path: string, command: Cmd) { const request = await Deno.connect({ transport: "unix", path: sock_path }); const text = JSON.stringify(command) + "\r\n\r\n"; await request.write(new TextEncoder().encode(text)); return await toText(request.readable); } export class Runner { name; config; nginx; loop; constructor(config: ContainerConfig, nginx: NginxController) { this.name = config.name; this.config = config; this.nginx = nginx; this.loop = null as LoopProcess | null; } async start() { await stop_container(this.name); await this.update_proxies(); this.start_process(); } private start_process() { const paths = container_paths(this.name); const command = container_command(this.name, paths.root, { boot: true, veth: true, redirections: this.config.redirections, cmd_opts: { stdin: "null", stdout: "null", }, syscall_filter: ["add_key", "keyctl", "bpf"], }); this.loop = loop_process(command, { on_start: () => log("container", this.name, "started"), on_stop: () => log("container", this.name, "stopped"), }); } async stop() { await this.loop?.kill(); } async update_proxies() { const paths = container_paths(this.name); await Deno.mkdir(paths.sites, { recursive: true }); const sites = new Set(); for (const redir of this.config.redirections) { if (redir.kind !== "http") continue; await this.nginx.add_proxy(redir.domain, redir.port, paths.sites, redir.tls); sites.add(redir.domain); } for await (const domains of this.nginx.read_all_in_dir(paths.sites)) { if (!sites.has(domains)) this.nginx.remove_proxy(domains, paths.sites); } } }