build: add systemd autostart for the receiver
Ship systemd/screencast-receiver.service, a template the install script substitutes (binary path, run user) and enables so the receiver starts at boot after the network and avahi-daemon, with restart-on-failure and a raised start limit for slow-booting boards. SIGTERM gives the graceful shutdown that withdraws the mDNS announcement — systemctl stop never triggers the stale-record trap that kill -9 does. Headless consoles render via KMSDRM (the script adds the run user to video/render/input); desktop autologin sessions uncomment two documented Environment lines. The install script gains SC_RECEIVER_SERVICE=0 to opt out. Validated: systemd-analyze verify on the substituted unit (only the expected missing /usr/local/bin path on this dev machine), and a live lifecycle test as a user unit: the service-managed receiver announced itself (--discover found it), systemctl stop produced a clean exit (Result=success, no restart) and the mDNS announcement was withdrawn immediately.
This commit is contained in:
@@ -31,6 +31,12 @@ loopback; current phase is Phase 7.
|
||||
building SDL3 from source when the distro lacks it. Requires GCC 13+
|
||||
(`<format>`). Validated on x86_64: builds, tests 5/5, --discover works,
|
||||
--send refuses with a message.
|
||||
- **systemd autostart**: `systemd/screencast-receiver.service` is a
|
||||
template (`__SC_RECEIVER_BIN__`/`__SC_RECEIVER_USER__`); the install
|
||||
script substitutes and enables it (opt out: `SC_RECEIVER_SERVICE=0`),
|
||||
adding the run user to video/render/input for headless KMSDRM.
|
||||
Lifecycle-validated with a user unit on this machine: SIGTERM stop →
|
||||
clean exit (success), mDNS withdrawn, no restart.
|
||||
- **mDNS operational lesson (hit during validation)**: `kill -9` on a
|
||||
process holding an avahi registration leaves stale daemon records; the
|
||||
service then browses but never resolves (timeout for every peer) until
|
||||
|
||||
@@ -51,7 +51,9 @@ The script installs the dependencies, builds a receiver-only binary
|
||||
(`-Dsender=false`), runs the test suite, and installs to `/usr/local/bin`
|
||||
(override with `SC_RECEIVER_INSTALL_DIR`). It needs Raspberry Pi OS Trixie
|
||||
or newer (GCC 13+ for C++20 `<format>`), builds SDL3 from source when the
|
||||
distribution does not package it, and enables `avahi-daemon`.
|
||||
distribution does not package it, and enables `avahi-daemon`. It also
|
||||
installs and enables a systemd service so the receiver starts at boot —
|
||||
`systemctl status screencast-receiver` to check on it.
|
||||
|
||||
Performance note: decoding is software H.264; on very small boards expect
|
||||
smooth playback for modest resolutions and reduced frame rates at high
|
||||
|
||||
@@ -70,6 +70,26 @@ records for that service name until they expire (~75 min), making resolution
|
||||
time out for every peer. If that happens, `systemctl restart avahi-daemon`
|
||||
clears it. Always stop screencast with Ctrl-C (SIGTERM).
|
||||
|
||||
## Receiver as a systemd service (autostart)
|
||||
|
||||
`scripts/install-receiver.sh` installs and enables
|
||||
`screencast-receiver.service` (see `systemd/screencast-receiver.service`).
|
||||
It starts at boot after the network and avahi-daemon, and restarts on
|
||||
failure. On a headless console SDL renders via KMSDRM; on a desktop
|
||||
autologin, edit `/etc/systemd/system/screencast-receiver.service` and
|
||||
uncomment the Environment lines for `XDG_RUNTIME_DIR`/`WAYLAND_DISPLAY`.
|
||||
|
||||
```sh
|
||||
systemctl status screencast-receiver # is it running?
|
||||
journalctl -u screencast-receiver -f # follow logs
|
||||
sudo systemctl stop screencast-receiver # graceful; withdraws mDNS
|
||||
sudo systemctl disable --now screencast-receiver # turn autostart off
|
||||
```
|
||||
|
||||
Verified behavior: `systemctl stop` results in a clean exit (success,
|
||||
no restart) and the mDNS announcement is withdrawn — senders stop finding
|
||||
the receiver immediately.
|
||||
|
||||
## Sender / receiver loopback (Phase 5, manual)
|
||||
|
||||
Two terminals on the same desktop session:
|
||||
|
||||
+40
-10
@@ -15,6 +15,7 @@
|
||||
# Environment overrides:
|
||||
# SC_RECEIVER_INSTALL_DIR install target (default /usr/local/bin)
|
||||
# SC_RECEIVER_SKIP_TESTS set to 1 to skip the test suite (faster install)
|
||||
# SC_RECEIVER_SERVICE set to 0 to skip the systemd service setup
|
||||
#
|
||||
# Requirements:
|
||||
# - Debian/Raspberry Pi OS (apt) with GCC 13 or newer (the code uses
|
||||
@@ -28,6 +29,7 @@ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
BUILD_DIR="${REPO_ROOT}/build-receiver"
|
||||
INSTALL_DIR="${SC_RECEIVER_INSTALL_DIR:-/usr/local/bin}"
|
||||
SKIP_TESTS="${SC_RECEIVER_SKIP_TESTS:-0}"
|
||||
ENABLE_SERVICE="${SC_RECEIVER_SERVICE:-1}"
|
||||
|
||||
log() { printf '\n=== %s\n' "$*"; }
|
||||
die() { printf 'install-receiver: error: %s\n' "$*" >&2; exit 1; }
|
||||
@@ -99,22 +101,50 @@ install -m 755 "${BUILD_DIR}/src/app/screencast" "${INSTALL_DIR}/screencast"
|
||||
|
||||
systemctl enable --now avahi-daemon 2>/dev/null || true
|
||||
|
||||
# --- systemd autostart --------------------------------------------------------
|
||||
if [ "${ENABLE_SERVICE}" = "1" ] && command -v systemctl >/dev/null 2>&1; then
|
||||
log "installing the systemd service (autostart on boot)"
|
||||
RUN_USER="${SUDO_USER:-root}"
|
||||
|
||||
# Headless KMSDRM rendering needs device access; skip groups that do
|
||||
# not exist on this system.
|
||||
if [ "${RUN_USER}" != "root" ] && id "${RUN_USER}" >/dev/null 2>&1; then
|
||||
for group in video render input; do
|
||||
if getent group "${group}" >/dev/null 2>&1; then
|
||||
usermod -aG "${group}" "${RUN_USER}" || true
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
sed -e "s|__SC_RECEIVER_BIN__|${INSTALL_DIR}/screencast|g" \
|
||||
-e "s|__SC_RECEIVER_USER__|${RUN_USER}|g" \
|
||||
"${REPO_ROOT}/systemd/screencast-receiver.service" \
|
||||
> /etc/systemd/system/screencast-receiver.service
|
||||
chmod 644 /etc/systemd/system/screencast-receiver.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now screencast-receiver.service
|
||||
else
|
||||
log "skipping the systemd service (disabled or systemd not available)"
|
||||
fi
|
||||
|
||||
log "done."
|
||||
cat <<'EOF'
|
||||
|
||||
The receiver is installed. On this machine run:
|
||||
The receiver is installed and starts automatically at boot.
|
||||
|
||||
screencast --receive
|
||||
|
||||
and on the sending machine:
|
||||
|
||||
screencast --send # discovers this receiver on the LAN
|
||||
systemctl status screencast-receiver # is it running?
|
||||
journalctl -u screencast-receiver -f # follow its logs
|
||||
sudo systemctl stop screencast-receiver # stop gracefully (Ctrl-C like)
|
||||
sudo systemctl disable --now screencast-receiver # turn autostart off
|
||||
|
||||
Notes for small boards (e.g. Pi Zero 2 W):
|
||||
- Decoding is software H.264; expect smooth playback for small streams
|
||||
and a lower frame rate at high resolutions.
|
||||
- Stop the receiver with Ctrl-C; never kill -9 (it would leave stale
|
||||
mDNS records that break discovery for ~75 minutes).
|
||||
- The receiver needs avahi-daemon (enabled) and a graphical session or
|
||||
KMS console to open its window.
|
||||
- Stop the receiver via systemctl (never kill -9; stale mDNS records
|
||||
would break discovery for ~75 minutes).
|
||||
- The service runs on the headless console via KMSDRM. On a desktop
|
||||
session instead, edit /etc/systemd/system/screencast-receiver.service:
|
||||
uncomment the Environment lines and point XDG_RUNTIME_DIR at the
|
||||
session owner's /run/user/<uid> (see the comments in that file).
|
||||
- avahi-daemon must stay enabled; it is what senders discover.
|
||||
EOF
|
||||
@@ -0,0 +1,46 @@
|
||||
# screencast-receiver.service — autostart for the receiver.
|
||||
#
|
||||
# This file is a template: scripts/install-receiver.sh substitutes
|
||||
# __SC_RECEIVER_BIN__ and __SC_RECEIVER_USER__ before installing it to
|
||||
# /etc/systemd/system/screencast-receiver.service.
|
||||
#
|
||||
# Display environments:
|
||||
#
|
||||
# 1. Headless console (SDL renders via KMSDRM): works as installed. The
|
||||
# configured User needs access to /dev/dri and the input devices; the
|
||||
# install script adds it to the video/render/input groups.
|
||||
#
|
||||
# 2. Graphical session (Wayland or X11 desktop with autologin): uncomment
|
||||
# the Environment lines in [Service] and adjust them to the session
|
||||
# owner (XDG_RUNTIME_DIR usually lives in /run/user/1000).
|
||||
|
||||
[Unit]
|
||||
Description=Screencast receiver (H.264 over RTP/UDP)
|
||||
Wants=network-online.target
|
||||
After=network-online.target avahi-daemon.service
|
||||
# Give slow-booting boards (and desktop sessions that start late) a chance;
|
||||
# systemd's default limit would give up after 5 fast failures.
|
||||
StartLimitIntervalSec=30
|
||||
StartLimitBurst=10
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=__SC_RECEIVER_USER__
|
||||
ExecStart=__SC_RECEIVER_BIN__ --receive
|
||||
# SIGTERM (systemctl stop) shuts the receiver down gracefully, which
|
||||
# withdraws its mDNS announcement. Never SIGKILL it; the systemd default
|
||||
# KillMode escalates only after the graceful timeout.
|
||||
KillSignal=SIGTERM
|
||||
TimeoutStopSec=10
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
|
||||
# Graphical session: uncomment and adjust to the session owner.
|
||||
#Environment=XDG_RUNTIME_DIR=/run/user/1000
|
||||
#Environment=WAYLAND_DISPLAY=wayland-0
|
||||
#Environment=DISPLAY=:0
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Reference in New Issue
Block a user