mirror of
https://github.com/n0mjs710/HBlink4.git
synced 2026-11-08 04:19:26 +00:00
Add scripts/install_services.sh to install the systemd units
The shipped unit files carry the author's user and paths (User=cort, /home/cort/hblink4), so copying them verbatim produces two services that fail to start. The docs said so only in a Customization section below the copy instructions, and told people to edit the repo copies -- which then conflict on the next git pull. The installer prompts for the service user and group (defaulting to the invoking sudo user and their primary group), substitutes them and the detected installation path into both units, installs to /etc/systemd/system/, reloads systemd, and optionally enables and starts the services. It backs up existing units, refuses unknown users/groups, checks the venv exists, warns when the service user cannot write the installation directory, and verifies the substitution landed before leaving anything in place. DESTDIR previews the output without touching the live system. The manual instructions now say to copy first and edit the installed copy, and note that daemon-reload is required after any unit change. Both unit files carry a header saying the same. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
512249c3ed
commit
331fe8814c
6 files changed
+270
-15
No files matched your search
+75
-14
@@ -12,7 +12,40 @@ step, so after a `git pull` you only need to restart the services. If you are
|
||||
upgrading from a version whose units pointed at `run.py`, re-copy the unit files
|
||||
below before restarting.
|
||||
|
||||
## Installation
|
||||
## Quick install (recommended)
|
||||
|
||||
The shipped unit files contain the author's user and paths (`User=cort`,
|
||||
`/home/cort/hblink4`), so they will not work as-is on your system. This script
|
||||
asks for your service user and group, fills in your paths, installs both units
|
||||
and reloads systemd:
|
||||
|
||||
```bash
|
||||
sudo ./scripts/install_services.sh
|
||||
```
|
||||
|
||||
It offers sensible defaults (the user who invoked `sudo`, and that user's
|
||||
primary group), backs up any units you already have, checks that your venv
|
||||
exists and that the service user can write to the installation directory, and
|
||||
optionally enables and starts the services.
|
||||
|
||||
To see what it would produce without touching the live system:
|
||||
|
||||
```bash
|
||||
sudo DESTDIR=/tmp/preview ./scripts/install_services.sh
|
||||
```
|
||||
|
||||
The manual steps below do the same thing by hand.
|
||||
|
||||
## Manual installation
|
||||
|
||||
> **⚠️ The shipped unit files contain the author's user and paths** —
|
||||
> `User=cort`, `Group=cort`, and `/home/cort/hblink4` throughout. **They will not
|
||||
> work as-is on your system.** You must change the user, the group, and every
|
||||
> path to match your installation.
|
||||
>
|
||||
> Edit the *installed* copies in `/etc/systemd/system/`, not the ones in the
|
||||
> repository — editing the repo copies makes them show up as local changes and
|
||||
> conflict on the next `git pull`.
|
||||
|
||||
### 1. Install the service files
|
||||
|
||||
@@ -23,15 +56,39 @@ sudo cp hblink4.service /etc/systemd/system/
|
||||
sudo cp hblink4-dash.service /etc/systemd/system/
|
||||
```
|
||||
|
||||
### 2. Reload systemd
|
||||
### 2. Set your user and paths
|
||||
|
||||
Tell systemd to reload its configuration:
|
||||
Four settings in each file need to match your system: `User=`, `Group=`,
|
||||
`WorkingDirectory=`, and `ExecStart=`. Edit them with your editor of choice:
|
||||
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/hblink4.service
|
||||
sudo nano /etc/systemd/system/hblink4-dash.service
|
||||
```
|
||||
|
||||
Or, run this from your HBlink4 directory to substitute them automatically:
|
||||
|
||||
```bash
|
||||
sudo sed -i "s|/home/cort/hblink4|$PWD|g; s|^User=cort$|User=$USER|; s|^Group=cort$|Group=$(id -gn)|" \
|
||||
/etc/systemd/system/hblink4.service /etc/systemd/system/hblink4-dash.service
|
||||
```
|
||||
|
||||
Check the result before continuing — no `cort` or `/home/cort` should remain:
|
||||
|
||||
```bash
|
||||
grep -nE "^(User|Group|WorkingDirectory|ExecStart)=" /etc/systemd/system/hblink4*.service
|
||||
```
|
||||
|
||||
### 3. Reload systemd
|
||||
|
||||
Tell systemd to reload its configuration. **This is required after any edit to a
|
||||
unit file** — systemd will keep using the old version until you do:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
```
|
||||
|
||||
### 3. Enable services (optional)
|
||||
### 4. Enable services (optional)
|
||||
|
||||
To start the services automatically at boot:
|
||||
|
||||
@@ -40,7 +97,7 @@ sudo systemctl enable hblink4
|
||||
sudo systemctl enable hblink4-dash
|
||||
```
|
||||
|
||||
### 4. Start the services
|
||||
### 5. Start the services
|
||||
|
||||
```bash
|
||||
sudo systemctl start hblink4
|
||||
@@ -92,22 +149,26 @@ sudo systemctl disable hblink4-dash
|
||||
|
||||
## Configuration
|
||||
|
||||
The service files are configured to:
|
||||
- Run as user `cort` in group `cort`
|
||||
- Use the Python virtual environment at `/home/cort/hblink4/venv`
|
||||
As shipped, the service files:
|
||||
- Run as user `cort` in group `cort` — **change this to your own user and group**
|
||||
- Use the Python virtual environment at `/home/cort/hblink4/venv` — **change to your path**
|
||||
- Automatically restart on failure (after 10 seconds)
|
||||
- Log to systemd journal (view with `journalctl`)
|
||||
- Start after network is available
|
||||
- Dashboard starts after and depends on HBlink4
|
||||
|
||||
### Customization
|
||||
### Changing a unit file later
|
||||
|
||||
If you need to modify the services (different user, paths, etc.), edit the files before copying them:
|
||||
Always edit the copy in `/etc/systemd/system/`, then reload and restart:
|
||||
|
||||
1. Edit `hblink4.service` and/or `hblink4-dash.service`
|
||||
2. Change `User=`, `Group=`, `WorkingDirectory=`, or `ExecStart=` as needed
|
||||
3. Copy to `/etc/systemd/system/`
|
||||
4. Run `sudo systemctl daemon-reload`
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/hblink4.service
|
||||
sudo systemctl daemon-reload # required, or systemd keeps the old version
|
||||
sudo systemctl restart hblink4
|
||||
```
|
||||
|
||||
Editing the copy in the repository has no effect on a running service, and
|
||||
leaves you with local changes that conflict on the next `git pull`.
|
||||
|
||||
## Security Features
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# NOTE: this file ships with the author's user and install paths. Change User=,
|
||||
# Group=, WorkingDirectory= and ExecStart= below to match your system.
|
||||
# Edit the installed copy in /etc/systemd/system/, not this one, and run
|
||||
# "sudo systemctl daemon-reload" after any change. See SYSTEMD.md.
|
||||
[Unit]
|
||||
Description=HBlink4 Dashboard
|
||||
After=network.target hblink4.service
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# NOTE: this file ships with the author's user and install paths. Change User=,
|
||||
# Group=, WorkingDirectory= and ExecStart= below to match your system.
|
||||
# Edit the installed copy in /etc/systemd/system/, not this one, and run
|
||||
# "sudo systemctl daemon-reload" after any change. See SYSTEMD.md.
|
||||
[Unit]
|
||||
Description=HBlink4 DMR Server
|
||||
After=network.target
|
||||
|
||||
@@ -74,7 +74,12 @@ See the [Configuration Guide](docs/configuration.md) for complete documentation
|
||||
## Running
|
||||
|
||||
### Production (systemd services)
|
||||
For production deployments with automatic startup, see [SYSTEMD.md](SYSTEMD.md).
|
||||
```bash
|
||||
sudo ./scripts/install_services.sh
|
||||
```
|
||||
Asks for your service user/group, fills in your paths, installs both units and
|
||||
reloads systemd. See [SYSTEMD.md](SYSTEMD.md) for manual installation and
|
||||
service management.
|
||||
|
||||
### Development
|
||||
```bash
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# HBlink4 Scripts
|
||||
|
||||
## install_services.sh
|
||||
|
||||
Installs the systemd unit files with your user, group and paths filled in — the
|
||||
shipped units carry the author's (`User=cort`, `/home/cort/hblink4`) and will not
|
||||
work unmodified.
|
||||
|
||||
```bash
|
||||
sudo ./scripts/install_services.sh
|
||||
```
|
||||
|
||||
Prompts for the service user and group (defaulting to whoever ran `sudo` and
|
||||
that user's primary group), rewrites both units, installs them to
|
||||
`/etc/systemd/system/`, runs `systemctl daemon-reload`, and optionally enables
|
||||
and starts the services. Any existing units are backed up as
|
||||
`<unit>.bak-<timestamp>` first.
|
||||
|
||||
Preview the result without touching the live system:
|
||||
|
||||
```bash
|
||||
sudo DESTDIR=/tmp/preview ./scripts/install_services.sh
|
||||
```
|
||||
|
||||
See [SYSTEMD.md](../SYSTEMD.md) for manual installation and service management.
|
||||
|
||||
## filter_user_csv.py
|
||||
|
||||
Filters the DMR user database to only include US and Canada entries, reducing file size and memory usage.
|
||||
|
||||
Executable
+157
@@ -0,0 +1,157 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Install the HBlink4 systemd units, with your user, group and paths filled in.
|
||||
#
|
||||
# sudo ./scripts/install_services.sh
|
||||
#
|
||||
# Prompts for the service user and group (sensible defaults are offered),
|
||||
# rewrites the shipped unit files accordingly, installs them to
|
||||
# /etc/systemd/system/, and reloads systemd. Existing units are backed up first.
|
||||
#
|
||||
# Set DESTDIR to install somewhere else (useful for inspecting the result
|
||||
# without touching the live system):
|
||||
#
|
||||
# sudo DESTDIR=/tmp/preview ./scripts/install_services.sh
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
RED='\033[0;31m'; GREEN='\033[0;32m'; BLUE='\033[0;34m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
||||
|
||||
die() { echo -e "${RED}✗ $*${NC}" >&2; exit 1; }
|
||||
note() { echo -e "${BLUE}$*${NC}"; }
|
||||
ok() { echo -e "${GREEN}✓ $*${NC}"; }
|
||||
warn() { echo -e "${YELLOW}⚠ $*${NC}"; }
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
UNIT_DIR="${DESTDIR:-/etc/systemd/system}"
|
||||
UNITS=(hblink4.service hblink4-dash.service)
|
||||
LIVE_INSTALL=0
|
||||
[ -z "${DESTDIR:-}" ] && LIVE_INSTALL=1
|
||||
|
||||
if [ "$LIVE_INSTALL" = 1 ] && [ "$(id -u)" -ne 0 ]; then
|
||||
die "This script must be run with sudo: sudo $0"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------- sanity checks
|
||||
|
||||
for u in "${UNITS[@]}"; do
|
||||
[ -f "$REPO_ROOT/$u" ] || die "Missing $REPO_ROOT/$u -- run this from the HBlink4 checkout."
|
||||
done
|
||||
|
||||
[ -x "$REPO_ROOT/venv/bin/python" ] || die \
|
||||
"No virtual environment at $REPO_ROOT/venv
|
||||
Create it first:
|
||||
python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt"
|
||||
|
||||
[ -f "$REPO_ROOT/config/config.json" ] || warn \
|
||||
"No config/config.json yet -- copy config/config_sample.json and edit it before starting."
|
||||
|
||||
echo
|
||||
note "HBlink4 service installer"
|
||||
echo " Install directory : $REPO_ROOT"
|
||||
echo " Unit directory : $UNIT_DIR"
|
||||
echo
|
||||
|
||||
# ------------------------------------------------------------------- user/group
|
||||
|
||||
# Default to whoever invoked sudo, falling back to the directory's owner --
|
||||
# HBlink4 must run as a user that can write logs/ and dashboard/data/.
|
||||
DEFAULT_USER="${SUDO_USER:-$(stat -c '%U' "$REPO_ROOT")}"
|
||||
DEFAULT_GROUP="$(id -gn "$DEFAULT_USER" 2>/dev/null || echo "$DEFAULT_USER")"
|
||||
|
||||
read -r -p "Service user [$DEFAULT_USER]: " SVC_USER
|
||||
SVC_USER="${SVC_USER:-$DEFAULT_USER}"
|
||||
read -r -p "Service group [$DEFAULT_GROUP]: " SVC_GROUP
|
||||
SVC_GROUP="${SVC_GROUP:-$DEFAULT_GROUP}"
|
||||
|
||||
id "$SVC_USER" >/dev/null 2>&1 || die "No such user: $SVC_USER"
|
||||
getent group "$SVC_GROUP" >/dev/null 2>&1 || die "No such group: $SVC_GROUP"
|
||||
|
||||
# The service user must be able to write persistence and logs.
|
||||
if [ "$(id -u)" -eq 0 ] && ! runuser -u "$SVC_USER" -- test -w "$REPO_ROOT" 2>/dev/null; then
|
||||
warn "$SVC_USER cannot write to $REPO_ROOT"
|
||||
warn "The dashboard needs write access for dashboard/data/ and logs/."
|
||||
fi
|
||||
|
||||
echo
|
||||
note "Will install as:"
|
||||
echo " User=$SVC_USER"
|
||||
echo " Group=$SVC_GROUP"
|
||||
echo " WorkingDirectory=$REPO_ROOT"
|
||||
echo " ExecStart=$REPO_ROOT/venv/bin/python $REPO_ROOT/hblink4/hblink.py $REPO_ROOT/config/config.json"
|
||||
echo " ExecStart=$REPO_ROOT/venv/bin/python $REPO_ROOT/dashboard/server.py"
|
||||
echo
|
||||
read -r -p "Proceed? [y/N]: " CONFIRM
|
||||
case "$CONFIRM" in [yY]|[yY][eE][sS]) ;; *) echo "Aborted."; exit 0 ;; esac
|
||||
|
||||
# ---------------------------------------------------------------------- install
|
||||
|
||||
mkdir -p "$UNIT_DIR"
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
|
||||
for u in "${UNITS[@]}"; do
|
||||
if [ -f "$UNIT_DIR/$u" ]; then
|
||||
cp -p "$UNIT_DIR/$u" "$UNIT_DIR/$u.bak-$STAMP"
|
||||
echo " backed up existing $u -> $u.bak-$STAMP"
|
||||
fi
|
||||
|
||||
# Substitute only the shipped author values; anchored so nothing else matches.
|
||||
sed -e "s|/home/cort/hblink4|$REPO_ROOT|g" \
|
||||
-e "s|^User=cort$|User=$SVC_USER|" \
|
||||
-e "s|^Group=cort$|Group=$SVC_GROUP|" \
|
||||
"$REPO_ROOT/$u" > "$UNIT_DIR/$u"
|
||||
chmod 644 "$UNIT_DIR/$u"
|
||||
ok "installed $UNIT_DIR/$u"
|
||||
done
|
||||
|
||||
# Confirm the substitution produced what we intended, rather than leaving a
|
||||
# half-rewritten unit in place. (Checked against the chosen values, not against
|
||||
# the author's literal strings -- those are legitimate if you happen to use the
|
||||
# same username or path.)
|
||||
for u in "${UNITS[@]}"; do
|
||||
grep -q "^User=$SVC_USER$" "$UNIT_DIR/$u" || die "$u: User= was not set correctly"
|
||||
grep -q "^Group=$SVC_GROUP$" "$UNIT_DIR/$u" || die "$u: Group= was not set correctly"
|
||||
grep -q "^WorkingDirectory=$REPO_ROOT$" "$UNIT_DIR/$u" || die "$u: WorkingDirectory= was not set correctly"
|
||||
grep -q "^ExecStart=$REPO_ROOT/venv/bin/python " "$UNIT_DIR/$u" || die "$u: ExecStart= was not set correctly"
|
||||
done
|
||||
|
||||
if [ "$LIVE_INSTALL" = 0 ]; then
|
||||
echo
|
||||
ok "Wrote units to $UNIT_DIR (DESTDIR set -- systemd not touched)."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
systemctl daemon-reload
|
||||
ok "systemctl daemon-reload"
|
||||
|
||||
# ------------------------------------------------------------- enable and start
|
||||
|
||||
echo
|
||||
read -r -p "Enable services at boot? [y/N]: " DO_ENABLE
|
||||
case "$DO_ENABLE" in
|
||||
[yY]|[yY][eE][sS]) systemctl enable "${UNITS[@]}" >/dev/null && ok "enabled at boot" ;;
|
||||
esac
|
||||
|
||||
read -r -p "Start (or restart) the services now? [y/N]: " DO_START
|
||||
case "$DO_START" in
|
||||
[yY]|[yY][eE][sS])
|
||||
systemctl restart "${UNITS[@]}"
|
||||
sleep 2
|
||||
echo
|
||||
for u in "${UNITS[@]}"; do
|
||||
if systemctl is-active --quiet "$u"; then
|
||||
ok "$u is running"
|
||||
else
|
||||
warn "$u did not start -- journalctl -u ${u%.service} -n 30"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
esac
|
||||
|
||||
echo
|
||||
note "Done. Useful commands:"
|
||||
echo " systemctl status hblink4 hblink4-dash"
|
||||
echo " journalctl -u hblink4 -f"
|
||||
echo
|
||||
note "If you edit a unit later, edit $UNIT_DIR/<unit> and run 'systemctl daemon-reload'."
|
||||
Reference in new issue
Block a user