chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import { execFile } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import {
|
||||
readPidFile,
|
||||
isPidRunning,
|
||||
cleanupPidFile,
|
||||
killAllSubprocesses,
|
||||
sleep,
|
||||
} from "../utils/pid.mjs";
|
||||
import { t } from "../i18n.mjs";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
export function registerStop(program) {
|
||||
program
|
||||
.command("stop")
|
||||
.description(t("stop.description"))
|
||||
.action(async (opts) => {
|
||||
const exitCode = await runStopCommand(opts);
|
||||
if (exitCode !== 0) process.exit(exitCode);
|
||||
});
|
||||
}
|
||||
|
||||
export async function runStopCommand(opts = {}) {
|
||||
const pid = readPidFile("server");
|
||||
|
||||
if (pid && isPidRunning(pid)) {
|
||||
console.log(t("stop.stopping", { pid }));
|
||||
try {
|
||||
process.kill(pid, "SIGTERM");
|
||||
|
||||
let waited = 0;
|
||||
while (waited < 5000 && isPidRunning(pid)) {
|
||||
await sleep(100);
|
||||
waited += 100;
|
||||
}
|
||||
|
||||
if (isPidRunning(pid)) {
|
||||
process.kill(pid, "SIGKILL");
|
||||
await sleep(500);
|
||||
}
|
||||
|
||||
killAllSubprocesses();
|
||||
cleanupPidFile("server");
|
||||
console.log(t("stop.stopped"));
|
||||
return 0;
|
||||
} catch (err) {
|
||||
console.error(
|
||||
t("common.error", { message: err instanceof Error ? err.message : String(err) })
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
const port = opts.port ? parseInt(String(opts.port), 10) : 20128;
|
||||
if (pid === null) {
|
||||
console.log(t("stop.portFallback"));
|
||||
await killByPort(port);
|
||||
killAllSubprocesses();
|
||||
cleanupPidFile("server");
|
||||
console.log(t("stop.stopped"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
console.log(t("stop.notRunning"));
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function killByPort(port) {
|
||||
if (process.platform === "win32") return;
|
||||
try {
|
||||
const { stdout } = await execFileAsync("lsof", ["-ti", `:${port}`]);
|
||||
const pids = stdout
|
||||
.trim()
|
||||
.split("\n")
|
||||
.map((p) => parseInt(p, 10))
|
||||
.filter((p) => Number.isFinite(p) && p > 0);
|
||||
|
||||
for (const p of pids) {
|
||||
try {
|
||||
process.kill(p, "SIGTERM");
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (pids.length > 0) {
|
||||
await sleep(1000);
|
||||
for (const p of pids) {
|
||||
try {
|
||||
if (isPidRunning(p)) process.kill(p, "SIGKILL");
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// lsof not available or no process on port
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user