Files

52 lines
2.1 KiB
JavaScript
Executable File

#!/usr/bin/env node
import fs from "node:fs";
import { spawn } from "node:child_process";
const appPort = 4322;
const portHex = appPort.toString(16).toUpperCase().padStart(4, "0");
function listeningSocketInodes() {
const inodes = new Set();
for (const file of ["/proc/net/tcp", "/proc/net/tcp6"]) {
if (!fs.existsSync(file)) continue;
for (const line of fs.readFileSync(file, "utf8").trim().split("\n").slice(1)) {
const columns = line.trim().split(/\s+/);
const localPort = columns[1]?.split(":").at(-1);
const state = columns[3];
const inode = columns[9];
if (localPort === portHex && state === "0A" && inode) inodes.add(inode);
}
}
return inodes;
}
function listenerPids(inodes) {
const pids = [];
for (const entry of fs.readdirSync("/proc").filter((value) => /^\d+$/.test(value))) {
const directory = `/proc/${entry}/fd`;
let descriptors;
try { descriptors = fs.readdirSync(directory); } catch { continue; }
const ownsSocket = descriptors.some((descriptor) => {
try {
const target = fs.readlinkSync(`${directory}/${descriptor}`);
return [...inodes].some((inode) => target === `socket:[${inode}]`);
} catch { return false; }
});
if (ownsSocket && Number(entry) !== process.pid) pids.push(Number(entry));
}
return pids;
}
const delay = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
const alive = (pid) => { try { process.kill(pid, 0); return true; } catch { return false; } };
const pids = listenerPids(listeningSocketInodes());
for (const pid of pids) process.kill(pid, "SIGTERM");
for (let attempt = 0; attempt < 20 && pids.some(alive); attempt += 1) await delay(100);
for (const pid of pids.filter(alive)) process.kill(pid, "SIGKILL");
const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
const child = spawn(npmCommand, ["run", "dev:app"], { stdio: "inherit" });
for (const signal of ["SIGINT", "SIGTERM"]) process.on(signal, () => child.kill(signal));
child.on("exit", (code, signal) => process.exit(signal ? 1 : (code ?? 1)));