()
| 473 | }; |
| 474 | |
| 475 | const makeSystemdBackend = (): ServiceBackend => { |
| 476 | const unitName = `${SERVICE_LABEL}.service`; |
| 477 | return { |
| 478 | platform: "linux", |
| 479 | automated: true, |
| 480 | install: (descriptor) => |
| 481 | Effect.gen(function* () { |
| 482 | const fs = yield* FileSystem.FileSystem; |
| 483 | const path = yield* Path.Path; |
| 484 | const dataDir = resolveExecutorDataDir(path); |
| 485 | const logs = serviceLogDir(path); |
| 486 | yield* fs.makeDirectory(systemdUnitDir(path), { recursive: true }); |
| 487 | yield* fs.makeDirectory(logs, { recursive: true }); |
| 488 | const unit = generateSystemdUnit({ |
| 489 | execStart: serviceProgramArguments(descriptor), |
| 490 | environment: serviceEnvironment(descriptor, dataDir), |
| 491 | workingDirectory: dataDir, |
| 492 | stdoutPath: path.join(logs, "daemon.log"), |
| 493 | stderrPath: path.join(logs, "daemon.error.log"), |
| 494 | }); |
| 495 | yield* fs.writeFileString(systemdUnitPath(path), unit, { mode: 0o600 }); |
| 496 | // `systemctl --user` needs XDG_RUNTIME_DIR to reach the user bus. Supply |
| 497 | // it if the caller's environment lacks it (e.g. a non-login shell) so |
| 498 | // install is robust regardless of how it was invoked. |
| 499 | const username = userInfo().username; |
| 500 | const sdEnv = { |
| 501 | XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR ?? `/run/user/${currentUid()}`, |
| 502 | }; |
| 503 | yield* runCommand("systemctl", ["--user", "daemon-reload"], sdEnv).pipe(Effect.ignore); |
| 504 | const enable = yield* runCommand( |
| 505 | "systemctl", |
| 506 | ["--user", "enable", "--now", unitName], |
| 507 | sdEnv, |
| 508 | ); |
| 509 | if (enable.code !== 0) { |
| 510 | return yield* Effect.fail( |
| 511 | new Error( |
| 512 | `systemctl --user enable failed (exit ${enable.code}): ${enable.stderr.trim()}`, |
| 513 | ), |
| 514 | ); |
| 515 | } |
| 516 | // Enable lingering so the user manager — and this enabled service — |
| 517 | // starts at BOOT, not just on login, so the daemon survives a reboot |
| 518 | // unattended (verified in a real Ubuntu VM via loginctl). Best-effort: |
| 519 | // if the platform needs privilege, the service still works for the |
| 520 | // logged-in case and `service status` flags the missing linger. |
| 521 | yield* runCommand("loginctl", ["enable-linger", username], sdEnv).pipe(Effect.ignore); |
| 522 | }), |
| 523 | uninstall: () => |
| 524 | Effect.gen(function* () { |
| 525 | const fs = yield* FileSystem.FileSystem; |
| 526 | const path = yield* Path.Path; |
| 527 | const sdEnv = { |
| 528 | XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR ?? `/run/user/${currentUid()}`, |
| 529 | }; |
| 530 | yield* runCommand("systemctl", ["--user", "disable", "--now", unitName], sdEnv).pipe( |
| 531 | Effect.ignore, |
| 532 | ); |
no test coverage detected