Am Pi diagnostiziert: unter systemd ist HOME leer und cwd "/", lgpio legt seine Benachrichtigungs-Pipe aber unter "$HOME/.lgd-nfy0" an - das wird "//.lgd-nfy0" und scheitert an ProtectSystem=strict. gpiozero faellt bis zur experimentellen NativeFactory durch, die dann mit EINVAL abbricht; display.py faengt das als DisplayUnavailable ab und beendet sich mit 0, sodass der Ausfall wie ein fehlendes Panel aussieht statt wie die eigentliche Sandbox-Ursache. RuntimeDirectory=ninlight-display, WorkingDirectory= und Environment=HOME= zeigen jetzt auf ein eigenes, beschreibbares /run/ninlight-display - bewusst getrennt vom RuntimeDirectory der Hauptunit, da dieser Dienst dort nur liest. Szenario 19 in test_setup_pi.sh prueft die drei Zeilen in der ausgelieferten Unit; per Mutationsprobe bestaetigt, dass das Entfernen jeder einzelnen davon den Test rot werden laesst.
97 lines
6.0 KiB
Desktop File
97 lines
6.0 KiB
Desktop File
[Unit]
|
|
Description=ninlight E-Paper-Anzeige (Status der Migrations-/Rettungsstation)
|
|
# Nur eine Anzeige fuer den Hauptdienst - Wants=/After=, NICHT Requires=/BindsTo=: ein
|
|
# fehlendes oder abgestuerztes Panel darf server.py niemals mitreissen, und server.py
|
|
# darf umgekehrt starten, auch wenn dieser Dienst fehlt oder deaktiviert ist. Siehe
|
|
# display.py's Modul-Docstring: die Anzeige ist nirgends Voraussetzung fuer die
|
|
# Migration.
|
|
After=ninlight-station.service
|
|
Wants=ninlight-station.service
|
|
# Gehoeren seit systemd 229 ins [Unit], nicht ins [Service] (dort standen sie zuerst -
|
|
# Fixrunde 1, I1: systemd ignoriert sie dort mit "Unknown key name", die Vorgabe (5
|
|
# Starts/10s) greift dann bei RestartSec=5 nie, und genau das sollte die Deckelung unten
|
|
# eigentlich verhindern). ninlight-station.service (dieselbe Sektion) macht es richtig.
|
|
StartLimitIntervalSec=60
|
|
StartLimitBurst=5
|
|
|
|
[Service]
|
|
Type=simple
|
|
# Reihenfolge wichtig: systemd wendet Environment=/EnvironmentFile= in der Reihenfolge
|
|
# an, in der sie hier stehen, und eine spaetere Zuweisung ueberschreibt eine gleichnamige
|
|
# fruehere. EPD_REVISION=V3 hier ist nur der Vorgabewert, falls die Konfigurationsdatei
|
|
# (unten, per EnvironmentFile=) den Schluessel (noch) nicht enthaelt - z.B. auf einem Pi,
|
|
# der vor Task 6 aufgesetzt wurde. Steht er dort, gewinnt er.
|
|
Environment=EPD_REVISION=V3
|
|
Environment=NINLIGHT_RUN_DIR=/run/ninlight-station
|
|
Environment=NINLIGHT_STATION_CONF=/etc/ninlight-station.conf
|
|
# "-"-Praefix VOR dem Dateipfad, nicht vor dem Direktivennamen (Fixrunde 3, F1 - das war
|
|
# in Fixrunde 2 falsch herum: "-EnvironmentFile=..." ist kein gueltiger Schluesselname,
|
|
# systemd verwirft die Zeile komplett mit "Unknown key name", und die Konfigurationsdatei
|
|
# wurde dadurch ueberhaupt nicht mehr gelesen - Environment=EPD_REVISION=V3 oben haette
|
|
# IMMER gewonnen, unabhaengig davon, was in /etc/ninlight-station.conf steht). Mit dem
|
|
# Praefix am richtigen Platz gilt: ohne die Konfigurationsdatei startet die Anzeige
|
|
# trotzdem (mit den Environment=-Vorgaben oben) statt mit einem harten Fehler - "die
|
|
# Anzeige darf nirgends Voraussetzung sein" gilt auch fuer ihre eigene Konfiguration.
|
|
EnvironmentFile=-/etc/ninlight-station.conf
|
|
# RuntimeDirectory=/WorkingDirectory=/Environment=HOME= zusammen beheben einen Fund aus
|
|
# der Hardware-Abnahme: lgpio (gpiozero's bevorzugte Pin-Factory) legt beim Start eine
|
|
# Benachrichtigungs-Pipe unter "$HOME/.lgd-nfy0" an. Unter systemd ist HOME per Vorgabe
|
|
# LEER und das Arbeitsverzeichnis "/" - der Pfad wird "//.lgd-nfy0", und ProtectSystem=
|
|
# strict (unten) macht "/" schreibgeschuetzt. lgpio scheitert dann mit "Can't set
|
|
# permissions", gpiozero faellt ueber rpigpio/pigpio bis zur experimentellen NativeFactory
|
|
# durch, und DIE scheitert ihrerseits mit [Errno 22] Invalid argument - display.py faengt
|
|
# das korrekt als DisplayUnavailable ab und beendet sich mit 0 (siehe Kommentar zu
|
|
# Restart=on-failure unten), aber das Panel bleibt schwarz und das Journal sieht aus wie
|
|
# "kein Panel angeschlossen", obwohl die Hardware in Ordnung ist. Am Geraet nachgemessen
|
|
# (systemd-run mit denselben zwei Property=, gegen ein Shell-echo von $HOME und pwd).
|
|
#
|
|
# RuntimeDirectory=ninlight-display ist ABSICHTLICH ein eigenes Verzeichnis, nicht das
|
|
# RuntimeDirectory=ninlight-station der Hauptunit: dieser Dienst liest von dort nur
|
|
# (status.json/control.json ueber NINLIGHT_RUN_DIR, s.o.), er soll dort nichts anlegen
|
|
# koennen. Schriebe die Anzeige in fremden Laufzeitzustand, waere das eine unnoetige
|
|
# Kopplung an dessen Lebenszyklus (siehe die Begruendung zu ReadOnlyPaths= weiter unten:
|
|
# server.py's RuntimeDirectory wird beim Stop/Neustart geleert). systemd legt
|
|
# /run/ninlight-display beim Start dieser Unit selbst an (Vorgabe 0755, vom User dieses
|
|
# Dienstes - hier root, s.u. - beschreibbar) und raeumt es beim Stop wieder auf.
|
|
RuntimeDirectory=ninlight-display
|
|
WorkingDirectory=/run/ninlight-display
|
|
Environment=HOME=/run/ninlight-display
|
|
ExecStart=/usr/bin/python3 /opt/ninlight-station/display/display.py
|
|
|
|
# display.py faengt ein fehlendes/abgestuerztes Panel als DisplayUnavailable ab,
|
|
# protokolliert eine Zeile und beendet sich mit sys.exit(0) - das ist also nie ein
|
|
# "Fehlschlag" im systemd-Sinne und loest kein Restart= aus. Restart=on-failure (nicht
|
|
# always) greift nur fuer echte Abstuerze (z.B. ein Python-Traceback aus einem
|
|
# Programmierfehler); ohne den Deckel wuerde ein dauerhaft fehlendes Panel sonst NICHT
|
|
# staendig neu versucht (gut so), waehrend ein echter Bug endlos neu starten wuerde und
|
|
# so das Journal fluten koennte.
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
NoNewPrivileges=yes
|
|
ProtectSystem=strict
|
|
ProtectHome=yes
|
|
PrivateTmp=yes
|
|
# KEIN ReadOnlyPaths=/run/ninlight-station hier (stand hier zuerst, Fixrunde 1, C3):
|
|
# server.py's Unit traegt RuntimeDirectory=ninlight-station, und "ninlight-station off"
|
|
# stoppt den Hauptdienst regulaer - dabei raeumt systemd dessen RuntimeDirectory auf,
|
|
# sofern nicht gerade ein Neustart lief (RuntimeDirectoryPreserve=restart deckt nur den
|
|
# Neustart-Fall ab, nicht den regulaeren Stop). Ein nicht mit "-" prefigierter Pfad in
|
|
# ReadOnlyPaths=/ReadWritePaths=/BindPaths=, der beim Start fehlt, laesst systemd die
|
|
# Namensraum-Einrichtung mit 226/NAMESPACE abbrechen - ausgerechnet dann, wenn die
|
|
# Anzeige "Station aus" zeigen soll, waere sie selbst nicht mehr startbar gewesen. Die
|
|
# Zeile war obendrein wirkungslos: ProtectSystem=strict macht "/" (und damit "/run",
|
|
# soweit nicht per ReadWritePaths= explizit freigegeben) bereits schreibgeschuetzt.
|
|
RestrictAddressFamilies=AF_UNIX AF_NETLINK
|
|
MemoryMax=64M
|
|
|
|
# Laeuft als root (wie der Hauptdienst) fuer /dev/spidev0.0 und die GPIO-Zeichenleitung -
|
|
# kein SupplementaryGroups=spi gpio hier (stand hier zuerst, Fixrunde 1, I2): auf reinem
|
|
# Debian (im Unterschied zu Raspberry Pi OS) gibt es diese Gruppen oft gar nicht, und
|
|
# eine fehlende Gruppe ist fuer systemd ein harter Startfehler (216/GROUP) - ausgerechnet
|
|
# in der Unit, die nie stoeren soll, wenn etwas an der Anzeige fehlt. Als root sind die
|
|
# Gruppen fuer den Geraetezugriff ohnehin nicht noetig.
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|