feat(daemon): add legacy Clawdis service cleanup
This commit is contained in:
@@ -1,3 +1,10 @@
|
|||||||
export const GATEWAY_LAUNCH_AGENT_LABEL = "com.clawdbot.gateway";
|
export const GATEWAY_LAUNCH_AGENT_LABEL = "com.clawdbot.gateway";
|
||||||
export const GATEWAY_SYSTEMD_SERVICE_NAME = "clawdbot-gateway";
|
export const GATEWAY_SYSTEMD_SERVICE_NAME = "clawdbot-gateway";
|
||||||
export const GATEWAY_WINDOWS_TASK_NAME = "Clawdbot Gateway";
|
export const GATEWAY_WINDOWS_TASK_NAME = "Clawdbot Gateway";
|
||||||
|
export const LEGACY_GATEWAY_LAUNCH_AGENT_LABELS = [
|
||||||
|
"com.steipete.clawdbot.gateway",
|
||||||
|
"com.steipete.clawdis.gateway",
|
||||||
|
"com.clawdis.gateway",
|
||||||
|
];
|
||||||
|
export const LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES = ["clawdis-gateway"];
|
||||||
|
export const LEGACY_GATEWAY_WINDOWS_TASK_NAMES = ["Clawdis Gateway"];
|
||||||
|
|||||||
@@ -3,39 +3,30 @@ import fs from "node:fs/promises";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
import { GATEWAY_LAUNCH_AGENT_LABEL } from "./constants.js";
|
import {
|
||||||
|
GATEWAY_LAUNCH_AGENT_LABEL,
|
||||||
|
LEGACY_GATEWAY_LAUNCH_AGENT_LABELS,
|
||||||
|
} from "./constants.js";
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
const LEGACY_GATEWAY_LAUNCH_AGENT_LABEL = "com.steipete.clawdbot.gateway";
|
|
||||||
|
|
||||||
function resolveHomeDir(env: Record<string, string | undefined>): string {
|
function resolveHomeDir(env: Record<string, string | undefined>): string {
|
||||||
const home = env.HOME?.trim() || env.USERPROFILE?.trim();
|
const home = env.HOME?.trim() || env.USERPROFILE?.trim();
|
||||||
if (!home) throw new Error("Missing HOME");
|
if (!home) throw new Error("Missing HOME");
|
||||||
return home;
|
return home;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveLaunchAgentPlistPathForLabel(
|
||||||
|
env: Record<string, string | undefined>,
|
||||||
|
label: string,
|
||||||
|
): string {
|
||||||
|
const home = resolveHomeDir(env);
|
||||||
|
return path.join(home, "Library", "LaunchAgents", `${label}.plist`);
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveLaunchAgentPlistPath(
|
export function resolveLaunchAgentPlistPath(
|
||||||
env: Record<string, string | undefined>,
|
env: Record<string, string | undefined>,
|
||||||
): string {
|
): string {
|
||||||
const home = resolveHomeDir(env);
|
return resolveLaunchAgentPlistPathForLabel(env, GATEWAY_LAUNCH_AGENT_LABEL);
|
||||||
return path.join(
|
|
||||||
home,
|
|
||||||
"Library",
|
|
||||||
"LaunchAgents",
|
|
||||||
`${GATEWAY_LAUNCH_AGENT_LABEL}.plist`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveLegacyLaunchAgentPlistPath(
|
|
||||||
env: Record<string, string | undefined>,
|
|
||||||
): string {
|
|
||||||
const home = resolveHomeDir(env);
|
|
||||||
return path.join(
|
|
||||||
home,
|
|
||||||
"Library",
|
|
||||||
"LaunchAgents",
|
|
||||||
`${LEGACY_GATEWAY_LAUNCH_AGENT_LABEL}.plist`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveGatewayLogPaths(
|
export function resolveGatewayLogPaths(
|
||||||
@@ -212,6 +203,79 @@ export async function isLaunchAgentLoaded(): Promise<boolean> {
|
|||||||
return res.code === 0;
|
return res.code === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type LegacyLaunchAgent = {
|
||||||
|
label: string;
|
||||||
|
plistPath: string;
|
||||||
|
loaded: boolean;
|
||||||
|
exists: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function findLegacyLaunchAgents(
|
||||||
|
env: Record<string, string | undefined>,
|
||||||
|
): Promise<LegacyLaunchAgent[]> {
|
||||||
|
const domain = resolveGuiDomain();
|
||||||
|
const results: LegacyLaunchAgent[] = [];
|
||||||
|
for (const label of LEGACY_GATEWAY_LAUNCH_AGENT_LABELS) {
|
||||||
|
const plistPath = resolveLaunchAgentPlistPathForLabel(env, label);
|
||||||
|
const res = await execLaunchctl(["print", `${domain}/${label}`]);
|
||||||
|
const loaded = res.code === 0;
|
||||||
|
let exists = false;
|
||||||
|
try {
|
||||||
|
await fs.access(plistPath);
|
||||||
|
exists = true;
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
if (loaded || exists) {
|
||||||
|
results.push({ label, plistPath, loaded, exists });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uninstallLegacyLaunchAgents({
|
||||||
|
env,
|
||||||
|
stdout,
|
||||||
|
}: {
|
||||||
|
env: Record<string, string | undefined>;
|
||||||
|
stdout: NodeJS.WritableStream;
|
||||||
|
}): Promise<LegacyLaunchAgent[]> {
|
||||||
|
const domain = resolveGuiDomain();
|
||||||
|
const agents = await findLegacyLaunchAgents(env);
|
||||||
|
if (agents.length === 0) return agents;
|
||||||
|
|
||||||
|
const home = resolveHomeDir(env);
|
||||||
|
const trashDir = path.join(home, ".Trash");
|
||||||
|
try {
|
||||||
|
await fs.mkdir(trashDir, { recursive: true });
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const agent of agents) {
|
||||||
|
await execLaunchctl(["bootout", domain, agent.plistPath]);
|
||||||
|
await execLaunchctl(["unload", agent.plistPath]);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.access(agent.plistPath);
|
||||||
|
} catch {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dest = path.join(trashDir, `${agent.label}.plist`);
|
||||||
|
try {
|
||||||
|
await fs.rename(agent.plistPath, dest);
|
||||||
|
stdout.write(`Moved legacy LaunchAgent to Trash: ${dest}\n`);
|
||||||
|
} catch {
|
||||||
|
stdout.write(
|
||||||
|
`Legacy LaunchAgent remains at ${agent.plistPath} (could not move)\n`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return agents;
|
||||||
|
}
|
||||||
|
|
||||||
export async function uninstallLaunchAgent({
|
export async function uninstallLaunchAgent({
|
||||||
env,
|
env,
|
||||||
stdout,
|
stdout,
|
||||||
@@ -259,14 +323,19 @@ export async function installLaunchAgent({
|
|||||||
const { logDir, stdoutPath, stderrPath } = resolveGatewayLogPaths(env);
|
const { logDir, stdoutPath, stderrPath } = resolveGatewayLogPaths(env);
|
||||||
await fs.mkdir(logDir, { recursive: true });
|
await fs.mkdir(logDir, { recursive: true });
|
||||||
|
|
||||||
const legacyPlistPath = resolveLegacyLaunchAgentPlistPath(env);
|
|
||||||
const domain = resolveGuiDomain();
|
const domain = resolveGuiDomain();
|
||||||
await execLaunchctl(["bootout", domain, legacyPlistPath]);
|
for (const legacyLabel of LEGACY_GATEWAY_LAUNCH_AGENT_LABELS) {
|
||||||
await execLaunchctl(["unload", legacyPlistPath]);
|
const legacyPlistPath = resolveLaunchAgentPlistPathForLabel(
|
||||||
try {
|
env,
|
||||||
await fs.unlink(legacyPlistPath);
|
legacyLabel,
|
||||||
} catch {
|
);
|
||||||
// ignore
|
await execLaunchctl(["bootout", domain, legacyPlistPath]);
|
||||||
|
await execLaunchctl(["unload", legacyPlistPath]);
|
||||||
|
try {
|
||||||
|
await fs.unlink(legacyPlistPath);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const plistPath = resolveLaunchAgentPlistPath(env);
|
const plistPath = resolveLaunchAgentPlistPath(env);
|
||||||
|
|||||||
103
src/daemon/legacy.ts
Normal file
103
src/daemon/legacy.ts
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import {
|
||||||
|
findLegacyLaunchAgents,
|
||||||
|
uninstallLegacyLaunchAgents,
|
||||||
|
} from "./launchd.js";
|
||||||
|
import {
|
||||||
|
findLegacyScheduledTasks,
|
||||||
|
uninstallLegacyScheduledTasks,
|
||||||
|
} from "./schtasks.js";
|
||||||
|
import {
|
||||||
|
findLegacySystemdUnits,
|
||||||
|
uninstallLegacySystemdUnits,
|
||||||
|
} from "./systemd.js";
|
||||||
|
|
||||||
|
export type LegacyGatewayService = {
|
||||||
|
platform: "darwin" | "linux" | "win32";
|
||||||
|
label: string;
|
||||||
|
detail: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function formatLegacyLaunchAgents(
|
||||||
|
agents: Awaited<ReturnType<typeof findLegacyLaunchAgents>>,
|
||||||
|
): LegacyGatewayService[] {
|
||||||
|
return agents.map((agent) => ({
|
||||||
|
platform: "darwin",
|
||||||
|
label: agent.label,
|
||||||
|
detail: [
|
||||||
|
agent.loaded ? "loaded" : "not loaded",
|
||||||
|
agent.exists ? `plist: ${agent.plistPath}` : "plist missing",
|
||||||
|
].join(", "),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLegacySystemdUnits(
|
||||||
|
units: Awaited<ReturnType<typeof findLegacySystemdUnits>>,
|
||||||
|
): LegacyGatewayService[] {
|
||||||
|
return units.map((unit) => ({
|
||||||
|
platform: "linux",
|
||||||
|
label: `${unit.name}.service`,
|
||||||
|
detail: [
|
||||||
|
unit.enabled ? "enabled" : "disabled",
|
||||||
|
unit.exists ? `unit: ${unit.unitPath}` : "unit missing",
|
||||||
|
].join(", "),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatLegacyScheduledTasks(
|
||||||
|
tasks: Awaited<ReturnType<typeof findLegacyScheduledTasks>>,
|
||||||
|
): LegacyGatewayService[] {
|
||||||
|
return tasks.map((task) => ({
|
||||||
|
platform: "win32",
|
||||||
|
label: task.name,
|
||||||
|
detail: [
|
||||||
|
task.installed ? "installed" : "not installed",
|
||||||
|
task.scriptExists ? `script: ${task.scriptPath}` : "script missing",
|
||||||
|
].join(", "),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findLegacyGatewayServices(
|
||||||
|
env: Record<string, string | undefined>,
|
||||||
|
): Promise<LegacyGatewayService[]> {
|
||||||
|
if (process.platform === "darwin") {
|
||||||
|
const agents = await findLegacyLaunchAgents(env);
|
||||||
|
return formatLegacyLaunchAgents(agents);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
const units = await findLegacySystemdUnits(env);
|
||||||
|
return formatLegacySystemdUnits(units);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
const tasks = await findLegacyScheduledTasks(env);
|
||||||
|
return formatLegacyScheduledTasks(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uninstallLegacyGatewayServices({
|
||||||
|
env,
|
||||||
|
stdout,
|
||||||
|
}: {
|
||||||
|
env: Record<string, string | undefined>;
|
||||||
|
stdout: NodeJS.WritableStream;
|
||||||
|
}): Promise<LegacyGatewayService[]> {
|
||||||
|
if (process.platform === "darwin") {
|
||||||
|
const agents = await uninstallLegacyLaunchAgents({ env, stdout });
|
||||||
|
return formatLegacyLaunchAgents(agents);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
const units = await uninstallLegacySystemdUnits({ env, stdout });
|
||||||
|
return formatLegacySystemdUnits(units);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
const tasks = await uninstallLegacyScheduledTasks({ env, stdout });
|
||||||
|
return formatLegacyScheduledTasks(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
@@ -3,7 +3,10 @@ import fs from "node:fs/promises";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
import { GATEWAY_WINDOWS_TASK_NAME } from "./constants.js";
|
import {
|
||||||
|
GATEWAY_WINDOWS_TASK_NAME,
|
||||||
|
LEGACY_GATEWAY_WINDOWS_TASK_NAMES,
|
||||||
|
} from "./constants.js";
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
@@ -20,6 +23,13 @@ function resolveTaskScriptPath(
|
|||||||
return path.join(home, ".clawdbot", "gateway.cmd");
|
return path.join(home, ".clawdbot", "gateway.cmd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveLegacyTaskScriptPath(
|
||||||
|
env: Record<string, string | undefined>,
|
||||||
|
): string {
|
||||||
|
const home = resolveHomeDir(env);
|
||||||
|
return path.join(home, ".clawdis", "gateway.cmd");
|
||||||
|
}
|
||||||
|
|
||||||
function quoteCmdArg(value: string): string {
|
function quoteCmdArg(value: string): string {
|
||||||
if (!/[ \t"]/g.test(value)) return value;
|
if (!/[ \t"]/g.test(value)) return value;
|
||||||
return `"${value.replace(/"/g, '\\"')}"`;
|
return `"${value.replace(/"/g, '\\"')}"`;
|
||||||
@@ -242,3 +252,79 @@ export async function isScheduledTaskInstalled(): Promise<boolean> {
|
|||||||
const res = await execSchtasks(["/Query", "/TN", GATEWAY_WINDOWS_TASK_NAME]);
|
const res = await execSchtasks(["/Query", "/TN", GATEWAY_WINDOWS_TASK_NAME]);
|
||||||
return res.code === 0;
|
return res.code === 0;
|
||||||
}
|
}
|
||||||
|
export type LegacyScheduledTask = {
|
||||||
|
name: string;
|
||||||
|
scriptPath: string;
|
||||||
|
installed: boolean;
|
||||||
|
scriptExists: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function findLegacyScheduledTasks(
|
||||||
|
env: Record<string, string | undefined>,
|
||||||
|
): Promise<LegacyScheduledTask[]> {
|
||||||
|
const results: LegacyScheduledTask[] = [];
|
||||||
|
let schtasksAvailable = true;
|
||||||
|
try {
|
||||||
|
await assertSchtasksAvailable();
|
||||||
|
} catch {
|
||||||
|
schtasksAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const name of LEGACY_GATEWAY_WINDOWS_TASK_NAMES) {
|
||||||
|
const scriptPath = resolveLegacyTaskScriptPath(env);
|
||||||
|
let installed = false;
|
||||||
|
if (schtasksAvailable) {
|
||||||
|
const res = await execSchtasks(["/Query", "/TN", name]);
|
||||||
|
installed = res.code === 0;
|
||||||
|
}
|
||||||
|
let scriptExists = false;
|
||||||
|
try {
|
||||||
|
await fs.access(scriptPath);
|
||||||
|
scriptExists = true;
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
if (installed || scriptExists) {
|
||||||
|
results.push({ name, scriptPath, installed, scriptExists });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uninstallLegacyScheduledTasks({
|
||||||
|
env,
|
||||||
|
stdout,
|
||||||
|
}: {
|
||||||
|
env: Record<string, string | undefined>;
|
||||||
|
stdout: NodeJS.WritableStream;
|
||||||
|
}): Promise<LegacyScheduledTask[]> {
|
||||||
|
const tasks = await findLegacyScheduledTasks(env);
|
||||||
|
if (tasks.length === 0) return tasks;
|
||||||
|
|
||||||
|
let schtasksAvailable = true;
|
||||||
|
try {
|
||||||
|
await assertSchtasksAvailable();
|
||||||
|
} catch {
|
||||||
|
schtasksAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const task of tasks) {
|
||||||
|
if (schtasksAvailable && task.installed) {
|
||||||
|
await execSchtasks(["/Delete", "/F", "/TN", task.name]);
|
||||||
|
} else if (!schtasksAvailable && task.installed) {
|
||||||
|
stdout.write(
|
||||||
|
`schtasks unavailable; unable to remove legacy task: ${task.name}\n`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.unlink(task.scriptPath);
|
||||||
|
stdout.write(`Removed legacy task script: ${task.scriptPath}\n`);
|
||||||
|
} catch {
|
||||||
|
stdout.write(`Legacy task script not found at ${task.scriptPath}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,10 @@ import fs from "node:fs/promises";
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { promisify } from "node:util";
|
import { promisify } from "node:util";
|
||||||
|
|
||||||
import { GATEWAY_SYSTEMD_SERVICE_NAME } from "./constants.js";
|
import {
|
||||||
|
GATEWAY_SYSTEMD_SERVICE_NAME,
|
||||||
|
LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES,
|
||||||
|
} from "./constants.js";
|
||||||
|
|
||||||
const execFileAsync = promisify(execFile);
|
const execFileAsync = promisify(execFile);
|
||||||
|
|
||||||
@@ -13,17 +16,18 @@ function resolveHomeDir(env: Record<string, string | undefined>): string {
|
|||||||
return home;
|
return home;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveSystemdUnitPathForName(
|
||||||
|
env: Record<string, string | undefined>,
|
||||||
|
name: string,
|
||||||
|
): string {
|
||||||
|
const home = resolveHomeDir(env);
|
||||||
|
return path.join(home, ".config", "systemd", "user", `${name}.service`);
|
||||||
|
}
|
||||||
|
|
||||||
function resolveSystemdUnitPath(
|
function resolveSystemdUnitPath(
|
||||||
env: Record<string, string | undefined>,
|
env: Record<string, string | undefined>,
|
||||||
): string {
|
): string {
|
||||||
const home = resolveHomeDir(env);
|
return resolveSystemdUnitPathForName(env, GATEWAY_SYSTEMD_SERVICE_NAME);
|
||||||
return path.join(
|
|
||||||
home,
|
|
||||||
".config",
|
|
||||||
"systemd",
|
|
||||||
"user",
|
|
||||||
`${GATEWAY_SYSTEMD_SERVICE_NAME}.service`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function systemdEscapeArg(value: string): string {
|
function systemdEscapeArg(value: string): string {
|
||||||
@@ -276,3 +280,82 @@ export async function isSystemdServiceEnabled(): Promise<boolean> {
|
|||||||
const res = await execSystemctl(["--user", "is-enabled", unitName]);
|
const res = await execSystemctl(["--user", "is-enabled", unitName]);
|
||||||
return res.code === 0;
|
return res.code === 0;
|
||||||
}
|
}
|
||||||
|
export type LegacySystemdUnit = {
|
||||||
|
name: string;
|
||||||
|
unitPath: string;
|
||||||
|
enabled: boolean;
|
||||||
|
exists: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function isSystemctlAvailable(): Promise<boolean> {
|
||||||
|
const res = await execSystemctl(["--user", "status"]);
|
||||||
|
if (res.code === 0) return true;
|
||||||
|
const detail = `${res.stderr || res.stdout}`.toLowerCase();
|
||||||
|
return !detail.includes("not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function findLegacySystemdUnits(
|
||||||
|
env: Record<string, string | undefined>,
|
||||||
|
): Promise<LegacySystemdUnit[]> {
|
||||||
|
const results: LegacySystemdUnit[] = [];
|
||||||
|
const systemctlAvailable = await isSystemctlAvailable();
|
||||||
|
for (const name of LEGACY_GATEWAY_SYSTEMD_SERVICE_NAMES) {
|
||||||
|
const unitPath = resolveSystemdUnitPathForName(env, name);
|
||||||
|
let exists = false;
|
||||||
|
try {
|
||||||
|
await fs.access(unitPath);
|
||||||
|
exists = true;
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
let enabled = false;
|
||||||
|
if (systemctlAvailable) {
|
||||||
|
const res = await execSystemctl([
|
||||||
|
"--user",
|
||||||
|
"is-enabled",
|
||||||
|
`${name}.service`,
|
||||||
|
]);
|
||||||
|
enabled = res.code === 0;
|
||||||
|
}
|
||||||
|
if (exists || enabled) {
|
||||||
|
results.push({ name, unitPath, enabled, exists });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uninstallLegacySystemdUnits({
|
||||||
|
env,
|
||||||
|
stdout,
|
||||||
|
}: {
|
||||||
|
env: Record<string, string | undefined>;
|
||||||
|
stdout: NodeJS.WritableStream;
|
||||||
|
}): Promise<LegacySystemdUnit[]> {
|
||||||
|
const units = await findLegacySystemdUnits(env);
|
||||||
|
if (units.length === 0) return units;
|
||||||
|
|
||||||
|
const systemctlAvailable = await isSystemctlAvailable();
|
||||||
|
for (const unit of units) {
|
||||||
|
if (systemctlAvailable) {
|
||||||
|
await execSystemctl([
|
||||||
|
"--user",
|
||||||
|
"disable",
|
||||||
|
"--now",
|
||||||
|
`${unit.name}.service`,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
stdout.write(
|
||||||
|
`systemctl unavailable; removed legacy unit file only: ${unit.name}.service\n`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fs.unlink(unit.unitPath);
|
||||||
|
stdout.write(`Removed legacy systemd service: ${unit.unitPath}\n`);
|
||||||
|
} catch {
|
||||||
|
stdout.write(`Legacy systemd unit not found at ${unit.unitPath}\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return units;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user