server.py legt status.json ueber mkstemp() (Modus 0600) + os.replace() an; da
os.replace() den Modus der temporaeren Datei uebernimmt, war die Datei nach jedem
Schreiben nur fuer root lesbar - der Dienst laeuft als root, das CLI liest aber als
normaler Benutzer. os.chmod(tmp, 0o644) vor dem Rename behebt das umask-fest, auch
bei wiederholten Schreibvorgaengen.
ninlight-station status hat den fehlenden Lesezugriff bisher stillschweigend wie
"noch nie ausgeliefert" behandelt (beides fiel durch denselben "-r"-Test). Jetzt wird
zwischen "Datei fehlt" (unauffaellig) und "Datei da, aber nicht lesbar" (Fehlermeldung
auf stderr) unterschieden.
Neuer Test prueft, dass status.json nach dem ersten und nach einem weiteren
Schreibvorgang world-readable bleibt, auch unter restriktivem umask.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"systemctl is-active" schreibt den Zustand ("inactive", ...) auf stdout
und liefert dabei einen Rueckgabewert ungleich 0 - der "|| echo inaktiv"-
Zweig lief also zusaetzlich mit, und beide Zeilen landeten in der
Ausgabe. Ausserdem endete "status" ueber den letzten Befehl im
case-Zweig faktisch mit dessen Exit-Code, der 1 war, sobald noch nie
ausgeliefert wurde (kein /run/ninlight-station/status.json) - das machte
"status" in jedem Skript, das den Exit-Code abfragt, unbrauchbar.
Beide Stellen behoben: der Dienstzustand wird nur noch einmal ausgegeben,
und der Zweig endet mit explizitem "exit 0", sobald die Auskunft erteilt
werden konnte.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
setup_pi.sh sourcte /etc/ninlight-station.conf (0600 root:root) als
unprivilegierter Benutzer und scheiterte damit bei jedem Lauf, bevor
das NetworkManager-Profil angelegt wurde. Die Konfiguration wird jetzt
per "sudo cat" gelesen; ninlight-station macht denselben Fallback fuer
den dokumentierten Aufruf als normaler Benutzer, ohne die Datei fuer
alle lesbar zu machen.
Das AP-Profil wird nicht mehr per "nmcli ... wifi-sec.psk $PSK"
angelegt (das WLAN-Passwort waere sonst fuer jeden lokalen Prozess in
der Kommandozeile sichtbar), sondern als NetworkManager-Keyfile
(0600 root:root) geschrieben und per "nmcli connection load"
eingelesen.
deploy.sh loeschte bei einem gezielten Deploy eines einzelnen
Geraetetyps per "rsync --delete" alle Images anderer Geraetetypen aus
dem live ausgelieferten Verzeichnis. --delete laeuft jetzt nur noch
beim Vollabgleich (kein target-Argument); ein gezielter Deploy ersetzt
nur die genannten Dateien. Dabei zusaetzlich -c (Checksumme statt
Groesse+Zeitstempel) ergaenzt, sonst uebersieht rsync ein neues Image
mit zufaellig gleicher Byte-Groesse. Ausserdem meldet deploy.sh jetzt
einen fehlgeschlagenen Dienst-Neustart als Fehler statt ihn mit "Dienst
laeuft nicht" zu verwechseln.
Stub-Tests unter tools/station/tests/test_deploy.sh (ssh/rsync im PATH
ersetzt, Installationsschritt laeuft gegen ein Sandbox-Verzeichnis)
decken beide Bugfixes sowie die bereits bestehenden Faelle ab: Image
besteht die Trailer-Pruefung nicht, keine Images gefunden.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 'on' fing bisher nur einen bereits laufenden, aber toten Dienst ab. Scheiterte
schon der Aufruf "systemctl start" selbst (z.B. start-limit-hit nach einer
Absturzserie durch Restart=always), beendete set -eu das Skript dort, bevor
die Lebendigkeitspruefung je erreicht wurde - der eben hochgefahrene
Zugangspunkt blieb offen ohne Server dahinter stehen. Der Startaufruf wird
jetzt selbst abgefangen und faehrt im Fehlerfall dieselbe Rueckabwicklung wie
die is-active-Pruefung. "systemctl reset-failed" vor dem Start verhindert,
dass eine fruehere Absturzserie den naechsten ehrlichen Versuch blockiert.
- Stirbt der Dienst nach einem erfolgreichen Start, stoppt das Skript ihn jetzt
explizit, bevor es den Zugangspunkt herunterfaehrt und "abgeschaltet" meldet -
sonst haette ein mit Restart=always weiterlaufender Dienst im Hintergrund
einen saubereren Zustand suggeriert, als tatsaechlich vorlag.
- ninlight-station.service setzt StartLimitIntervalSec/StartLimitBurst jetzt
explizit statt sich stillschweigend auf systemds Vorgabewerte zu verlassen,
mit Begruendung als Kommentar: ohne Zugangspunkt kann server.py nicht binden
und stirbt sofort, Restart=always soll das nach kurzer Zeit ehrlich aufgeben
statt das Journal zu fluten.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- 'on' prueft nach dem Start wirklich, ob der Dienst aktiv ist (Type=simple
meldet sonst schon Erfolg, wenn der Prozess nur gestartet wurde), und faehrt
den Zugangspunkt bei einem toten Server wieder herunter statt ihn offen mit
bekanntem Passwort ohne Server dahinter stehen zu lassen.
- 'off' faengt einen fehlschlagenden Wechsel zurueck ins Heim-WLAN ab statt
unter set -eu mit nmclis Rohausgabe abzubrechen.
- Fehlende Schluessel in der Konfiguration werden nach dem Sourcen explizit
geprueft und mit einem Verweis auf setup_pi.sh gemeldet statt mit
"parameter not set" abzubrechen.
- station.conf.example verwendet einfache statt doppelter Anfuehrungszeichen:
POSIX ". datei" wertet $ und ` auch in doppelten Anfuehrungszeichen aus,
ein Passwort mit "$1" waere sonst lautlos durch ein Shell-Argument ersetzt
worden. systemds EnvironmentFile= fuehrt ohnehin nie eine Shell aus und
entfernt einfache wie doppelte Anfuehrungszeichen gleichermassen.
- ninlight-station.service: BindsTo=NetworkManager.service entfernt (ein
dadurch ausgeloester Stop gilt fuer systemd als sauber und wird von keiner
Restart=-Politik erneut gestartet - ein NetworkManager-Neustart haette den
Server sonst still und dauerhaft mitgerissen); stattdessen nur
After=NetworkManager.service fuer die Startreihenfolge und Restart=always,
damit der Prozess nach einem Absturz (z.B. Bind-Fehler) automatisch
wiederkommt.
- Trailer-Reader im 'status'-Zweig nutzt jetzt errors="replace" wie
trailer_version() in server.py, statt bei nicht-ASCII-Bytes im
Versionsfeld abzustuerzen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Die Station ist bewusst nicht dauerhaft an - 'ninlight-station on'
schaltet Zugangspunkt und Dienst gemeinsam ein, 'off' schaltet wlan0
zurueck ins Heim-WLAN. Der Dienst laeuft als root (Port 80 und die
DHCP-Leases fuer 4c) und bekommt dafuer systemd-Einschraenkungen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>