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