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