fix(systemd): give the receiver service its own VT instead of tty1

The receiver service never started on the Pi: systemd blocks before
exec in acquire_terminal(), waiting for a controlling terminal that
the console session or getty on tty1 already owns. Symptoms: the
status shows the main PID as "(screencast)" with Tasks:1 and ~40ms
CPU, a silent journal, and no mDNS announcement — the binary never
ran. Reproduced locally with a transient unit (the desktop's Wayland
session owns tty1) and diagnosed by gdb-attaching the stuck process.

The service now owns a dedicated free VT (tty7) so acquisition is
immediate, with a tolerant ExecStartPre chvt to make the screencast
the on-screen console at boot. tty1 keeps the console login;
Ctrl+Alt+F1/Ctrl+Alt+F7 switch between them. The previous advice to
enable Console Autologin actively caused the hang and is removed
from the unit, RUNBOOK, and install script notes.

Validated locally: the same properties via systemd-run give a
running receiver with all ~15 threads alive that announces itself
and is found by --discover; verified unit passes systemd-analyze
verify; graceful stop withdraws mDNS.
This commit is contained in:
2026-09-07 13:45:47 +02:00
parent 414c2c39ee
commit 343c6b45e6
4 changed files with 33 additions and 12 deletions
+11 -5
View File
@@ -26,12 +26,16 @@ StartLimitBurst=10
[Service]
Type=simple
User=__SC_RECEIVER_USER__
# SDL's KMSDRM backend wants a tty. The service gets its OWN virtual
# terminal: systemd must acquire the controlling terminal before exec, and
# acquiring one that another session owns (tty1: getty, console autologin,
# or a desktop) blocks the start FOREVER in a pre-exec placeholder process
# (systemctl status shows the main PID as "(screencast)"). tty7 is free on
# stock systems; tty1 keeps the console login.
ExecStartPre=-/usr/bin/chvt 7
ExecStart=__SC_RECEIVER_BIN__ --receive
# SDL's KMSDRM backend (headless console) expects a controlling terminal
# for its VT handling; system services have none by default. Harmless in
# desktop mode, where the process never touches the tty.
StandardInput=tty
TTYPath=/dev/tty1
TTYPath=/dev/tty7
# 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.
@@ -54,6 +58,8 @@ WantedBy=multi-user.target
# to the kernel via KMSDRM):
# * Raspberry Pi OS Lite, no desktop enabled (a running compositor would
# hold the DRM master and the receiver could not take it)
# * raspi-config → System Options → Boot → Console Autologin
# * no console autologin needed: the service owns tty7 and switches to it
# at boot; Ctrl+Alt+F1 returns to the console login, Ctrl+Alt+F7 to the
# screencast
# * disable console blanking: add consoleblank=0 to
# /boot/firmware/cmdline.txt