| 1 | #!/bin/sh |
| 2 | # lchat-svc — scoped systemctl for operators, without a root shell. |
| 3 | # |
| 4 | # Ubuntu 26.04 ships sudo-rs, which (unlike classic sudo) rejects argument |
| 5 | # wildcards in sudoers — so `systemctl restart lchat-*` cannot be granted |
| 6 | # directly. Operators go through this helper instead; sudoers grants only |
| 7 | # `/usr/local/sbin/lchat-svc *` (a whole-argument wildcard, which sudo-rs |
| 8 | # does allow), and the helper confines systemctl to lchat* units. Same shape |
| 9 | # as lchat-env-install: the narrow, auditable privileged surface. Amend via a |
| 10 | # reviewed PR + redeploy, never by hand on a box. |
| 11 | # |
| 12 | # lchat-svc daemon-reload |
| 13 | # lchat-svc <verb> [--now] <lchat-unit...> |
| 14 | # verb ∈ start|stop|restart|reload|enable|disable|status|reset-failed|is-active|is-enabled |
| 15 | set -eu |
| 16 | |
| 17 | verb="${1:-}" |
| 18 | [ -n "$verb" ] || { echo "usage: lchat-svc <verb> [--now] <lchat-unit...> | lchat-svc daemon-reload" >&2; exit 1; } |
| 19 | |
| 20 | # daemon-reload is unit-less and always safe to allow. |
| 21 | if [ "$verb" = daemon-reload ]; then |
| 22 | [ "$#" -eq 1 ] || { echo "daemon-reload takes no arguments" >&2; exit 1; } |
| 23 | exec systemctl daemon-reload |
| 24 | fi |
| 25 | |
| 26 | case "$verb" in |
| 27 | start|stop|restart|reload|enable|disable|status|reset-failed|is-active|is-enabled) ;; |
| 28 | *) echo "verb not permitted: $verb" >&2; exit 1 ;; |
| 29 | esac |
| 30 | shift |
| 31 | |
| 32 | # --now may follow enable/disable; pass it through if present. |
| 33 | now="" |
| 34 | if [ "${1:-}" = "--now" ]; then now="--now"; shift; fi |
| 35 | |
| 36 | [ "$#" -ge 1 ] || { echo "no unit given" >&2; exit 1; } |
| 37 | for u in "$@"; do |
| 38 | case "$u" in |
| 39 | lchat|lchat-*|lchat@*|lchat.*) ;; |
| 40 | *) echo "unit not permitted (lchat* only): $u" >&2; exit 1 ;; |
| 41 | esac |
| 42 | done |
| 43 | |
| 44 | exec systemctl "$verb" ${now:+"$now"} "$@" |
| 45 | |