systemd-ts 261.2.1 → 262.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/test.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { j as CommandExecutor, n as Systemd } from "./index-gV6lNweL.mjs";
1
+ import { j as CommandExecutor, n as Systemd } from "./index-4VL-jAGf.mjs";
2
2
  //#region src/test/host.d.ts
3
3
  interface TestHostInfo {
4
4
  readonly backend: TestHostBackendName;
@@ -9,36 +9,36 @@ interface TestHostInfo {
9
9
  readonly stateRoot: string;
10
10
  }
11
11
  type TestHostBackendName = `colima` | `docker`;
12
- declare function getTestHostInfo(): TestHostInfo;
13
- declare function ensureTestHost(): Promise<TestHostInfo>;
14
- declare function closeTestHost(): void;
15
- declare function runGuestCommand(script: string): Promise<string>;
16
- declare function runIsolatedGuestCommand(script: string): Promise<string>;
12
+ export declare function getTestHostInfo(): TestHostInfo;
13
+ export declare function ensureTestHost(): Promise<TestHostInfo>;
14
+ export declare function closeTestHost(): void;
15
+ export declare function runGuestCommand(script: string): Promise<string>;
16
+ export declare function runIsolatedGuestCommand(script: string): Promise<string>;
17
17
  //#endregion
18
18
  //#region src/test/harness.d.ts
19
19
  /**
20
20
  * Creates a command executor that runs argv-style commands inside the current
21
21
  * test host guest shell.
22
22
  */
23
- declare function createGuestCommandExecutor(runCommand: (script: string) => Promise<string>): CommandExecutor;
23
+ export declare function createGuestCommandExecutor(runCommand: (script: string) => Promise<string>): CommandExecutor;
24
24
  /**
25
25
  * Executor that reuses the warm guest shell for fast sandbox-backed tests.
26
26
  */
27
- declare const guestCommandExecutor: CommandExecutor;
27
+ export declare const guestCommandExecutor: CommandExecutor;
28
28
  /**
29
29
  * Executor that runs each guest command in a fresh isolated guest shell.
30
30
  */
31
- declare const isolatedGuestCommandExecutor: CommandExecutor;
31
+ export declare const isolatedGuestCommandExecutor: CommandExecutor;
32
32
  /**
33
33
  * Creates a `Systemd` instance scoped to the active test sandbox and backed by
34
34
  * the warm guest-shell executor.
35
35
  */
36
- declare function sandboxSystemd(): Systemd;
36
+ export declare function sandboxSystemd(): Systemd;
37
37
  /**
38
38
  * Creates a `Systemd` instance scoped to the active test sandbox and backed by
39
39
  * a fresh guest shell per command.
40
40
  */
41
- declare function isolatedSandboxSystemd(): Systemd;
41
+ export declare function isolatedSandboxSystemd(): Systemd;
42
42
  //#endregion
43
43
  //#region src/test/sandbox.d.ts
44
44
  interface TestSandbox {
@@ -48,9 +48,9 @@ interface TestSandbox {
48
48
  readonly rootDir: string;
49
49
  readonly workDir: string;
50
50
  }
51
- declare function createTestSandbox(testName?: string): Promise<TestSandbox>;
52
- declare function destroyCurrentTestSandbox(): Promise<void>;
53
- declare function useCurrentTestSandbox(): TestSandbox;
51
+ export declare function createTestSandbox(testName?: string): Promise<TestSandbox>;
52
+ export declare function destroyCurrentTestSandbox(): Promise<void>;
53
+ export declare function useCurrentTestSandbox(): TestSandbox;
54
54
  //#endregion
55
- export { type TestHostInfo, type TestSandbox, closeTestHost, createGuestCommandExecutor, createTestSandbox, destroyCurrentTestSandbox, ensureTestHost, getTestHostInfo, guestCommandExecutor, isolatedGuestCommandExecutor, isolatedSandboxSystemd, runGuestCommand, runIsolatedGuestCommand, sandboxSystemd, useCurrentTestSandbox };
55
+ export type { TestHostInfo, TestSandbox };
56
56
  //# sourceMappingURL=test.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"test.d.mts","names":[],"sources":["../src/test/host.ts","../src/test/harness.ts","../src/test/sandbox.ts"],"mappings":";;UAwBiB;WACN,SAAS;WACT;WACA;WACA;WACA;WACA;;KAGN;iBAYW,mBAAmB;iBAInB,kBAAkB,QAAQ;iBAK1B;iBAIM,gBAAgB,iBAAiB;iBAKjC,wBAAwB,iBAAiB;;;;;;;iBCjD/C,2BACd,aAAa,mBAAmB,kBAC/B;;;;cAqBU,sBAAsB;;;;cAKtB,8BAA8B;;;;;iBAO3B,kBAAkB;;;;;iBAclB,0BAA0B;;;UC3DzB;WACN;WACA;WACA;WACA;WACA;;iBASW,kBAAkB,oBAAoB,QAAQ;iBAyB9C,6BAA6B;iBAgCnC,yBAAyB"}
1
+ {"version":3,"file":"test.d.mts","names":[],"sources":["../src/test/host.ts","../src/test/harness.ts","../src/test/sandbox.ts"],"mappings":";;UAwBiB;WACN,SAAS;WACT;WACA;WACA;WACA;WACA;;KAGN;wBAYW,mBAAmB;wBAInB,kBAAkB,QAAQ;wBAK1B;wBAIM,gBAAgB,iBAAiB;wBAKjC,wBAAwB,iBAAiB;;;;;;;wBCjD/C,2BACd,aAAa,mBAAmB,kBAC/B;;;;qBAqBU,sBAAsB;;;;qBAKtB,8BAA8B;;;;;wBAO3B,kBAAkB;;;;;wBAclB,0BAA0B;;;UC3DzB;WACN;WACA;WACA;WACA;WACA;;wBASW,kBAAkB,oBAAoB,QAAQ;wBAyB9C,6BAA6B;wBAgCnC,yBAAyB"}
package/dist/test.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as Systemd } from "./main-DqJ5f7Al.mjs";
1
+ import { n as Systemd } from "./main-BlzhT1eg.mjs";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { execFile, spawn } from "node:child_process";
4
4
  import { promisify } from "node:util";
@@ -6,10 +6,10 @@ import { createHash, randomUUID } from "node:crypto";
6
6
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
7
7
  //#region src/test/host.ts
8
8
  const execFileAsync = promisify(execFile);
9
- const commandMaxBufferBytes = 16 * 1024 * 1024;
10
- const dockerBuildTimeoutMs = 300 * 1e3;
11
- const dockerExecTimeoutMs = 60 * 1e3;
12
- const guestCommandTimeoutMs = 10 * 1e3;
9
+ const commandMaxBufferBytes = 16777216;
10
+ const dockerBuildTimeoutMs = 3e5;
11
+ const dockerExecTimeoutMs = 6e4;
12
+ const guestCommandTimeoutMs = 1e4;
13
13
  const repoRoot = fileURLToPath(new URL(`../../../../`, import.meta.url));
14
14
  const dockerfilePath = fileURLToPath(new URL(`./systemd-container.Dockerfile`, import.meta.url));
15
15
  const selectedBackendName = resolveBackendName(process.env[`SYSTEMD_TS_TEST_HOST_BACKEND`]);
package/dist/test.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"test.mjs","names":["delay","shellQuote","shellQuote"],"sources":["../src/test/host.ts","../src/test/sandbox.ts","../src/test/harness.ts"],"sourcesContent":["import { execFile, spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\nconst commandMaxBufferBytes = 16 * 1024 * 1024;\nconst dockerBuildTimeoutMs = 5 * 60 * 1_000;\nconst dockerExecTimeoutMs = 60 * 1_000;\nconst guestCommandTimeoutMs = 10 * 1_000;\nconst repoRoot = fileURLToPath(new URL(`../../../../`, import.meta.url));\nconst dockerfilePath = fileURLToPath(new URL(`./systemd-container.Dockerfile`, import.meta.url));\nconst selectedBackendName = resolveBackendName(process.env[`SYSTEMD_TS_TEST_HOST_BACKEND`]);\nconst colimaHome = process.env[`COLIMA_HOME`] ?? `${repoRoot}.colima`;\nconst limaHome = process.env[`LIMA_HOME`] ?? `${colimaHome}/_lima`;\nconst colimaProfile = process.env[`COLIMA_PROFILE`] ?? `systemd-ts`;\nconst dockerStateRoot = process.env[`SYSTEMD_TS_TEST_HOST_ROOT`] ?? `${repoRoot}.docker`;\nconst dockerImage = process.env[`SYSTEMD_TS_TEST_DOCKER_IMAGE`] ?? `systemd-ts-test-host:local`;\nconst dockerUser = process.env[`SYSTEMD_TS_TEST_DOCKER_USER`] ?? `runner`;\nconst dockerConfiguredUserId = process.env[`SYSTEMD_TS_TEST_DOCKER_UID`];\nconst dockerContainer =\n process.env[`SYSTEMD_TS_TEST_DOCKER_CONTAINER`] ?? defaultDockerContainerName();\n\nexport interface TestHostInfo {\n readonly backend: TestHostBackendName;\n readonly colimaHome?: string;\n readonly colimaProfile?: string;\n readonly limaHome?: string;\n readonly repoRoot: string;\n readonly stateRoot: string;\n}\n\ntype TestHostBackendName = `colima` | `docker`;\n\ninterface TestHostBackend {\n readonly info: TestHostInfo;\n ensureHost(): Promise<void>;\n createGuestShellProcess(): ChildProcessWithoutNullStreams;\n runIsolatedCommand(script: string): Promise<string>;\n}\n\nlet ensuredHost: Promise<TestHostInfo> | undefined;\nlet guestShell: GuestShell | undefined;\n\nexport function getTestHostInfo(): TestHostInfo {\n return backend.info;\n}\n\nexport function ensureTestHost(): Promise<TestHostInfo> {\n ensuredHost ??= ensureTestHostInner();\n return ensuredHost;\n}\n\nexport function closeTestHost(): void {\n closeGuestShell(`warmup complete`);\n}\n\nexport async function runGuestCommand(script: string): Promise<string> {\n const shell = getGuestShell();\n return shell.run(script);\n}\n\nexport async function runIsolatedGuestCommand(script: string): Promise<string> {\n return backend.runIsolatedCommand(script);\n}\n\nasync function ensureTestHostInner(): Promise<TestHostInfo> {\n logTestHost(`Ensuring ${backend.info.backend} test host`);\n await backend.ensureHost();\n logTestHost(`Waiting for systemd --user readiness`);\n await waitForUserSystemd();\n logTestHost(`Test host ready`);\n return getTestHostInfo();\n}\n\nasync function waitForUserSystemd(): Promise<void> {\n for (let attempt = 0; attempt < 30; attempt += 1) {\n const state = await runGuestCommandWithTimeout(\n `printf 'pid1=%s\\n' \"$(ps -p 1 -o comm=)\"; systemctl --user is-system-running || true; printf 'xdg=%s\\n' \"$XDG_RUNTIME_DIR\"`,\n guestCommandTimeoutMs,\n );\n\n if (\n state.includes(`pid1=systemd`) &&\n state.includes(`xdg=/run/user/`) &&\n [`running`, `degraded`].some((status) => state.includes(status))\n ) {\n logTestHost(\n `systemd --user ready after ${attempt + 1} attempt${attempt === 0 ? `` : `s`}: ${summarizeSystemdState(state)}`,\n );\n return;\n }\n\n if (attempt === 0 || (attempt + 1) % 5 === 0) {\n logTestHost(\n `systemd --user not ready yet (${attempt + 1}/30): ${summarizeSystemdState(state)}`,\n );\n }\n\n await delay(1_000);\n }\n\n throw new Error(`Timed out waiting for systemd --user in the ${backend.info.backend} guest`);\n}\n\nfunction getGuestShell(): GuestShell {\n guestShell ??= new GuestShell(\n () => backend.createGuestShellProcess(),\n () => {\n if (guestShell !== undefined) {\n guestShell = undefined;\n }\n },\n );\n return guestShell;\n}\n\nfunction closeGuestShell(reason: string): void {\n if (guestShell === undefined) {\n return;\n }\n\n logTestHost(`Resetting guest shell: ${reason}`);\n guestShell.close();\n guestShell = undefined;\n}\n\nasync function runGuestCommandWithTimeout(script: string, timeoutMs: number): Promise<string> {\n const shell = getGuestShell();\n try {\n return await shell.run(script, timeoutMs);\n } catch (error) {\n if (error instanceof Error && error.message.includes(`timed out`)) {\n closeGuestShell(error.message);\n }\n\n throw error;\n }\n}\n\nfunction mergeOutput(stdout: string, stderr: string): string {\n return [stdout, stderr].filter((output) => output.length > 0).join(`\\n`);\n}\n\nfunction summarizeSystemdState(state: string): string {\n return state\n .split(`\\n`)\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .join(`; `);\n}\n\nfunction inferBackendName(): TestHostBackendName {\n if (process.platform === `darwin`) {\n return `colima`;\n }\n\n if (process.platform === `linux`) {\n return `docker`;\n }\n\n throw new Error(\n `Unsupported test host platform: ${process.platform}. Set SYSTEMD_TS_TEST_HOST_BACKEND explicitly if you have a compatible backend.`,\n );\n}\n\nfunction resolveBackendName(value: string | undefined): TestHostBackendName {\n if (value === undefined) {\n return inferBackendName();\n }\n\n if (value === `colima` || value === `docker`) {\n return value;\n }\n\n throw new Error(\n `Unsupported SYSTEMD_TS_TEST_HOST_BACKEND value: ${value}. Expected \"colima\" or \"docker\".`,\n );\n}\n\nfunction defaultDockerContainerName(): string {\n const digest = createHash(`sha256`).update(repoRoot).digest(`hex`).slice(0, 12);\n return `systemd-ts-${digest}`;\n}\n\nfunction logTestHost(message: string): void {\n console.info(`[systemd-ts:test-host] ${message}`);\n}\n\nfunction createBackend(name: TestHostBackendName): TestHostBackend {\n switch (name) {\n case `colima`:\n return new ColimaBackend();\n case `docker`:\n return new DockerBackend();\n }\n}\n\nclass ColimaBackend implements TestHostBackend {\n public readonly info: TestHostInfo = {\n backend: `colima`,\n colimaHome,\n colimaProfile,\n limaHome,\n repoRoot,\n stateRoot: colimaHome,\n };\n\n public async ensureHost(): Promise<void> {\n if (await this.isRunning()) {\n return;\n }\n\n console.info(`Starting repo-local Colima host for integration tests...`);\n await this.execColima([\n `start`,\n `--cpu`,\n `2`,\n `--memory`,\n `4`,\n `--disk`,\n `30`,\n `--runtime`,\n `containerd`,\n ]);\n }\n\n public createGuestShellProcess(): ChildProcessWithoutNullStreams {\n return spawn(`colima`, [`ssh`, `--`, `bash`, `--noprofile`, `--norc`, `-s`], {\n env: this.hostEnv,\n stdio: [`pipe`, `pipe`, `pipe`],\n });\n }\n\n public async runIsolatedCommand(script: string): Promise<string> {\n return this.execColima([`ssh`, `--`, `bash`, `-lc`, script]);\n }\n\n private readonly hostEnv = {\n ...process.env,\n COLIMA_HOME: colimaHome,\n COLIMA_PROFILE: colimaProfile,\n LIMA_HOME: limaHome,\n };\n\n private async isRunning(): Promise<boolean> {\n try {\n const status = await this.execColima([`status`]);\n return status.includes(`is running`);\n } catch {\n return false;\n }\n }\n\n private async execColima(args: readonly string[]): Promise<string> {\n const result = await execFileAsync(`colima`, args, {\n env: this.hostEnv,\n maxBuffer: commandMaxBufferBytes,\n });\n return mergeOutput(result.stdout, result.stderr);\n }\n}\n\nclass DockerBackend implements TestHostBackend {\n public readonly info: TestHostInfo = {\n backend: `docker`,\n repoRoot,\n stateRoot: dockerStateRoot,\n };\n\n private runtimeInfo:\n | {\n readonly home: string;\n readonly runtimeDir: string;\n readonly userId: number;\n }\n | undefined;\n\n public async ensureHost(): Promise<void> {\n logTestHost(`Building Docker test host image ${dockerImage}`);\n await this.execDocker(\n [`build`, `--quiet`, `--tag`, dockerImage, `--file`, dockerfilePath, repoRoot],\n { timeout: dockerBuildTimeoutMs },\n );\n logTestHost(`Docker test host image ready`);\n\n if (!(await this.isContainerRunning())) {\n await this.removeExistingContainer();\n logTestHost(`Starting Docker test host container ${dockerContainer}`);\n await this.execDocker([\n `run`,\n `--detach`,\n `--name`,\n dockerContainer,\n `--hostname`,\n `systemd-ts-test-host`,\n `--privileged`,\n `--cgroupns=host`,\n `--tmpfs`,\n `/run`,\n `--tmpfs`,\n `/run/lock`,\n `--volume`,\n `/sys/fs/cgroup:/sys/fs/cgroup:rw`,\n `--volume`,\n `${repoRoot}:${repoRoot}`,\n `--volume`,\n `${dockerStateRoot}:${dockerStateRoot}`,\n `--workdir`,\n repoRoot,\n dockerImage,\n ]);\n logTestHost(`Docker test host container started`);\n } else {\n logTestHost(`Reusing running Docker test host container ${dockerContainer}`);\n }\n\n await this.ensureUserSession();\n }\n\n public createGuestShellProcess(): ChildProcessWithoutNullStreams {\n const runtimeInfo = this.requireRuntimeInfo();\n return spawn(\n `docker`,\n [\n `exec`,\n `--interactive`,\n `--user`,\n dockerUser,\n `--env`,\n `HOME=${runtimeInfo.home}`,\n `--env`,\n `XDG_RUNTIME_DIR=${runtimeInfo.runtimeDir}`,\n `--env`,\n `DBUS_SESSION_BUS_ADDRESS=unix:path=${runtimeInfo.runtimeDir}/bus`,\n dockerContainer,\n `bash`,\n `--noprofile`,\n `--norc`,\n `-s`,\n ],\n {\n stdio: [`pipe`, `pipe`, `pipe`],\n },\n );\n }\n\n public async runIsolatedCommand(script: string): Promise<string> {\n const runtimeInfo = this.requireRuntimeInfo();\n const result = await this.execDockerAsUser(runtimeInfo, [`bash`, `-lc`, script]);\n\n return mergeOutput(result.stdout, result.stderr);\n }\n\n private async isContainerRunning(): Promise<boolean> {\n try {\n const status = await this.execDocker([\n `inspect`,\n `--format`,\n `{{.State.Running}}`,\n dockerContainer,\n ]);\n return status.trim() === `true`;\n } catch {\n return false;\n }\n }\n\n private async removeExistingContainer(): Promise<void> {\n try {\n await this.execDocker([`rm`, `--force`, dockerContainer]);\n } catch {\n // Ignore missing containers so the first run can proceed.\n }\n }\n\n private async ensureUserSession(): Promise<void> {\n const runtimeInfo = await this.resolveRuntimeInfo();\n logTestHost(\n `Ensuring user session for ${dockerUser} (uid=${runtimeInfo.userId}, home=${runtimeInfo.home})`,\n );\n await this.execDocker(\n [\n `exec`,\n dockerContainer,\n `bash`,\n `-lc`,\n [\n `mkdir -p ${shellQuote(dockerStateRoot)}`,\n `chown ${runtimeInfo.userId}:${runtimeInfo.userId} ${shellQuote(dockerStateRoot)}`,\n `loginctl enable-linger ${shellQuote(dockerUser)}`,\n `systemctl start user@${runtimeInfo.userId}.service`,\n ].join(`\\n`),\n ],\n { timeout: dockerExecTimeoutMs },\n );\n await this.execDockerAsUser(runtimeInfo, [\n `bash`,\n `-lc`,\n `mkdir -p ${shellQuote(`${dockerStateRoot}/tests`)}`,\n ]);\n logTestHost(`User session started for ${dockerUser}`);\n }\n\n private async execDocker(\n args: readonly string[],\n options?: {\n readonly timeout?: number;\n },\n ): Promise<string> {\n const result = await execFileAsync(`docker`, args, {\n maxBuffer: commandMaxBufferBytes,\n timeout: options?.timeout,\n });\n return mergeOutput(result.stdout, result.stderr);\n }\n\n private execDockerAsUser(\n runtimeInfo: {\n readonly home: string;\n readonly runtimeDir: string;\n readonly userId: number;\n },\n command: readonly string[],\n ) {\n return execFileAsync(\n `docker`,\n [\n `exec`,\n `--user`,\n dockerUser,\n `--env`,\n `HOME=${runtimeInfo.home}`,\n `--env`,\n `XDG_RUNTIME_DIR=${runtimeInfo.runtimeDir}`,\n `--env`,\n `DBUS_SESSION_BUS_ADDRESS=unix:path=${runtimeInfo.runtimeDir}/bus`,\n dockerContainer,\n ...command,\n ],\n { maxBuffer: commandMaxBufferBytes },\n );\n }\n\n private requireRuntimeInfo(): {\n readonly home: string;\n readonly runtimeDir: string;\n readonly userId: number;\n } {\n if (this.runtimeInfo === undefined) {\n throw new Error(`Docker test host runtime info is unavailable before ensureHost() completes`);\n }\n\n return this.runtimeInfo;\n }\n\n private async resolveRuntimeInfo(): Promise<{\n readonly home: string;\n readonly runtimeDir: string;\n readonly userId: number;\n }> {\n if (this.runtimeInfo !== undefined) {\n return this.runtimeInfo;\n }\n\n const userId =\n dockerConfiguredUserId === undefined\n ? await this.lookupUserId()\n : Number(dockerConfiguredUserId);\n const home = await this.lookupUserHome();\n\n this.runtimeInfo = {\n home,\n runtimeDir: `/run/user/${userId}`,\n userId,\n };\n\n return this.runtimeInfo;\n }\n\n private async lookupUserHome(): Promise<string> {\n logTestHost(`Resolving home directory for Docker test user ${dockerUser}`);\n const output = await this.execDocker([\n `exec`,\n dockerContainer,\n `bash`,\n `-lc`,\n `getent passwd ${shellQuote(dockerUser)} | cut -d: -f6`,\n ]);\n\n const home = output.trim();\n if (home.length === 0) {\n throw new Error(`Could not resolve home directory for Docker test user ${dockerUser}`);\n }\n\n logTestHost(`Resolved home directory for ${dockerUser}: ${home}`);\n return home;\n }\n\n private async lookupUserId(): Promise<number> {\n logTestHost(`Resolving uid for Docker test user ${dockerUser}`);\n const output = await this.execDocker([\n `exec`,\n dockerContainer,\n `bash`,\n `-lc`,\n `id -u ${shellQuote(dockerUser)}`,\n ]);\n\n const parsed = Number(output.trim());\n if (!Number.isInteger(parsed)) {\n throw new Error(`Could not resolve uid for Docker test user ${dockerUser}`);\n }\n\n logTestHost(`Resolved uid for ${dockerUser}: ${parsed}`);\n return parsed;\n }\n}\n\nclass GuestShell {\n private readonly process: ChildProcessWithoutNullStreams;\n private readonly onExitCleanup: () => void;\n private closed = false;\n private buffer = ``;\n private current:\n | {\n marker: string;\n timeoutId: NodeJS.Timeout | undefined;\n reject: (error: Error) => void;\n resolve: (output: string) => void;\n }\n | undefined;\n private readonly queue: Array<{\n script: string;\n reject: (error: Error) => void;\n resolve: (output: string) => void;\n timeoutMs: number | undefined;\n }> = [];\n\n public constructor(\n createProcess: () => ChildProcessWithoutNullStreams,\n onExitCleanup: () => void,\n ) {\n this.process = createProcess();\n this.onExitCleanup = onExitCleanup;\n this.process.stdout.setEncoding(`utf8`);\n this.process.stderr.setEncoding(`utf8`);\n this.process.stdout.on(`data`, (chunk: string) => this.onData(chunk));\n this.process.stderr.on(`data`, (chunk: string) => this.onData(chunk));\n this.process.on(`exit`, (code, signal) => {\n const reason = new Error(\n `Persistent guest shell exited unexpectedly (code=${code ?? `null`}, signal=${signal ?? `null`})`,\n );\n this.failCurrent(reason);\n while (this.queue.length > 0) {\n this.queue.shift()?.reject(reason);\n }\n this.onExitCleanup();\n });\n\n process.once(`exit`, () => {\n this.process.kill();\n });\n }\n\n public run(script: string, timeoutMs?: number): Promise<string> {\n return new Promise((resolve, reject) => {\n this.queue.push({ script, resolve, reject, timeoutMs });\n this.drain();\n });\n }\n\n public close(): void {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.process.kill();\n }\n\n private drain(): void {\n if (this.current !== undefined) {\n return;\n }\n\n const next = this.queue.shift();\n if (next === undefined) {\n return;\n }\n\n const marker = `__SYSTEMD_TS_END_${randomUUID()}__`;\n const timeoutId =\n next.timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n if (this.current?.marker !== marker) {\n return;\n }\n\n const reason = new Error(`Guest command timed out after ${next.timeoutMs}ms`);\n this.failCurrent(reason);\n this.close();\n }, next.timeoutMs);\n this.current = {\n marker,\n timeoutId,\n reject: next.reject,\n resolve: next.resolve,\n };\n\n this.buffer = ``;\n const command = `(\n${next.script}\n) 2>&1\nstatus=$?\nprintf '\\\\n${marker}:%s\\\\n' \"$status\"\n`;\n\n this.process.stdin.write(command);\n }\n\n private failCurrent(error: Error): void {\n const current = this.current;\n this.current = undefined;\n if (current?.timeoutId !== undefined) {\n clearTimeout(current.timeoutId);\n }\n current?.reject(error);\n }\n\n private onData(chunk: string): void {\n if (this.current === undefined) {\n return;\n }\n\n this.buffer += chunk;\n const markerIndex = this.buffer.indexOf(this.current.marker);\n if (markerIndex === -1) {\n return;\n }\n\n const output = this.buffer.slice(0, markerIndex).replace(/\\n$/, ``);\n const statusMatch = this.buffer\n .slice(markerIndex)\n .match(/^__SYSTEMD_TS_END_[^:]+__:(\\d+)\\r?\\n?/u);\n\n if (statusMatch === null) {\n return;\n }\n\n const status = Number(statusMatch[1]);\n const remainder = this.buffer.slice(markerIndex + statusMatch[0].length);\n const current = this.current;\n this.current = undefined;\n this.buffer = remainder;\n if (current.timeoutId !== undefined) {\n clearTimeout(current.timeoutId);\n }\n\n if (status === 0) {\n current.resolve(output);\n } else {\n current.reject(new Error(output.length > 0 ? output : `Guest command failed`));\n }\n\n this.drain();\n }\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(`'`, `'\\\\''`)}'`;\n}\n\nconst backend = createBackend(selectedBackendName);\n","import { randomUUID } from \"node:crypto\";\n\nimport { getTestHostInfo, runGuestCommand } from \"./host.ts\";\n\nexport interface TestSandbox {\n readonly id: string;\n readonly linkedUnitDir: string;\n readonly namePrefix: string;\n readonly rootDir: string;\n readonly workDir: string;\n}\n\nexport interface DestroyTestSandboxOptions {\n readonly noisy?: boolean;\n}\n\nlet currentSandbox: TestSandbox | undefined;\n\nexport async function createTestSandbox(testName?: string): Promise<TestSandbox> {\n const id = randomUUID().slice(0, 8);\n const slug = slugify(testName ?? `sandbox`);\n const namePrefix = `systemd-ts-${slug}-${id}`;\n const rootDir = `${getTestHostInfo().stateRoot}/tests/${namePrefix}`;\n const linkedUnitDir = `${rootDir}/linked-units`;\n const workDir = `${rootDir}/work`;\n\n await runGuestCommand(\n `set -euo pipefail\nrm -rf ${shellQuote(rootDir)}\nmkdir -p ${shellQuote(linkedUnitDir)} ${shellQuote(workDir)}`,\n );\n\n currentSandbox = {\n id,\n linkedUnitDir,\n namePrefix,\n rootDir,\n workDir,\n };\n\n return currentSandbox;\n}\n\nexport async function destroyCurrentTestSandbox(): Promise<void> {\n if (currentSandbox === undefined) {\n return;\n }\n\n const sandbox = currentSandbox;\n currentSandbox = undefined;\n\n const unitsOutput = await runGuestCommand(\n `systemctl --user list-unit-files --all --no-legend ${shellQuote(`${sandbox.namePrefix}*`)} 2>/dev/null | awk '{print $1}' || true`,\n );\n\n const units = unitsOutput\n .split(`\\n`)\n .map((unit) => unit.trim())\n .filter((unit) => unit.length > 0);\n\n if (units.length > 0) {\n await runGuestCommand(`systemctl --user stop ${units.map(shellQuote).join(` `)} || true`);\n\n await runGuestCommand(`systemctl --user disable ${units.map(shellQuote).join(` `)} || true`);\n\n await runGuestCommand(\n `systemctl --user reset-failed ${units.map(shellQuote).join(` `)} || true`,\n );\n }\n\n await runGuestCommand(`systemctl --user daemon-reload || true`);\n\n await runGuestCommand(`rm -rf ${shellQuote(sandbox.rootDir)}`);\n}\n\nexport function useCurrentTestSandbox(): TestSandbox {\n if (currentSandbox === undefined) {\n throw new Error(`No active test sandbox is available`);\n }\n\n return currentSandbox;\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(`'`, `'\\\\''`)}'`;\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/g, `-`)\n .replaceAll(/^-+|-+$/g, ``)\n .slice(0, 32);\n}\n","import {\n Systemd,\n type CommandExecutionOptions,\n type CommandExecutor,\n type CommandOutput,\n} from \"../main/index.ts\";\n\nimport { runGuestCommand, runIsolatedGuestCommand } from \"./host.ts\";\nimport { useCurrentTestSandbox } from \"./sandbox.ts\";\n\n/**\n * Creates a command executor that runs argv-style commands inside the current\n * test host guest shell.\n */\nexport function createGuestCommandExecutor(\n runCommand: (script: string) => Promise<string>,\n): CommandExecutor {\n return async (\n command: string,\n args: readonly string[],\n options: CommandExecutionOptions = {},\n ): Promise<CommandOutput> => {\n try {\n const stdout = await runCommand(buildGuestCommand(command, args, options));\n return {\n stderr: ``,\n stdout,\n };\n } catch (cause) {\n throw enrichGuestCommandError(cause);\n }\n };\n}\n\n/**\n * Executor that reuses the warm guest shell for fast sandbox-backed tests.\n */\nexport const guestCommandExecutor: CommandExecutor = createGuestCommandExecutor(runGuestCommand);\n\n/**\n * Executor that runs each guest command in a fresh isolated guest shell.\n */\nexport const isolatedGuestCommandExecutor: CommandExecutor =\n createGuestCommandExecutor(runIsolatedGuestCommand);\n\n/**\n * Creates a `Systemd` instance scoped to the active test sandbox and backed by\n * the warm guest-shell executor.\n */\nexport function sandboxSystemd(): Systemd {\n const sandbox = useCurrentTestSandbox();\n return new Systemd({\n executor: guestCommandExecutor,\n linkUnits: true,\n scope: `user`,\n unitDir: sandbox.linkedUnitDir,\n });\n}\n\n/**\n * Creates a `Systemd` instance scoped to the active test sandbox and backed by\n * a fresh guest shell per command.\n */\nexport function isolatedSandboxSystemd(): Systemd {\n const sandbox = useCurrentTestSandbox();\n return new Systemd({\n executor: isolatedGuestCommandExecutor,\n linkUnits: true,\n scope: `user`,\n unitDir: sandbox.linkedUnitDir,\n });\n}\n\nfunction buildGuestCommand(\n command: string,\n args: readonly string[],\n options: CommandExecutionOptions,\n): string {\n const envArgs =\n options.env === undefined\n ? []\n : Object.entries(options.env).flatMap(([key, value]) =>\n value === undefined ? [`-u`, key] : [`${key}=${value}`],\n );\n\n return [`env`, ...envArgs, command, ...args].map((part) => shellQuote(part)).join(` `);\n}\n\nfunction enrichGuestCommandError(cause: unknown): Error {\n if (cause instanceof Error) {\n return Object.assign(cause, {\n code: 1,\n stderr: ``,\n stdout: cause.message,\n });\n }\n\n return Object.assign(new Error(`Guest command failed`), {\n code: 1,\n stderr: ``,\n stdout: ``,\n });\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(`'`, `'\\\\''`)}'`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"test.mjs","names":["delay","shellQuote","shellQuote"],"sources":["../src/test/host.ts","../src/test/sandbox.ts","../src/test/harness.ts"],"sourcesContent":["import { execFile, spawn, type ChildProcessWithoutNullStreams } from \"node:child_process\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { fileURLToPath } from \"node:url\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\nconst commandMaxBufferBytes = 16 * 1024 * 1024;\nconst dockerBuildTimeoutMs = 5 * 60 * 1_000;\nconst dockerExecTimeoutMs = 60 * 1_000;\nconst guestCommandTimeoutMs = 10 * 1_000;\nconst repoRoot = fileURLToPath(new URL(`../../../../`, import.meta.url));\nconst dockerfilePath = fileURLToPath(new URL(`./systemd-container.Dockerfile`, import.meta.url));\nconst selectedBackendName = resolveBackendName(process.env[`SYSTEMD_TS_TEST_HOST_BACKEND`]);\nconst colimaHome = process.env[`COLIMA_HOME`] ?? `${repoRoot}.colima`;\nconst limaHome = process.env[`LIMA_HOME`] ?? `${colimaHome}/_lima`;\nconst colimaProfile = process.env[`COLIMA_PROFILE`] ?? `systemd-ts`;\nconst dockerStateRoot = process.env[`SYSTEMD_TS_TEST_HOST_ROOT`] ?? `${repoRoot}.docker`;\nconst dockerImage = process.env[`SYSTEMD_TS_TEST_DOCKER_IMAGE`] ?? `systemd-ts-test-host:local`;\nconst dockerUser = process.env[`SYSTEMD_TS_TEST_DOCKER_USER`] ?? `runner`;\nconst dockerConfiguredUserId = process.env[`SYSTEMD_TS_TEST_DOCKER_UID`];\nconst dockerContainer =\n process.env[`SYSTEMD_TS_TEST_DOCKER_CONTAINER`] ?? defaultDockerContainerName();\n\nexport interface TestHostInfo {\n readonly backend: TestHostBackendName;\n readonly colimaHome?: string;\n readonly colimaProfile?: string;\n readonly limaHome?: string;\n readonly repoRoot: string;\n readonly stateRoot: string;\n}\n\ntype TestHostBackendName = `colima` | `docker`;\n\ninterface TestHostBackend {\n readonly info: TestHostInfo;\n ensureHost(): Promise<void>;\n createGuestShellProcess(): ChildProcessWithoutNullStreams;\n runIsolatedCommand(script: string): Promise<string>;\n}\n\nlet ensuredHost: Promise<TestHostInfo> | undefined;\nlet guestShell: GuestShell | undefined;\n\nexport function getTestHostInfo(): TestHostInfo {\n return backend.info;\n}\n\nexport function ensureTestHost(): Promise<TestHostInfo> {\n ensuredHost ??= ensureTestHostInner();\n return ensuredHost;\n}\n\nexport function closeTestHost(): void {\n closeGuestShell(`warmup complete`);\n}\n\nexport async function runGuestCommand(script: string): Promise<string> {\n const shell = getGuestShell();\n return shell.run(script);\n}\n\nexport async function runIsolatedGuestCommand(script: string): Promise<string> {\n return backend.runIsolatedCommand(script);\n}\n\nasync function ensureTestHostInner(): Promise<TestHostInfo> {\n logTestHost(`Ensuring ${backend.info.backend} test host`);\n await backend.ensureHost();\n logTestHost(`Waiting for systemd --user readiness`);\n await waitForUserSystemd();\n logTestHost(`Test host ready`);\n return getTestHostInfo();\n}\n\nasync function waitForUserSystemd(): Promise<void> {\n for (let attempt = 0; attempt < 30; attempt += 1) {\n const state = await runGuestCommandWithTimeout(\n `printf 'pid1=%s\\n' \"$(ps -p 1 -o comm=)\"; systemctl --user is-system-running || true; printf 'xdg=%s\\n' \"$XDG_RUNTIME_DIR\"`,\n guestCommandTimeoutMs,\n );\n\n if (\n state.includes(`pid1=systemd`) &&\n state.includes(`xdg=/run/user/`) &&\n [`running`, `degraded`].some((status) => state.includes(status))\n ) {\n logTestHost(\n `systemd --user ready after ${attempt + 1} attempt${attempt === 0 ? `` : `s`}: ${summarizeSystemdState(state)}`,\n );\n return;\n }\n\n if (attempt === 0 || (attempt + 1) % 5 === 0) {\n logTestHost(\n `systemd --user not ready yet (${attempt + 1}/30): ${summarizeSystemdState(state)}`,\n );\n }\n\n await delay(1_000);\n }\n\n throw new Error(`Timed out waiting for systemd --user in the ${backend.info.backend} guest`);\n}\n\nfunction getGuestShell(): GuestShell {\n guestShell ??= new GuestShell(\n () => backend.createGuestShellProcess(),\n () => {\n if (guestShell !== undefined) {\n guestShell = undefined;\n }\n },\n );\n return guestShell;\n}\n\nfunction closeGuestShell(reason: string): void {\n if (guestShell === undefined) {\n return;\n }\n\n logTestHost(`Resetting guest shell: ${reason}`);\n guestShell.close();\n guestShell = undefined;\n}\n\nasync function runGuestCommandWithTimeout(script: string, timeoutMs: number): Promise<string> {\n const shell = getGuestShell();\n try {\n return await shell.run(script, timeoutMs);\n } catch (error) {\n if (error instanceof Error && error.message.includes(`timed out`)) {\n closeGuestShell(error.message);\n }\n\n throw error;\n }\n}\n\nfunction mergeOutput(stdout: string, stderr: string): string {\n return [stdout, stderr].filter((output) => output.length > 0).join(`\\n`);\n}\n\nfunction summarizeSystemdState(state: string): string {\n return state\n .split(`\\n`)\n .map((line) => line.trim())\n .filter((line) => line.length > 0)\n .join(`; `);\n}\n\nfunction inferBackendName(): TestHostBackendName {\n if (process.platform === `darwin`) {\n return `colima`;\n }\n\n if (process.platform === `linux`) {\n return `docker`;\n }\n\n throw new Error(\n `Unsupported test host platform: ${process.platform}. Set SYSTEMD_TS_TEST_HOST_BACKEND explicitly if you have a compatible backend.`,\n );\n}\n\nfunction resolveBackendName(value: string | undefined): TestHostBackendName {\n if (value === undefined) {\n return inferBackendName();\n }\n\n if (value === `colima` || value === `docker`) {\n return value;\n }\n\n throw new Error(\n `Unsupported SYSTEMD_TS_TEST_HOST_BACKEND value: ${value}. Expected \"colima\" or \"docker\".`,\n );\n}\n\nfunction defaultDockerContainerName(): string {\n const digest = createHash(`sha256`).update(repoRoot).digest(`hex`).slice(0, 12);\n return `systemd-ts-${digest}`;\n}\n\nfunction logTestHost(message: string): void {\n console.info(`[systemd-ts:test-host] ${message}`);\n}\n\nfunction createBackend(name: TestHostBackendName): TestHostBackend {\n switch (name) {\n case `colima`:\n return new ColimaBackend();\n case `docker`:\n return new DockerBackend();\n }\n}\n\nclass ColimaBackend implements TestHostBackend {\n public readonly info: TestHostInfo = {\n backend: `colima`,\n colimaHome,\n colimaProfile,\n limaHome,\n repoRoot,\n stateRoot: colimaHome,\n };\n\n public async ensureHost(): Promise<void> {\n if (await this.isRunning()) {\n return;\n }\n\n console.info(`Starting repo-local Colima host for integration tests...`);\n await this.execColima([\n `start`,\n `--cpu`,\n `2`,\n `--memory`,\n `4`,\n `--disk`,\n `30`,\n `--runtime`,\n `containerd`,\n ]);\n }\n\n public createGuestShellProcess(): ChildProcessWithoutNullStreams {\n return spawn(`colima`, [`ssh`, `--`, `bash`, `--noprofile`, `--norc`, `-s`], {\n env: this.hostEnv,\n stdio: [`pipe`, `pipe`, `pipe`],\n });\n }\n\n public async runIsolatedCommand(script: string): Promise<string> {\n return this.execColima([`ssh`, `--`, `bash`, `-lc`, script]);\n }\n\n private readonly hostEnv = {\n ...process.env,\n COLIMA_HOME: colimaHome,\n COLIMA_PROFILE: colimaProfile,\n LIMA_HOME: limaHome,\n };\n\n private async isRunning(): Promise<boolean> {\n try {\n const status = await this.execColima([`status`]);\n return status.includes(`is running`);\n } catch {\n return false;\n }\n }\n\n private async execColima(args: readonly string[]): Promise<string> {\n const result = await execFileAsync(`colima`, args, {\n env: this.hostEnv,\n maxBuffer: commandMaxBufferBytes,\n });\n return mergeOutput(result.stdout, result.stderr);\n }\n}\n\nclass DockerBackend implements TestHostBackend {\n public readonly info: TestHostInfo = {\n backend: `docker`,\n repoRoot,\n stateRoot: dockerStateRoot,\n };\n\n private runtimeInfo:\n | {\n readonly home: string;\n readonly runtimeDir: string;\n readonly userId: number;\n }\n | undefined;\n\n public async ensureHost(): Promise<void> {\n logTestHost(`Building Docker test host image ${dockerImage}`);\n await this.execDocker(\n [`build`, `--quiet`, `--tag`, dockerImage, `--file`, dockerfilePath, repoRoot],\n { timeout: dockerBuildTimeoutMs },\n );\n logTestHost(`Docker test host image ready`);\n\n if (!(await this.isContainerRunning())) {\n await this.removeExistingContainer();\n logTestHost(`Starting Docker test host container ${dockerContainer}`);\n await this.execDocker([\n `run`,\n `--detach`,\n `--name`,\n dockerContainer,\n `--hostname`,\n `systemd-ts-test-host`,\n `--privileged`,\n `--cgroupns=host`,\n `--tmpfs`,\n `/run`,\n `--tmpfs`,\n `/run/lock`,\n `--volume`,\n `/sys/fs/cgroup:/sys/fs/cgroup:rw`,\n `--volume`,\n `${repoRoot}:${repoRoot}`,\n `--volume`,\n `${dockerStateRoot}:${dockerStateRoot}`,\n `--workdir`,\n repoRoot,\n dockerImage,\n ]);\n logTestHost(`Docker test host container started`);\n } else {\n logTestHost(`Reusing running Docker test host container ${dockerContainer}`);\n }\n\n await this.ensureUserSession();\n }\n\n public createGuestShellProcess(): ChildProcessWithoutNullStreams {\n const runtimeInfo = this.requireRuntimeInfo();\n return spawn(\n `docker`,\n [\n `exec`,\n `--interactive`,\n `--user`,\n dockerUser,\n `--env`,\n `HOME=${runtimeInfo.home}`,\n `--env`,\n `XDG_RUNTIME_DIR=${runtimeInfo.runtimeDir}`,\n `--env`,\n `DBUS_SESSION_BUS_ADDRESS=unix:path=${runtimeInfo.runtimeDir}/bus`,\n dockerContainer,\n `bash`,\n `--noprofile`,\n `--norc`,\n `-s`,\n ],\n {\n stdio: [`pipe`, `pipe`, `pipe`],\n },\n );\n }\n\n public async runIsolatedCommand(script: string): Promise<string> {\n const runtimeInfo = this.requireRuntimeInfo();\n const result = await this.execDockerAsUser(runtimeInfo, [`bash`, `-lc`, script]);\n\n return mergeOutput(result.stdout, result.stderr);\n }\n\n private async isContainerRunning(): Promise<boolean> {\n try {\n const status = await this.execDocker([\n `inspect`,\n `--format`,\n `{{.State.Running}}`,\n dockerContainer,\n ]);\n return status.trim() === `true`;\n } catch {\n return false;\n }\n }\n\n private async removeExistingContainer(): Promise<void> {\n try {\n await this.execDocker([`rm`, `--force`, dockerContainer]);\n } catch {\n // Ignore missing containers so the first run can proceed.\n }\n }\n\n private async ensureUserSession(): Promise<void> {\n const runtimeInfo = await this.resolveRuntimeInfo();\n logTestHost(\n `Ensuring user session for ${dockerUser} (uid=${runtimeInfo.userId}, home=${runtimeInfo.home})`,\n );\n await this.execDocker(\n [\n `exec`,\n dockerContainer,\n `bash`,\n `-lc`,\n [\n `mkdir -p ${shellQuote(dockerStateRoot)}`,\n `chown ${runtimeInfo.userId}:${runtimeInfo.userId} ${shellQuote(dockerStateRoot)}`,\n `loginctl enable-linger ${shellQuote(dockerUser)}`,\n `systemctl start user@${runtimeInfo.userId}.service`,\n ].join(`\\n`),\n ],\n { timeout: dockerExecTimeoutMs },\n );\n await this.execDockerAsUser(runtimeInfo, [\n `bash`,\n `-lc`,\n `mkdir -p ${shellQuote(`${dockerStateRoot}/tests`)}`,\n ]);\n logTestHost(`User session started for ${dockerUser}`);\n }\n\n private async execDocker(\n args: readonly string[],\n options?: {\n readonly timeout?: number;\n },\n ): Promise<string> {\n const result = await execFileAsync(`docker`, args, {\n maxBuffer: commandMaxBufferBytes,\n timeout: options?.timeout,\n });\n return mergeOutput(result.stdout, result.stderr);\n }\n\n private execDockerAsUser(\n runtimeInfo: {\n readonly home: string;\n readonly runtimeDir: string;\n readonly userId: number;\n },\n command: readonly string[],\n ) {\n return execFileAsync(\n `docker`,\n [\n `exec`,\n `--user`,\n dockerUser,\n `--env`,\n `HOME=${runtimeInfo.home}`,\n `--env`,\n `XDG_RUNTIME_DIR=${runtimeInfo.runtimeDir}`,\n `--env`,\n `DBUS_SESSION_BUS_ADDRESS=unix:path=${runtimeInfo.runtimeDir}/bus`,\n dockerContainer,\n ...command,\n ],\n { maxBuffer: commandMaxBufferBytes },\n );\n }\n\n private requireRuntimeInfo(): {\n readonly home: string;\n readonly runtimeDir: string;\n readonly userId: number;\n } {\n if (this.runtimeInfo === undefined) {\n throw new Error(`Docker test host runtime info is unavailable before ensureHost() completes`);\n }\n\n return this.runtimeInfo;\n }\n\n private async resolveRuntimeInfo(): Promise<{\n readonly home: string;\n readonly runtimeDir: string;\n readonly userId: number;\n }> {\n if (this.runtimeInfo !== undefined) {\n return this.runtimeInfo;\n }\n\n const userId =\n dockerConfiguredUserId === undefined\n ? await this.lookupUserId()\n : Number(dockerConfiguredUserId);\n const home = await this.lookupUserHome();\n\n this.runtimeInfo = {\n home,\n runtimeDir: `/run/user/${userId}`,\n userId,\n };\n\n return this.runtimeInfo;\n }\n\n private async lookupUserHome(): Promise<string> {\n logTestHost(`Resolving home directory for Docker test user ${dockerUser}`);\n const output = await this.execDocker([\n `exec`,\n dockerContainer,\n `bash`,\n `-lc`,\n `getent passwd ${shellQuote(dockerUser)} | cut -d: -f6`,\n ]);\n\n const home = output.trim();\n if (home.length === 0) {\n throw new Error(`Could not resolve home directory for Docker test user ${dockerUser}`);\n }\n\n logTestHost(`Resolved home directory for ${dockerUser}: ${home}`);\n return home;\n }\n\n private async lookupUserId(): Promise<number> {\n logTestHost(`Resolving uid for Docker test user ${dockerUser}`);\n const output = await this.execDocker([\n `exec`,\n dockerContainer,\n `bash`,\n `-lc`,\n `id -u ${shellQuote(dockerUser)}`,\n ]);\n\n const parsed = Number(output.trim());\n if (!Number.isInteger(parsed)) {\n throw new Error(`Could not resolve uid for Docker test user ${dockerUser}`);\n }\n\n logTestHost(`Resolved uid for ${dockerUser}: ${parsed}`);\n return parsed;\n }\n}\n\nclass GuestShell {\n private readonly process: ChildProcessWithoutNullStreams;\n private readonly onExitCleanup: () => void;\n private closed = false;\n private buffer = ``;\n private current:\n | {\n marker: string;\n timeoutId: NodeJS.Timeout | undefined;\n reject: (error: Error) => void;\n resolve: (output: string) => void;\n }\n | undefined;\n private readonly queue: Array<{\n script: string;\n reject: (error: Error) => void;\n resolve: (output: string) => void;\n timeoutMs: number | undefined;\n }> = [];\n\n public constructor(\n createProcess: () => ChildProcessWithoutNullStreams,\n onExitCleanup: () => void,\n ) {\n this.process = createProcess();\n this.onExitCleanup = onExitCleanup;\n this.process.stdout.setEncoding(`utf8`);\n this.process.stderr.setEncoding(`utf8`);\n this.process.stdout.on(`data`, (chunk: string) => this.onData(chunk));\n this.process.stderr.on(`data`, (chunk: string) => this.onData(chunk));\n this.process.on(`exit`, (code, signal) => {\n const reason = new Error(\n `Persistent guest shell exited unexpectedly (code=${code ?? `null`}, signal=${signal ?? `null`})`,\n );\n this.failCurrent(reason);\n while (this.queue.length > 0) {\n this.queue.shift()?.reject(reason);\n }\n this.onExitCleanup();\n });\n\n process.once(`exit`, () => {\n this.process.kill();\n });\n }\n\n public run(script: string, timeoutMs?: number): Promise<string> {\n return new Promise((resolve, reject) => {\n this.queue.push({ script, resolve, reject, timeoutMs });\n this.drain();\n });\n }\n\n public close(): void {\n if (this.closed) {\n return;\n }\n\n this.closed = true;\n this.process.kill();\n }\n\n private drain(): void {\n if (this.current !== undefined) {\n return;\n }\n\n const next = this.queue.shift();\n if (next === undefined) {\n return;\n }\n\n const marker = `__SYSTEMD_TS_END_${randomUUID()}__`;\n const timeoutId =\n next.timeoutMs === undefined\n ? undefined\n : setTimeout(() => {\n if (this.current?.marker !== marker) {\n return;\n }\n\n const reason = new Error(`Guest command timed out after ${next.timeoutMs}ms`);\n this.failCurrent(reason);\n this.close();\n }, next.timeoutMs);\n this.current = {\n marker,\n timeoutId,\n reject: next.reject,\n resolve: next.resolve,\n };\n\n this.buffer = ``;\n const command = `(\n${next.script}\n) 2>&1\nstatus=$?\nprintf '\\\\n${marker}:%s\\\\n' \"$status\"\n`;\n\n this.process.stdin.write(command);\n }\n\n private failCurrent(error: Error): void {\n const current = this.current;\n this.current = undefined;\n if (current?.timeoutId !== undefined) {\n clearTimeout(current.timeoutId);\n }\n current?.reject(error);\n }\n\n private onData(chunk: string): void {\n if (this.current === undefined) {\n return;\n }\n\n this.buffer += chunk;\n const markerIndex = this.buffer.indexOf(this.current.marker);\n if (markerIndex === -1) {\n return;\n }\n\n const output = this.buffer.slice(0, markerIndex).replace(/\\n$/, ``);\n const statusMatch = this.buffer\n .slice(markerIndex)\n .match(/^__SYSTEMD_TS_END_[^:]+__:(\\d+)\\r?\\n?/u);\n\n if (statusMatch === null) {\n return;\n }\n\n const status = Number(statusMatch[1]);\n const remainder = this.buffer.slice(markerIndex + statusMatch[0].length);\n const current = this.current;\n this.current = undefined;\n this.buffer = remainder;\n if (current.timeoutId !== undefined) {\n clearTimeout(current.timeoutId);\n }\n\n if (status === 0) {\n current.resolve(output);\n } else {\n current.reject(new Error(output.length > 0 ? output : `Guest command failed`));\n }\n\n this.drain();\n }\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(`'`, `'\\\\''`)}'`;\n}\n\nconst backend = createBackend(selectedBackendName);\n","import { randomUUID } from \"node:crypto\";\n\nimport { getTestHostInfo, runGuestCommand } from \"./host.ts\";\n\nexport interface TestSandbox {\n readonly id: string;\n readonly linkedUnitDir: string;\n readonly namePrefix: string;\n readonly rootDir: string;\n readonly workDir: string;\n}\n\nexport interface DestroyTestSandboxOptions {\n readonly noisy?: boolean;\n}\n\nlet currentSandbox: TestSandbox | undefined;\n\nexport async function createTestSandbox(testName?: string): Promise<TestSandbox> {\n const id = randomUUID().slice(0, 8);\n const slug = slugify(testName ?? `sandbox`);\n const namePrefix = `systemd-ts-${slug}-${id}`;\n const rootDir = `${getTestHostInfo().stateRoot}/tests/${namePrefix}`;\n const linkedUnitDir = `${rootDir}/linked-units`;\n const workDir = `${rootDir}/work`;\n\n await runGuestCommand(\n `set -euo pipefail\nrm -rf ${shellQuote(rootDir)}\nmkdir -p ${shellQuote(linkedUnitDir)} ${shellQuote(workDir)}`,\n );\n\n currentSandbox = {\n id,\n linkedUnitDir,\n namePrefix,\n rootDir,\n workDir,\n };\n\n return currentSandbox;\n}\n\nexport async function destroyCurrentTestSandbox(): Promise<void> {\n if (currentSandbox === undefined) {\n return;\n }\n\n const sandbox = currentSandbox;\n currentSandbox = undefined;\n\n const unitsOutput = await runGuestCommand(\n `systemctl --user list-unit-files --all --no-legend ${shellQuote(`${sandbox.namePrefix}*`)} 2>/dev/null | awk '{print $1}' || true`,\n );\n\n const units = unitsOutput\n .split(`\\n`)\n .map((unit) => unit.trim())\n .filter((unit) => unit.length > 0);\n\n if (units.length > 0) {\n await runGuestCommand(`systemctl --user stop ${units.map(shellQuote).join(` `)} || true`);\n\n await runGuestCommand(`systemctl --user disable ${units.map(shellQuote).join(` `)} || true`);\n\n await runGuestCommand(\n `systemctl --user reset-failed ${units.map(shellQuote).join(` `)} || true`,\n );\n }\n\n await runGuestCommand(`systemctl --user daemon-reload || true`);\n\n await runGuestCommand(`rm -rf ${shellQuote(sandbox.rootDir)}`);\n}\n\nexport function useCurrentTestSandbox(): TestSandbox {\n if (currentSandbox === undefined) {\n throw new Error(`No active test sandbox is available`);\n }\n\n return currentSandbox;\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(`'`, `'\\\\''`)}'`;\n}\n\nfunction slugify(value: string): string {\n return value\n .toLowerCase()\n .replaceAll(/[^a-z0-9]+/g, `-`)\n .replaceAll(/^-+|-+$/g, ``)\n .slice(0, 32);\n}\n","import {\n Systemd,\n type CommandExecutionOptions,\n type CommandExecutor,\n type CommandOutput,\n} from \"../main/index.ts\";\n\nimport { runGuestCommand, runIsolatedGuestCommand } from \"./host.ts\";\nimport { useCurrentTestSandbox } from \"./sandbox.ts\";\n\n/**\n * Creates a command executor that runs argv-style commands inside the current\n * test host guest shell.\n */\nexport function createGuestCommandExecutor(\n runCommand: (script: string) => Promise<string>,\n): CommandExecutor {\n return async (\n command: string,\n args: readonly string[],\n options: CommandExecutionOptions = {},\n ): Promise<CommandOutput> => {\n try {\n const stdout = await runCommand(buildGuestCommand(command, args, options));\n return {\n stderr: ``,\n stdout,\n };\n } catch (cause) {\n throw enrichGuestCommandError(cause);\n }\n };\n}\n\n/**\n * Executor that reuses the warm guest shell for fast sandbox-backed tests.\n */\nexport const guestCommandExecutor: CommandExecutor = createGuestCommandExecutor(runGuestCommand);\n\n/**\n * Executor that runs each guest command in a fresh isolated guest shell.\n */\nexport const isolatedGuestCommandExecutor: CommandExecutor =\n createGuestCommandExecutor(runIsolatedGuestCommand);\n\n/**\n * Creates a `Systemd` instance scoped to the active test sandbox and backed by\n * the warm guest-shell executor.\n */\nexport function sandboxSystemd(): Systemd {\n const sandbox = useCurrentTestSandbox();\n return new Systemd({\n executor: guestCommandExecutor,\n linkUnits: true,\n scope: `user`,\n unitDir: sandbox.linkedUnitDir,\n });\n}\n\n/**\n * Creates a `Systemd` instance scoped to the active test sandbox and backed by\n * a fresh guest shell per command.\n */\nexport function isolatedSandboxSystemd(): Systemd {\n const sandbox = useCurrentTestSandbox();\n return new Systemd({\n executor: isolatedGuestCommandExecutor,\n linkUnits: true,\n scope: `user`,\n unitDir: sandbox.linkedUnitDir,\n });\n}\n\nfunction buildGuestCommand(\n command: string,\n args: readonly string[],\n options: CommandExecutionOptions,\n): string {\n const envArgs =\n options.env === undefined\n ? []\n : Object.entries(options.env).flatMap(([key, value]) =>\n value === undefined ? [`-u`, key] : [`${key}=${value}`],\n );\n\n return [`env`, ...envArgs, command, ...args].map((part) => shellQuote(part)).join(` `);\n}\n\nfunction enrichGuestCommandError(cause: unknown): Error {\n if (cause instanceof Error) {\n return Object.assign(cause, {\n code: 1,\n stderr: ``,\n stdout: cause.message,\n });\n }\n\n return Object.assign(new Error(`Guest command failed`), {\n code: 1,\n stderr: ``,\n stdout: ``,\n });\n}\n\nfunction shellQuote(value: string): string {\n return `'${value.replaceAll(`'`, `'\\\\''`)}'`;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "systemd-ts",
3
- "version": "261.2.1",
3
+ "version": "262.0.0",
4
4
  "description": "TypeScript builders and helpers for systemd services and timers.",
5
5
  "license": "LGPL-2.1-or-later",
6
6
  "repository": {
@@ -22,12 +22,12 @@
22
22
  "access": "public"
23
23
  },
24
24
  "devDependencies": {
25
- "@types/node": "25.9.5",
25
+ "@types/node": "26.6.2",
26
26
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
27
- "takua": "0.2.2",
27
+ "takua": "0.2.3",
28
28
  "typescript": "7.0.2",
29
- "vite-plus": "0.2.7",
30
- "vitest": "4.1.10"
29
+ "vite-plus": "0.3.3",
30
+ "vitest": "5.0.1"
31
31
  },
32
32
  "scripts": {
33
33
  "build": "vp pack",
@@ -21,6 +21,10 @@ export const notify = {
21
21
  * Use this when a `Type=notify` service has completed its startup work and is
22
22
  * ready to be considered fully started. If `options.status` is provided, it is
23
23
  * sent as an additional `STATUS=...` field.
24
+ *
25
+ * With `Type=notify-reload`, the main process must catch or block its
26
+ * `ReloadSignal=` (SIGHUP by default) before sending readiness. Since systemd
27
+ * v262, startup fails if it does neither. Source: systemd v262, `systemd.service(5)`.
24
28
  */
25
29
  async ready(options: NotifyOptions = {}): Promise<Result<void, NotifySendError>> {
26
30
  try {
package/src/main/types.ts CHANGED
@@ -138,10 +138,11 @@ export interface CustomDirectiveSection {
138
138
  /**
139
139
  * Generic unit-level directives shared by service and timer units.
140
140
  *
141
- * References in this file are grounded in cached upstream systemd v260.1 manpages.
141
+ * References in this file are grounded in cached upstream systemd manpages,
142
+ * reviewed through v262. Individual directives identify their documentation source.
142
143
  *
143
144
  * Sources:
144
- * - systemd v260.1, `systemd.unit(5)`
145
+ * - systemd v262, `systemd.unit(5)`
145
146
  */
146
147
  export interface SystemdUnitSection extends CustomDirectiveSection {
147
148
  /** Units that should be ordered before this unit starts. Source: systemd v260.1, `systemd.unit(5)`. */
@@ -152,7 +153,7 @@ export interface SystemdUnitSection extends CustomDirectiveSection {
152
153
  readonly AssertACPower?: boolean | readonly boolean[];
153
154
  /** Assertion form of `ConditionArchitecture=`. Check whether the system is running on a specific architecture. Takes one of x86, x86-64, ppc, ppc-le, ppc64, ppc64-le, ia64, parisc, parisc64, s390, s390x, sparc, sparc64, mips, mips-le, mips64, mips64-le, alpha, arm, arm-be, arm64, arm64-be, sh, sh64, m68k, tilegx, cris, arc, arc-be, or native. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v260.1, `systemd.unit(5)`. */
154
155
  readonly AssertArchitecture?: string | readonly string[];
155
- /** Assertion form of `ConditionCPUFeature=`. Verify that a given CPU feature is available via the CPUID instruction. This condition only does something on i386 and x86-64 processors. On other processors it is assumed that the CPU does not support the given feature. It checks the leaves 1, 7, 0x80000001, and 0x80000007. Valid values are: fpu, vme, de, pse, tsc, msr, pae, mce, cx8, apic, sep, mtrr, pge, mca, cmov, pat, pse36, clflush, mmx, fxsr, sse, sse2, ht, pni, pclmul, monitor, ssse3, fma3, cx16, sse4_1, sse4_2, movbe, popcnt, aes, xsave, osxsave, avx, f16c, rdrand, bmi1, avx2, bmi2, rdseed, adx, sha_ni, syscall, rdtscp, lm, lahf_lm, abm, constant_tsc. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v260.1, `systemd.unit(5)`. */
156
+ /** Assertion form of `ConditionCPUFeature=`. Checks CPU features via CPUID on i386 and x86-64, or ELF hardware capabilities on arm64. Accepts architecture-qualified names such as `arm64.bti`; a mismatched architecture prefix evaluates to false. On other architectures, features are assumed unsupported. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v262, `systemd.unit(5)`. */
156
157
  readonly AssertCPUFeature?: string | readonly string[];
157
158
  /** Assertion form of `ConditionCPUPressure=`. Verify that the overall system (memory, CPU or IO) pressure is below or equal to a threshold. This setting takes a threshold value as argument. It can be specified as a simple percentage value, suffixed with %, in which case the pressure will be measured as an average over the last five minutes before the attempt to start the unit is performed. Alternatively, the average timespan can also be specified using / as a separator, for example: 10%/1min. The supported timespans match what the kernel provides, and are limited to 10sec, 1min and 5min. The full PSI will be checked first, and if not found some will be checked. For more details, see the documentation on PSI (Pressure Stall Information) . Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v260.1, `systemd.unit(5)`. */
158
159
  readonly AssertCPUPressure?: string | readonly string[];
@@ -188,7 +189,7 @@ export interface SystemdUnitSection extends CustomDirectiveSection {
188
189
  readonly AssertKernelModuleLoaded?: string | readonly string[];
189
190
  /** Assertion form of `ConditionKernelVersion=`. May be used to check whether the kernel version (as reported by uname -r) matches a certain expression, or if prefixed with the exclamation mark, does not match. The argument must be a list of (potentially quoted) expressions. Each expression starts with one of = or != for string comparisons, <, <=, ==, <>, >=, > for version comparisons, or $=, !$= for a shell-style glob match. If no operator is specified, $= is implied. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v260.1, `systemd.unit(5)`. */
190
191
  readonly AssertKernelVersion?: string | readonly string[];
191
- /** Assertion form of `ConditionMachineTag=`. Matches machine tags configured in `machine-info(5)`. Source: systemd v261, `systemd.unit(5)`. */
192
+ /** Assertion form of `ConditionMachineTag=`. Matches machine tags using shell-style patterns, optionally negated with `!`. Parameterized tags include their value in the match: `role=web` matches exactly, `role=*` matches any value for that key, and `role` does not match `role=web`. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v262, `systemd.unit(5)`. */
192
193
  readonly AssertMachineTag?: string | readonly string[];
193
194
  /** Assertion form of `ConditionMemory=`. Verify that the specified amount of system memory is available to the current system. Takes a memory size in bytes as argument, optionally prefixed with a comparison operator <, <=, = (or ==), != (or <>), >=, >. On bare-metal systems compares the amount of physical memory in the system with the specified size, adhering to the specified comparison operator. In containers compares the amount of memory assigned to the container instead. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v260.1, `systemd.unit(5)`. */
194
195
  readonly AssertMemory?: number | string | readonly (number | string)[];
@@ -198,7 +199,7 @@ export interface SystemdUnitSection extends CustomDirectiveSection {
198
199
  readonly AssertNeedsUpdate?: string | readonly string[];
199
200
  /** Assertion form of `ConditionOSRelease=`. Verify that a specific key=value pair is set in the host's os-release5. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v260.1, `systemd.unit(5)`. */
200
201
  readonly AssertOSRelease?: string | readonly string[];
201
- /** Assertion form of `ConditionPathExists=`. Check for the existence of a file. If the specified absolute path name does not exist, the condition will fail. If the absolute path name passed to ConditionPathExists= is prefixed with an exclamation mark (!), the test is negated, and the unit is only started if the path does not exist. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v260.1, `systemd.unit(5)`. */
202
+ /** Assertion form of `ConditionPathExists=`. Checks for any file system object at an absolute path, including directories, device nodes, FIFOs, and sockets. Symbolic links are followed, so broken symbolic links do not count as existing. Prefix the path with `!` to require its absence. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v262, `systemd.unit(5)`. */
202
203
  readonly AssertPathExists?: string | readonly string[];
203
204
  /** Assertion form of `ConditionPathExistsGlob=`. Similar to `ConditionPathExists=`, but checks for the existence of at least one file or directory matching the specified globbing pattern. Unlike conditions, a mismatch fails the start job instead of skipping it. Source: systemd v260.1, `systemd.unit(5)`. */
204
205
  readonly AssertPathExistsGlob?: string | readonly string[];
@@ -232,7 +233,7 @@ export interface SystemdUnitSection extends CustomDirectiveSection {
232
233
  readonly ConditionACPower?: boolean | readonly boolean[];
233
234
  /** Check whether the system is running on a specific architecture. Takes one of x86, x86-64, ppc, ppc-le, ppc64, ppc64-le, ia64, parisc, parisc64, s390, s390x, sparc, sparc64, mips, mips-le, mips64, mips64-le, alpha, arm, arm-be, arm64, arm64-be, sh, sh64, m68k, tilegx, cris, arc, arc-be, or native. Source: systemd v260.1, `systemd.unit(5)`. */
234
235
  readonly ConditionArchitecture?: string | readonly string[];
235
- /** Verify that a given CPU feature is available via the CPUID instruction. This condition only does something on i386 and x86-64 processors. On other processors it is assumed that the CPU does not support the given feature. It checks the leaves 1, 7, 0x80000001, and 0x80000007. Valid values are: fpu, vme, de, pse, tsc, msr, pae, mce, cx8, apic, sep, mtrr, pge, mca, cmov, pat, pse36, clflush, mmx, fxsr, sse, sse2, ht, pni, pclmul, monitor, ssse3, fma3, cx16, sse4_1, sse4_2, movbe, popcnt, aes, xsave, osxsave, avx, f16c, rdrand, bmi1, avx2, bmi2, rdseed, adx, sha_ni, syscall, rdtscp, lm, lahf_lm, abm, constant_tsc. Source: systemd v260.1, `systemd.unit(5)`. */
236
+ /** Checks CPU features via CPUID on i386 and x86-64, or ELF hardware capabilities on arm64. Accepts architecture-qualified names such as `arm64.bti`; a mismatched architecture prefix evaluates to false. On other architectures, features are assumed unsupported. Source: systemd v262, `systemd.unit(5)`. */
236
237
  readonly ConditionCPUFeature?: string | readonly string[];
237
238
  /** Verify that the overall system (memory, CPU or IO) pressure is below or equal to a threshold. This setting takes a threshold value as argument. It can be specified as a simple percentage value, suffixed with %, in which case the pressure will be measured as an average over the last five minutes before the attempt to start the unit is performed. Alternatively, the average timespan can also be specified using / as a separator, for example: 10%/1min. The supported timespans match what the kernel provides, and are limited to 10sec, 1min and 5min. The full PSI will be checked first, and if not found some will be checked. For more details, see the documentation on PSI (Pressure Stall Information) . Source: systemd v260.1, `systemd.unit(5)`. */
238
239
  readonly ConditionCPUPressure?: string | readonly string[];
@@ -270,7 +271,7 @@ export interface SystemdUnitSection extends CustomDirectiveSection {
270
271
  readonly ConditionKernelModuleLoaded?: string | readonly string[];
271
272
  /** May be used to check whether the kernel version (as reported by uname -r) matches a certain expression, or if prefixed with the exclamation mark, does not match. The argument must be a list of (potentially quoted) expressions. Each expression starts with one of = or != for string comparisons, <, <=, ==, <>, >=, > for version comparisons, or $=, !$= for a shell-style glob match. If no operator is specified, $= is implied. Source: systemd v260.1, `systemd.unit(5)`. */
272
273
  readonly ConditionKernelVersion?: string | readonly string[];
273
- /** Matches machine tags configured in `machine-info(5)`. Source: systemd v261, `systemd.unit(5)`. */
274
+ /** Matches machine tags from `machine-info(5)` using shell-style patterns, optionally negated with `!`. Parameterized tags include their value in the match: `role=web` matches exactly, `role=*` matches any value for that key, and `role` does not match `role=web`. Source: systemd v262, `systemd.unit(5)`. */
274
275
  readonly ConditionMachineTag?: string | readonly string[];
275
276
  /** Verify that the specified amount of system memory is available to the current system. Takes a memory size in bytes as argument, optionally prefixed with a comparison operator <, <=, = (or ==), != (or <>), >=, >. On bare-metal systems compares the amount of physical memory in the system with the specified size, adhering to the specified comparison operator. In containers compares the amount of memory assigned to the container instead. Source: systemd v260.1, `systemd.unit(5)`. */
276
277
  readonly ConditionMemory?: number | string | readonly (number | string)[];
@@ -280,7 +281,7 @@ export interface SystemdUnitSection extends CustomDirectiveSection {
280
281
  readonly ConditionNeedsUpdate?: string | readonly string[];
281
282
  /** Verify that a specific key=value pair is set in the host's os-release5. Source: systemd v260.1, `systemd.unit(5)`. */
282
283
  readonly ConditionOSRelease?: string | readonly string[];
283
- /** Check for the existence of a file. If the specified absolute path name does not exist, the condition will fail. If the absolute path name passed to ConditionPathExists= is prefixed with an exclamation mark (!), the test is negated, and the unit is only started if the path does not exist. Source: systemd v260.1, `systemd.unit(5)`. */
284
+ /** Checks for any file system object at an absolute path, including directories, device nodes, FIFOs, and sockets. Symbolic links are followed, so broken symbolic links do not count as existing. Prefix the path with `!` to require its absence. Source: systemd v262, `systemd.unit(5)`. */
284
285
  readonly ConditionPathExists?: string | readonly string[];
285
286
  /** Similar to `ConditionPathExists=`, but checks for the existence of at least one file or directory matching the specified globbing pattern. Source: systemd v260.1, `systemd.unit(5)`. */
286
287
  readonly ConditionPathExistsGlob?: string | readonly string[];
@@ -312,7 +313,7 @@ export interface SystemdUnitSection extends CustomDirectiveSection {
312
313
  readonly Description?: string;
313
314
  /** A space-separated list of URIs referencing documentation for this unit or its configuration. Accepted are only URIs of the types http://, https://, file:, info:, man:. For more information about the syntax of these URIs, see uri(7). The URIs should be listed in order of relevance, starting with the most relevant. It is a good idea to first reference documentation that explains what the unit's purpose is, followed by how it is configured, followed by any other related documentation. This option may be specified more than once, in which case the specified list of URIs is merged. If the empty string is assigned to this option, the list is reset and all prior assignments will have no effect. Source: systemd v260.1, `systemd.unit(5)`. */
314
315
  readonly Documentation?: string | readonly string[];
315
- /** Action to take when the unit stops in a failed state. Source: systemd v260.1, `systemd.unit(5)`. */
316
+ /** Action to take when the unit stops in a failed state. For `exit` and `exit-force`, the manager returns the main process exit status, or 255 if none is available, unless overridden by `FailureActionExitStatus=`. Source: systemd v261.3, `systemd.unit(5)`. */
316
317
  readonly FailureAction?: UnitActionDirectiveValue;
317
318
  /** Exit status to propagate when `FailureAction=` triggers `exit` or `exit-force`. Source: systemd v260.1, `systemd.unit(5)`. */
318
319
  readonly FailureActionExitStatus?: string | number;
@@ -362,13 +363,13 @@ export interface SystemdUnitSection extends CustomDirectiveSection {
362
363
  readonly StartLimitAction?: UnitActionDirectiveValue;
363
364
  /** See systemd.unit(5) for StartLimitBurst=. Source: systemd v260.1, `systemd.unit(5)`. */
364
365
  readonly StartLimitBurst?: number;
365
- /** Configure unit start rate limiting. Units which are started more than burst times within an interval time span are not permitted to start any more. Use StartLimitIntervalSec= to configure the checking interval and StartLimitBurst= to configure how many starts per interval are allowed. Source: systemd v260.1, `systemd.unit(5)`. */
366
+ /** Configure unit start rate limiting. Units which are started more than burst times within an interval time span are not permitted to start any more. Use StartLimitIntervalSec= to configure the checking interval and StartLimitBurst= to configure how many starts per interval are allowed. Since systemd v262, time spent suspended counts toward the interval. Sources: systemd v262, `systemd.unit(5)` and `NEWS`. */
366
367
  readonly StartLimitIntervalSec?: number | string;
367
368
  /** Units whose stop requests should also stop this unit. Source: systemd v260.1, `systemd.unit(5)`. */
368
369
  readonly StopPropagatedFrom?: string | readonly string[];
369
370
  /** If true, this unit will be stopped when it is no longer used. Note that, in order to minimize the work to be executed, systemd will not stop units by default unless they are conflicting with other units, or the user explicitly requested their shut down. If this option is set, a unit will be automatically cleaned up if no other active unit requires it. Defaults to false. Source: systemd v260.1, `systemd.unit(5)`. */
370
371
  readonly StopWhenUnneeded?: boolean;
371
- /** Action to take when the unit stops in an inactive state. Source: systemd v260.1, `systemd.unit(5)`. */
372
+ /** Action to take when the unit stops in an inactive state. For `exit` and `exit-force`, the manager returns the main process exit status, or leaves its exit status unchanged if none is available, unless overridden by `SuccessActionExitStatus=`. Source: systemd v261.3, `systemd.unit(5)`. */
372
373
  readonly SuccessAction?: UnitActionDirectiveValue;
373
374
  /** Exit status to propagate when `SuccessAction=` triggers `exit` or `exit-force`. Source: systemd v260.1, `systemd.unit(5)`. */
374
375
  readonly SuccessActionExitStatus?: string | number;
@@ -452,7 +453,7 @@ export interface SystemdTimerSection extends CustomDirectiveSection {
452
453
  *
453
454
  * This interface includes directives from `systemd.service(5)`,
454
455
  * `systemd.exec(5)`, `systemd.kill(5)`, and
455
- * `systemd.resource-control(5)` as of systemd v260.1.
456
+ * `systemd.resource-control(5)`, reviewed through systemd v262.
456
457
  */
457
458
  export interface SystemdServiceSection extends CustomDirectiveSection {
458
459
  /** This setting controls the cpuset controller in the unified hierarchy. Source: systemd v260.1, `systemd.resource-control(5)`. */
@@ -683,6 +684,8 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
683
684
  readonly LogsDirectoryMode?: string;
684
685
  /** Storage quota for directories created by `LogsDirectory=`. Source: systemd v260.1, `systemd.exec(5)`. */
685
686
  readonly LogsDirectoryQuota?: number | string;
687
+ /** Creates named Live Update Orchestrator (LUO) sessions and passes their file descriptors through the service's file descriptor store. Takes whitespace-separated names; repeated assignments are combined, and an empty string resets the list. Implies `FileDescriptorStoreMax=` is at least the number of sessions. Use `FileDescriptorStorePreserve=true` to preserve them across kexec. Existing sessions with the same name are reused. If `/dev/liveupdate` is unavailable, the service starts normally without sessions. Only supported by the system service manager. Source: systemd v262, `systemd.service(5)`. */
688
+ readonly LUOSession?: string | readonly string[];
686
689
  /** See systemd.service(5) for ManagedOOMMemoryPressure=. Source: systemd v260.1, `systemd.service(5)`. */
687
690
  readonly ManagedOOMMemoryPressure?: ManagedOOMDirectiveValue;
688
691
  /** Overrides the default memory pressure duration set by oomd.conf5 for the cgroup of this unit. The specified value supports a time unit such as ms or μs, see systemd.time(7) for details on the permitted syntax. Must be set to either empty or a value of at least 1s. Defaults to empty, which means to use the default set by oomd.conf5. This property is ignored unless ManagedOOMMemoryPressure=kill. Source: systemd v260.1, `systemd.resource-control(5)`. */
@@ -731,7 +734,7 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
731
734
  readonly NFTSet?: string | readonly string[];
732
735
  /** Controls the NUMA node list which will be applied alongside with selected NUMA policy. Takes a list of NUMA nodes and has the same syntax as a list of CPUs for CPUAffinity= option or special "all" value which will include all available NUMA nodes in the mask. Note that the list of NUMA nodes is not required for default and local policies and for preferred policy we expect a single NUMA node. Source: systemd v260.1, `systemd.exec(5)`. */
733
736
  readonly NUMAMask?: string | readonly string[];
734
- /** Controls the NUMA memory policy of the executed processes. Takes a policy type, one of: default, preferred, bind, interleave and local. A list of NUMA nodes that should be associated with the policy must be specified in NUMAMask=. For more details on each policy please see, set_mempolicy(2). For overall overview of NUMA support in Linux see, numa(7). Source: systemd v260.1, `systemd.exec(5)`. */
737
+ /** Controls the NUMA memory policy: `default`, `preferred`, `bind`, `interleave`, `local`, `preferred-many` (Linux 5.15 or newer), or `weighted-interleave` (Linux 6.9 or newer). Configure associated nodes with `NUMAMask=` and weighted-interleave weights through `/sys/kernel/mm/mempolicy/weighted_interleave/`. Unsupported kernel policies log a warning and are ignored. Source: systemd v262, `systemd.exec(5)`. */
735
738
  readonly NUMAPolicy?: string | readonly string[];
736
739
  /** Takes an absolute file system path referring to a Linux network namespace pseudo-file (i.e. a file like /proc/$PID/ns/net or a bind mount or symlink to one). When set the invoked processes are added to the network namespace referenced by that path. The path has to point to a valid namespace file at the moment the processes are forked off. If this option is used PrivateNetwork= has no effect. If this option is used together with JoinsNamespaceOf= then it only has an effect if this unit is started before any of the listed units that have PrivateNetwork= or NetworkNamespacePath= configured, as otherwise the network namespace of those units is reused. Source: systemd v260.1, `systemd.exec(5)`. */
737
740
  readonly NetworkNamespacePath?: string;
@@ -803,7 +806,7 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
803
806
  readonly ReadWritePaths?: string | readonly string[];
804
807
  /** Supports either booleans or a space-separated list of refreshable resource kinds such as `extensions` and `credentials`. Source: systemd v260.1, `systemd.service(5)`. */
805
808
  readonly RefreshOnReload?: boolean | string;
806
- /** Configures the UNIX process signal to send to the service's main process when asked to reload the service's configuration. Defaults to SIGHUP. This option has no effect unless Type=notify-reload is used, see above. Source: systemd v260.1, `systemd.service(5)`. */
809
+ /** Selects the signal sent to the main process to reload a `Type=notify-reload` service. Defaults to SIGHUP and has no effect for other service types. The main process must catch or block this signal before its initial `READY=1` notification, or startup fails. The handler is checked again before reloading; a missing handler then causes a warning, but the signal is still sent. Source: systemd v262, `systemd.service(5)`. */
807
810
  readonly ReloadSignal?: NodeJS.Signals | number;
808
811
  /** Takes a boolean value that specifies whether the service shall be considered active even when all its processes exited. Defaults to no. Source: systemd v260.1, `systemd.service(5)`. */
809
812
  readonly RemainAfterExit?: boolean;
@@ -821,6 +824,8 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
821
824
  readonly RestartMode?: ServiceRestartModeDirectiveValue;
822
825
  /** Takes a list of exit status definitions that, when returned by the main service process, will prevent automatic service restarts, regardless of the restart setting configured with Restart=. Exit status definitions can be numeric termination statuses, termination status names, or termination signal names, separated by spaces. Defaults to the empty list, so that, by default, no exit status is excluded from the configured restart logic. A service with the RestartPreventExitStatus= setting RestartPreventExitStatus=TEMPFAIL 250 SIGKILL Exit status 75 (TEMPFAIL), 250, and the termination signal SIGKILL will not result in automatic service restarting. This option may appear more than once, in which case the list of restart-preventing statuses is merged. If the empty string is assigned to this option, the list is reset and all prior assignments of this option will have no effect. Source: systemd v260.1, `systemd.service(5)`. */
823
826
  readonly RestartPreventExitStatus?: string | number | readonly (string | number)[];
827
+ /** Adds a uniformly distributed random delay between zero and this duration to automatic restarts, on top of `RestartSec=` and any exponential backoff configured by `RestartSteps=`/`RestartMaxDelaySec=`. Accepts seconds or a time span string. Defaults to 0, disabling the extra delay. Source: systemd v262, `systemd.service(5)`. */
828
+ readonly RestartRandomizedDelaySec?: number | string;
824
829
  /** Configures the time to sleep before restarting a service (as configured with Restart=). Takes a unit-less value in seconds, or a time span value such as "5min 20s". Defaults to 100ms. Source: systemd v260.1, `systemd.service(5)`. */
825
830
  readonly RestartSec?: number | string;
826
831
  /** Configures the number of exponential steps to take to increase the interval of auto-restarts from RestartSec= to RestartMaxDelaySec=. Takes a positive integer or 0 to disable it. Defaults to 0. Hint: values between 3 and 5 are good choices when exponential backoff is desired. Source: systemd v260.1, `systemd.service(5)`. */
@@ -835,7 +840,7 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
835
840
  readonly RestrictNetworkInterfaces?: string | readonly string[];
836
841
  /** If set, any attempts to enable realtime scheduling in a process of the unit are refused. This restricts access to realtime task scheduling policies such as SCHED_FIFO, SCHED_RR or SCHED_DEADLINE. See sched7 for details about these scheduling policies. Realtime scheduling policies may be used to monopolize CPU time for longer periods of time, and may hence be used to lock up or otherwise trigger Denial-of-Service situations on the system. It is hence recommended to restrict access to realtime scheduling to the few programs that actually require them. Defaults to off. Source: systemd v260.1, `systemd.exec(5)`. */
837
842
  readonly RestrictRealtime?: boolean;
838
- /** If set, any attempts to set the set-user-ID (SUID) or set-group-ID (SGID) bits on files or directories will be denied (for details on these bits see inode7). As the SUID/SGID bits are mechanisms to elevate privileges, and allow users to acquire the identity of other users, it is recommended to restrict creation of SUID/SGID files to the few programs that actually require them. Note that this restricts marking of any type of file system object with these bits, including both regular files and directories (where the SGID is a different meaning than for files, see documentation). This option is implied if DynamicUser= is enabled. Source: systemd v260.1, `systemd.exec(5)`. */
843
+ /** If set, any attempts to set the set-user-ID (SUID) or set-group-ID (SGID) bits on files or directories will be denied (for details on these bits see inode7). As the SUID/SGID bits are mechanisms to elevate privileges, and allow users to acquire the identity of other users, it is recommended to restrict creation of SUID/SGID files to the few programs that actually require them. Note that this restricts marking of any type of file system object with these bits, including both regular files and directories (where the SGID is a different meaning than for files, see documentation). This option is implied if DynamicUser= is enabled. Seccomp blocks calls with directly supplied mode bits only when SUID or SGID is requested, but blocks calls such as openat2() entirely because their mode bits cannot be inspected. Source: systemd v261.3, `systemd.exec(5)`. */
839
844
  readonly RestrictSUIDSGID?: boolean;
840
845
  /** Takes a directory path relative to the host's root directory (i.e. the root of the system running the service manager). Sets the root directory for executed processes, with the pivot_root2 or chroot2 system call. If this is used, it must be ensured that the process binary and all its auxiliary files are available in the new root. Note that setting this parameter might result in additional dependencies to be added to the unit (see above). Source: systemd v260.1, `systemd.exec(5)`. */
841
846
  readonly RootDirectory?: string;
@@ -869,7 +874,7 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
869
874
  readonly RuntimeRandomizedExtraSec?: number | string;
870
875
  /** Set the SELinux security context of the executed process. If set, this will override the automated domain transition. However, the policy still needs to authorize the transition. This directive is ignored if SELinux is disabled. If prefixed by -, failing to set the SELinux security context will be ignored, but it is still possible that the subsequent execve() may fail if the policy does not allow the transition for the non-overridden context. This does not affect commands prefixed with +. See setexeccon3 for details. Source: systemd v260.1, `systemd.exec(5)`. */
871
876
  readonly SELinuxContext?: string;
872
- /** Controls the secure bits set for the executed process. Takes a space-separated combination of options from the following list: keep-caps, keep-caps-locked, no-setuid-fixup, no-setuid-fixup-locked, noroot, and noroot-locked. This option may appear more than once, in which case the secure bits are ORed. If the empty string is assigned to this option, the bits are reset to 0. This does not affect commands prefixed with +. See capabilities(7) for details. Source: systemd v260.1, `systemd.exec(5)`. */
877
+ /** Sets a space-separated combination of secure bits: `keep-caps`, `no-setuid-fixup`, `noroot`, `no-cap-ambient-raise`, `exec-restrict-file`, `exec-deny-interactive`, and their `-locked` variants. Repeated assignments combine the bits; an empty string resets them to zero. Commands prefixed with `+` are unaffected. See capabilities(7) and the kernel executability-check documentation. Source: systemd v262, `systemd.exec(5)`. */
873
878
  readonly SecureBits?: string | readonly string[];
874
879
  /** Specifies whether to send SIGHUP to remaining processes immediately after sending the signal configured with KillSignal=. This is useful to indicate to shells and shell-like programs that their connection has been severed. Takes a boolean value. Defaults to no. Source: systemd v260.1, `systemd.kill(5)`. */
875
880
  readonly SendSIGHUP?: boolean;
@@ -891,15 +896,15 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
891
896
  readonly SocketBindDeny?: string | readonly string[];
892
897
  /** Specifies the name of the socket units this service shall inherit socket file descriptors from when the service is started. Normally, it should not be necessary to use this setting, as all socket file descriptors whose unit shares the same name as the service (subject to the different unit name suffix of course) are passed to the spawned process. Source: systemd v260.1, `systemd.service(5)`. */
893
898
  readonly Sockets?: string | readonly string[];
894
- /** Controls where file descriptor 2 (stderr) of the executed processes is connected to. The available options are identical to those of StandardOutput=, with some exceptions: if set to inherit the file descriptor used for standard output is duplicated for standard error, while fd:name will use a default file descriptor name of stderr. Source: systemd v260.1, `systemd.exec(5)`. */
899
+ /** Controls where file descriptor 2 (stderr) of the executed processes is connected to. The available options are identical to those of StandardOutput=, with some exceptions: if set to inherit the file descriptor used for standard output is duplicated for standard error, while fd:name will use a default file descriptor name of stderr. File paths are opened by the service manager before SELinuxContext= or policy-defined transitions take effect; the process inherits the resulting descriptor. Source: systemd v261.3, `systemd.exec(5)`. */
895
900
  readonly StandardError?: StandardErrorDirectiveValue;
896
- /** Controls where file descriptor 0 (STDIN) of the executed processes is connected to. Takes one of null, tty, tty-force, tty-fail, data, file:path, socket or fd:name. Source: systemd v260.1, `systemd.exec(5)`. */
901
+ /** Controls where file descriptor 0 (STDIN) of the executed processes is connected to. Takes one of null, tty, tty-force, tty-fail, data, file:path, socket or fd:name. For file:path, the service manager opens the path before SELinuxContext= or policy-defined transitions take effect; the process inherits the resulting descriptor. Source: systemd v261.3, `systemd.exec(5)`. */
897
902
  readonly StandardInput?: StandardInputDirectiveValue;
898
903
  /** Configures arbitrary textual or binary data to pass via file descriptor 0 (STDIN) to the executed processes. These settings have no effect unless StandardInput= is set to data (which is the default if StandardInput= is not set otherwise, but StandardInputText=/StandardInputData= is). Use this option to embed process input data directly in the unit file. Source: systemd v260.1, `systemd.exec(5)`. */
899
904
  readonly StandardInputData?: string | readonly string[];
900
905
  /** Configures arbitrary textual or binary data to pass via file descriptor 0 (STDIN) to the executed processes. These settings have no effect unless StandardInput= is set to data (which is the default if StandardInput= is not set otherwise, but StandardInputText=/StandardInputData= is). Use this option to embed process input data directly in the unit file. Source: systemd v260.1, `systemd.exec(5)`. */
901
906
  readonly StandardInputText?: string | readonly string[];
902
- /** Controls where file descriptor 1 (stdout) of the executed processes is connected to. Takes one of inherit, null, tty, journal, kmsg, journal+console, kmsg+console, file:path, append:path, truncate:path, socket or fd:name. Source: systemd v260.1, `systemd.exec(5)`. */
907
+ /** Controls where file descriptor 1 (stdout) of the executed processes is connected to. Takes one of inherit, null, tty, journal, kmsg, journal+console, kmsg+console, file:path, append:path, truncate:path, socket or fd:name. For file:path, append:path, and truncate:path, the service manager opens the path before SELinuxContext= or policy-defined transitions take effect; the process inherits the resulting descriptor. Source: systemd v261.3, `systemd.exec(5)`. */
903
908
  readonly StandardOutput?: StandardOutputDirectiveValue;
904
909
  /** This setting controls the cpuset controller in the unified hierarchy. Source: systemd v260.1, `systemd.resource-control(5)`. */
905
910
  readonly StartupAllowedCPUs?: string;
@@ -987,7 +992,7 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
987
992
  readonly USBFunctionDescriptors?: string;
988
993
  /** Configure the location of a file containing USB FunctionFS strings. Behavior is similar to USBFunctionDescriptors= above. Source: systemd v260.1, `systemd.service(5)`. */
989
994
  readonly USBFunctionStrings?: string;
990
- /** Explicitly unset environment variable assignments that would normally be passed from the service manager to invoked processes of this unit. Takes a space-separated list of variable names or variable assignments. This option may be specified more than once, in which case all listed variables/assignments will be unset. If the empty string is assigned to this option, the list of environment variables/assignments to unset is reset. If a variable assignment is specified (that is: a variable name, followed by =, followed by its value), then any environment variable matching this precise assignment is removed. If a variable name is specified (that is a variable name without any following = or value), then any assignment matching the variable name, regardless of its value is removed. Note that the effect of UnsetEnvironment= is applied as final step when the environment list passed to executed processes is compiled. That means it may undo assignments from any configuration source, including assignments made through Environment= or EnvironmentFile=, inherited from the system manager's global set of environment variables, inherited via PassEnvironment=, set by the service manager itself (such as $NOTIFY_SOCKET and such), or set by a PAM module (in case PAMName= is used). Source: systemd v260.1, `systemd.exec(5)`. */
995
+ /** Explicitly unset environment variable assignments that would normally be passed from the service manager to invoked processes of this unit. Takes a space-separated list of variable names or variable assignments. This option may be specified more than once, in which case all listed variables/assignments will be unset. If the empty string is assigned to this option, the list of environment variables/assignments to unset is reset. If a variable assignment is specified (that is: a variable name, followed by =, followed by its value), then any environment variable matching this precise assignment is removed. If a variable name is specified (that is a variable name without any following = or value), then any assignment matching the variable name, regardless of its value is removed. Note that the effect of UnsetEnvironment= is applied as final step when the environment list passed to executed processes is compiled. That means it may undo assignments from any configuration source, including assignments made through Environment= or EnvironmentFile=, inherited from the system manager's global set of environment variables, inherited via PassEnvironment=, set by the service manager itself (such as $NOTIFY_SOCKET and such), or set by a PAM module (in case PAMName= is used). Command-line variable expansion in ExecStart= and related directives happens before this removal and is unaffected. Source: systemd v261.3, `systemd.exec(5)`. */
991
996
  readonly UnsetEnvironment?: string | readonly string[];
992
997
  /** Set the UNIX user or group that the processes are executed as, respectively. Takes a single user or group name, or a numeric ID as argument. For system services (services run by the system service manager, i.e. managed by PID 1) and for user services of the root user (services managed by root's instance of systemd --user), the default is root, but this directive may be used to specify a different identity. For user services of any other user, switching user identity is not permitted, hence the only valid setting is the same user the user's service manager is running as. If no group is set, the default group of the user is used. This setting does not affect commands whose command line is prefixed with +. Source: systemd v260.1, `systemd.exec(5)`. */
993
998
  readonly User?: string;
@@ -997,7 +1002,7 @@ export interface SystemdServiceSection extends CustomDirectiveSection {
997
1002
  readonly UtmpIdentifier?: string;
998
1003
  /** Takes one of init, login or user. If UtmpIdentifier= is set, controls which type of utmp5/wtmp entries for this service are generated. This setting has no effect unless UtmpIdentifier= is set too. If init is set, only an INIT_PROCESS entry is generated and the invoked process must implement a getty-compatible utmp/wtmp logic. If login is set, first an INIT_PROCESS entry, followed by a LOGIN_PROCESS entry is generated. In this case, the invoked process must implement a login1-compatible utmp/wtmp logic. If user is set, first an INIT_PROCESS entry, then a LOGIN_PROCESS entry and finally a USER_PROCESS entry is generated. In this case, the invoked process may be any process that is suitable to be run as session leader. Defaults to init. Source: systemd v260.1, `systemd.exec(5)`. */
999
1004
  readonly UtmpMode?: UtmpModeDirectiveValue;
1000
- /** Configures the mechanism via which the service notifies the manager that the service start-up has finished. One of simple, exec, forking, oneshot, dbus, notify, notify-reload, or idle:. Source: systemd v260.1, `systemd.service(5)`. */
1005
+ /** Configures how the service notifies the manager that startup has finished: `simple`, `exec`, `forking`, `oneshot`, `dbus`, `notify`, `notify-reload`, or `idle`. With `notify-reload`, the main process must catch or block `ReloadSignal=` before sending its initial `READY=1` notification, or startup fails. Source: systemd v262, `systemd.service(5)`. */
1001
1006
  readonly Type?: ServiceTypeDirectiveValue;
1002
1007
  /** Configures the watchdog timeout for a service. The watchdog is activated when the start-up is completed. The service must call sd_notify3 regularly with WATCHDOG=1 (i.e. the "keep-alive ping"). If the time between two such calls is larger than the configured time, then the service is placed in a failed state and it will be terminated with SIGABRT (or the signal specified by WatchdogSignal=). By setting Restart= to on-failure, on-watchdog, on-abnormal or always, the service will be automatically restarted. The time configured here will be passed to the executed service process in the WATCHDOG_USEC= environment variable. This allows daemons to automatically enable the keep-alive pinging logic if watchdog support is enabled for the service. If this option is used, NotifyAccess= (see below) should be set to open access to the notification socket provided by systemd. If NotifyAccess= is not set, it will be implicitly set to main. Defaults to 0, which disables this feature. The service can check whether the service manager expects watchdog keep-alive notifications. See sd_watchdog_enabled3 for details. sd_event_set_watchdog3 may be used to enable automatic watchdog notification support. Source: systemd v260.1, `systemd.service(5)`. */
1003
1008
  readonly WatchdogSec?: number | string;
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-gV6lNweL.d.mts","names":[],"sources":["../src/main/internal.ts","../src/main/executable.ts","../src/main/systemd-service.ts","../src/main/systemd-timer.ts","../src/main/types.ts","../src/main/errors.ts","../src/main/notify.ts","../src/main/systemctl.ts","../src/main/systemd.ts","../src/main/index.ts"],"mappings":"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"}