From 0954c6520878e073c7822fc4a8f4a752474f5d7a Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Fri, 29 May 2026 12:23:30 +0900 Subject: fix(packaging): convert dispatch-systemd from user units to system template units (User=%i) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-user systemd manager (user@UID.service) fails to start on WSL (kernel 6.6.87.2, microsoft/WSL#13186 — 'Failed to spawn executor: Device or resource busy'), which breaks pacman's 30-systemd-daemon-reload-user.hook on install. Changes: - New dispatch-api@.service + dispatch-frontend@.service system template units with User=%i (run as the named instance user) - Remove old user-scope dispatch-api.service / dispatch-frontend.service - Install to /usr/lib/systemd/system/ instead of .../systemd/user/ - Update PKGBUILD, .install hints, and bin/service to use sudo systemctl dispatch-api@ --- bin/service | 44 ++++++++++++++++++++++++++---------- packaging/PKGBUILD | 18 +++++++++------ packaging/dispatch-api.service | 15 ------------ packaging/dispatch-api@.service | 21 +++++++++++++++++ packaging/dispatch-frontend.service | 16 ------------- packaging/dispatch-frontend@.service | 22 ++++++++++++++++++ packaging/dispatch-systemd.install | 19 ++++++++-------- packaging/dispatch.install | 6 ++--- 8 files changed, 99 insertions(+), 62 deletions(-) delete mode 100644 packaging/dispatch-api.service create mode 100644 packaging/dispatch-api@.service delete mode 100644 packaging/dispatch-frontend.service create mode 100644 packaging/dispatch-frontend@.service diff --git a/bin/service b/bin/service index eaa63eb..62471b9 100755 --- a/bin/service +++ b/bin/service @@ -4,6 +4,12 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +# Dispatch runs as system service instances keyed on the *invoking* user, so +# `bin/service start` brings the services up running as you (e.g. dispatch-api@tradam). +RUN_USER="$(id -un)" +API_UNIT="dispatch-api@${RUN_USER}" +FRONTEND_UNIT="dispatch-frontend@${RUN_USER}" + cmd="${1:-help}" shift 2>/dev/null || true @@ -12,16 +18,20 @@ usage() { Usage: bin/service Commands: - install Install the dispatch packages (systemd user units + + install Install the dispatch packages (systemd system unit templates + application files in /opt/dispatch). - start Start dispatch-api and dispatch-frontend. + start Start dispatch-api and dispatch-frontend as ${RUN_USER}. If already running, restart instead. Prints the frontend URL when ready. stop Stop dispatch-api and dispatch-frontend. status Show status of both services. logs Tail journal output for both services. -After install, configure /etc/dispatch/dispatch-api.conf before starting. +Services run as system instances for the current user: + ${API_UNIT} + ${FRONTEND_UNIT} +Start/stop use sudo (system manager). After install, configure +/etc/dispatch/dispatch-api.conf before starting. EOF } @@ -33,14 +43,14 @@ install() { echo "==> Installing..." "$PROJECT_DIR/bin/install-pkg" - systemctl --user daemon-reload + sudo systemctl daemon-reload echo "" echo "==> Installed. Edit /etc/dispatch/dispatch-api.conf, then run:" echo " bin/service start" } have_units() { - systemctl --user cat dispatch-api.service &>/dev/null + systemctl cat dispatch-api@.service &>/dev/null } start() { @@ -49,29 +59,39 @@ start() { return 1 fi local api_active frontend_active - api_active=$(systemctl --user is-active dispatch-api 2>/dev/null || echo "inactive") - frontend_active=$(systemctl --user is-active dispatch-frontend 2>/dev/null || echo "inactive") + api_active=$(systemctl is-active "$API_UNIT" 2>/dev/null || echo "inactive") + frontend_active=$(systemctl is-active "$FRONTEND_UNIT" 2>/dev/null || echo "inactive") if [ "$api_active" = "active" ] || [ "$frontend_active" = "active" ]; then echo "==> Services already running (api=$api_active frontend=$frontend_active) — restarting" - systemctl --user restart dispatch-api dispatch-frontend + sudo systemctl restart "$API_UNIT" "$FRONTEND_UNIT" + echo "dispatch-api + dispatch-frontend restarted (as ${RUN_USER})" else - systemctl --user start dispatch-api dispatch-frontend + sudo systemctl start "$API_UNIT" "$FRONTEND_UNIT" + echo "dispatch-api + dispatch-frontend started (as ${RUN_USER})" fi - echo "dispatch-api + dispatch-frontend $( [ "$api_active" = "active" ] || [ "$frontend_active" = "active" ] && echo restarted || echo started )" echo " → http://localhost:18391" } +stop() { + if ! have_units; then + echo "dispatch services not installed." + return 1 + fi + sudo systemctl stop "$API_UNIT" "$FRONTEND_UNIT" + echo "dispatch-api + dispatch-frontend stopped" +} + status() { if ! have_units; then echo "dispatch services not installed." return 1 fi - systemctl --user status dispatch-api dispatch-frontend --no-pager 2>/dev/null || true + systemctl status "$API_UNIT" "$FRONTEND_UNIT" --no-pager 2>/dev/null || true } logs() { - journalctl --user -u dispatch-api -u dispatch-frontend -f "$@" + sudo journalctl -u "$API_UNIT" -u "$FRONTEND_UNIT" -f "$@" } case "$cmd" in diff --git a/packaging/PKGBUILD b/packaging/PKGBUILD index a514eb5..6aa66cc 100644 --- a/packaging/PKGBUILD +++ b/packaging/PKGBUILD @@ -2,7 +2,7 @@ # # Split package: builds the base application once and produces three packages: # dispatch — application files + CLI wrappers + env configs -# dispatch-systemd — systemd user units for dispatch-api / dispatch-frontend +# dispatch-systemd — systemd system units (run as a given user: dispatch-api@) # dispatch-s6 — s6 service definitions for dispatch-api / dispatch-frontend # # The Electron desktop wrapper lives in a separate PKGBUILD at packaging/electron/. @@ -129,21 +129,25 @@ package_dispatch() { } # ---------------------------------------------------------------------------- -# dispatch-systemd — systemd user unit files +# dispatch-systemd — systemd system unit templates (run as a chosen user) +# +# These are *system* units (managed by PID 1), not user units, so they do not +# depend on a per-user `user@UID.service` manager. Each is a template keyed on +# the username: `dispatch-api@tradam` runs as the `tradam` user via User=%i. # ---------------------------------------------------------------------------- package_dispatch-systemd() { - pkgdesc='Systemd user units for the Dispatch API and Frontend services' + pkgdesc='Systemd system unit templates for the Dispatch API and Frontend services (run as dispatch-api@)' depends=("dispatch=${pkgver}" 'systemd') conflicts=('dispatch-s6') install=dispatch-systemd.install install -Dm644 \ - "${_packagingdir}/dispatch-api.service" \ - "${pkgdir}/usr/lib/systemd/user/dispatch-api.service" + "${_packagingdir}/dispatch-api@.service" \ + "${pkgdir}/usr/lib/systemd/system/dispatch-api@.service" install -Dm644 \ - "${_packagingdir}/dispatch-frontend.service" \ - "${pkgdir}/usr/lib/systemd/user/dispatch-frontend.service" + "${_packagingdir}/dispatch-frontend@.service" \ + "${pkgdir}/usr/lib/systemd/system/dispatch-frontend@.service" } # ---------------------------------------------------------------------------- diff --git a/packaging/dispatch-api.service b/packaging/dispatch-api.service deleted file mode 100644 index 120d460..0000000 --- a/packaging/dispatch-api.service +++ /dev/null @@ -1,15 +0,0 @@ -[Unit] -Description=Dispatch API Backend - -[Service] -Type=simple -WorkingDirectory=/opt/dispatch -ExecStart=/usr/bin/bun packages/api/src/index.ts -EnvironmentFile=-/etc/dispatch/dispatch-api.conf -Restart=on-failure -RestartSec=5 -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=default.target diff --git a/packaging/dispatch-api@.service b/packaging/dispatch-api@.service new file mode 100644 index 0000000..3449fcb --- /dev/null +++ b/packaging/dispatch-api@.service @@ -0,0 +1,21 @@ +# Dispatch API — system service template. +# Runs under the system manager (PID 1) but drops privileges to the user named +# in the instance: `dispatch-api@tradam` runs as the `tradam` user. +# +# Enable/start: +# sudo systemctl enable --now dispatch-api@ +[Unit] +Description=Dispatch API Backend (running as %i) +After=network.target + +[Service] +Type=simple +User=%i +WorkingDirectory=/opt/dispatch +ExecStart=/usr/bin/bun packages/api/src/index.ts +EnvironmentFile=-/etc/dispatch/dispatch-api.conf +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/packaging/dispatch-frontend.service b/packaging/dispatch-frontend.service deleted file mode 100644 index 4aca1a3..0000000 --- a/packaging/dispatch-frontend.service +++ /dev/null @@ -1,16 +0,0 @@ -[Unit] -Description=Dispatch Frontend (static file server) -After=dispatch-api.service - -[Service] -Type=simple -WorkingDirectory=/opt/dispatch -ExecStart=/usr/bin/bun packages/frontend/serve.ts -EnvironmentFile=-/etc/dispatch/dispatch-frontend.conf -Restart=on-failure -RestartSec=5 -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=default.target diff --git a/packaging/dispatch-frontend@.service b/packaging/dispatch-frontend@.service new file mode 100644 index 0000000..6f35cc1 --- /dev/null +++ b/packaging/dispatch-frontend@.service @@ -0,0 +1,22 @@ +# Dispatch Frontend — system service template. +# Runs under the system manager (PID 1) but drops privileges to the user named +# in the instance: `dispatch-frontend@tradam` runs as the `tradam` user. +# Tied to the matching API instance for the same user. +# +# Enable/start: +# sudo systemctl enable --now dispatch-frontend@ +[Unit] +Description=Dispatch Frontend (static file server, running as %i) +After=dispatch-api@%i.service + +[Service] +Type=simple +User=%i +WorkingDirectory=/opt/dispatch +ExecStart=/usr/bin/bun packages/frontend/serve.ts +EnvironmentFile=-/etc/dispatch/dispatch-frontend.conf +Restart=on-failure +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/packaging/dispatch-systemd.install b/packaging/dispatch-systemd.install index 3bd29be..5ebf261 100644 --- a/packaging/dispatch-systemd.install +++ b/packaging/dispatch-systemd.install @@ -1,24 +1,25 @@ post_install() { echo "" - echo "==> Dispatch systemd user units installed." - echo " Enable and start:" - echo " systemctl --user daemon-reload" - echo " systemctl --user enable --now dispatch-api dispatch-frontend" + echo "==> Dispatch systemd system unit templates installed." + echo " These run as the user named in the instance (replace )." + echo " Enable and start (e.g. for user 'tradam'):" + echo " sudo systemctl daemon-reload" + echo " sudo systemctl enable --now dispatch-api@ dispatch-frontend@" echo "" } post_upgrade() { echo "" echo "==> Dispatch systemd units upgraded." - echo " Reload and restart:" - echo " systemctl --user daemon-reload" - echo " systemctl --user restart dispatch-api dispatch-frontend" + echo " Reload and restart your instances (replace ):" + echo " sudo systemctl daemon-reload" + echo " sudo systemctl restart dispatch-api@ dispatch-frontend@" echo "" } pre_remove() { echo "" - echo "==> Stop dispatch services before removal:" - echo " systemctl --user disable --now dispatch-api dispatch-frontend" + echo "==> Stop dispatch services before removal (replace ):" + echo " sudo systemctl disable --now dispatch-api@ dispatch-frontend@" echo "" } diff --git a/packaging/dispatch.install b/packaging/dispatch.install index 76837f5..75b36cc 100644 --- a/packaging/dispatch.install +++ b/packaging/dispatch.install @@ -11,7 +11,7 @@ post_install() { echo " dispatch-frontend # static frontend (port 18391 by default)" echo "" echo " To run as a service, install one of:" - echo " pacman -S dispatch-systemd # systemd user services" + echo " pacman -S dispatch-systemd # systemd system services (run as your user)" echo " pacman -S dispatch-s6 # s6 services (Artix)" echo "" echo " For the Electron desktop wrapper:" @@ -22,8 +22,8 @@ post_install() { post_upgrade() { echo "" echo "==> Dispatch (base) upgraded." - echo " If you use dispatch-systemd, restart the units:" - echo " systemctl --user restart dispatch-api dispatch-frontend" + echo " If you use dispatch-systemd, restart the units (replace ):" + echo " sudo systemctl restart dispatch-api@ dispatch-frontend@" echo " If you use dispatch-s6, restart the services:" echo " s6-svc -r /run/service/dispatch-api" echo " s6-svc -r /run/service/dispatch-frontend" -- cgit v1.2.3