import os from "node:os"; import { runCommandWithTimeout, runExec } from "../process/exec.js"; function resolveLoginctlUser(env: Record): string | null { const fromEnv = env.USER?.trim() || env.LOGNAME?.trim(); if (fromEnv) return fromEnv; try { return os.userInfo().username; } catch { return null; } } export type SystemdUserLingerStatus = { user: string; linger: "yes" | "no"; }; export async function readSystemdUserLingerStatus( env: Record, ): Promise { const user = resolveLoginctlUser(env); if (!user) return null; try { const { stdout } = await runExec("loginctl", ["show-user", user, "-p", "Linger"], { timeoutMs: 5_000, }); const line = stdout .split("\n") .map((entry) => entry.trim()) .find((entry) => entry.startsWith("Linger=")); const value = line?.split("=")[1]?.trim().toLowerCase(); if (value === "yes" || value === "no") { return { user, linger: value }; } } catch { // ignore; loginctl may be unavailable } return null; } export async function enableSystemdUserLinger(params: { env: Record; user?: string; sudoMode?: "prompt" | "non-interactive"; }): Promise<{ ok: boolean; stdout: string; stderr: string; code: number }> { const user = params.user ?? resolveLoginctlUser(params.env); if (!user) { return { ok: false, stdout: "", stderr: "Missing user", code: 1 }; } const needsSudo = typeof process.getuid === "function" ? process.getuid() !== 0 : true; const sudoArgs = needsSudo && params.sudoMode !== undefined ? ["sudo", ...(params.sudoMode === "non-interactive" ? ["-n"] : [])] : []; const argv = [...sudoArgs, "loginctl", "enable-linger", user]; try { const result = await runCommandWithTimeout(argv, { timeoutMs: 30_000 }); return { ok: result.code === 0, stdout: result.stdout, stderr: result.stderr, code: result.code ?? 1, }; } catch (error) { const message = error instanceof Error ? error.message : String(error); return { ok: false, stdout: "", stderr: message, code: 1 }; } }