Files
ninlight-w600/tools/station/ninlight-station
T
Michael SauerandClaude Sonnet 5 cb0a4b035d fix: Startaufruf absichern und Neustartschleife der Station deckeln
- '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>
2026-09-12 12:21:26 +02:00

104 lines
4.6 KiB
Bash
Executable File

#!/bin/sh
# Schaltet die ninlight-Recovery-Station an und aus.
#
# Die Station ist absichtlich nicht dauerhaft an: Solange sie laeuft, kann jeder
# in Funkreichweite mit dem Passwort einer Lampe beim naechsten Boot beliebige
# Firmware unterschieben, und der Werksreset per fuenf Stromzyklen funktioniert
# bei keiner Lampe in Reichweite mehr (die Firmware loescht den Zaehler vor
# jedem Recovery-Versuch).
set -eu
CONF=/etc/ninlight-station.conf
[ -r "$CONF" ] || { echo "Fehler: $CONF fehlt - zuerst tools/station/setup_pi.sh ausfuehren" >&2; exit 1; }
# shellcheck disable=SC1090
. "$CONF"
for _key in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION; do
eval "test -n \"\${$_key-}\"" || {
echo "Fehler: $CONF: Schluessel $_key fehlt oder ist leer - tools/station/setup_pi.sh ausfuehren" >&2
exit 1
}
done
unset _key
need_profile() {
nmcli -t -f NAME con show | grep -qx "$AP_CONNECTION" || {
echo "Fehler: NetworkManager-Profil '$AP_CONNECTION' fehlt - tools/station/setup_pi.sh ausfuehren" >&2
exit 1
}
}
images() {
find "$IMAGE_DIR" -maxdepth 1 -name 'ninlight_*_ota.img' 2>/dev/null | sort
}
case "${1:-}" in
on)
need_profile
n=$(images | wc -l | tr -d ' ')
[ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2
sudo nmcli con up "$AP_CONNECTION" >/dev/null
# Eine fruehere Absturzserie darf den naechsten ehrlichen Versuch nicht
# blockieren (systemd verweigert "start" sonst sofort mit "start-limit-hit").
sudo systemctl reset-failed ninlight-station 2>/dev/null || true
if ! sudo systemctl start ninlight-station; then
# Der Start-Aufruf selbst ist gescheitert (z.B. start-limit-hit) - unter
# set -eu wuerde das Skript hier sonst abbrechen, bevor die
# Lebendigkeitspruefung unten je erreicht wird, und der eben
# hochgefahrene Zugangspunkt bliebe offen ohne Server dahinter stehen.
sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true
echo "Fehler: ninlight-station liess sich nicht starten - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2
exit 1
fi
# "systemctl start" meldet bei Type=simple schon Erfolg, sobald der Prozess
# gestartet wurde - nicht, sobald er ueberlebt hat. Kurz warten und wirklich
# nachsehen, sonst bleibt ein offener AP ohne Server dahinter stehen.
sleep 1
if systemctl is-active --quiet ninlight-station; then
echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)"
echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist."
else
# Mit Restart=always laeuft ein abgestuerzter Dienst sonst im Hintergrund
# weiter in seiner Neustartschleife - erst stoppen, dann den AP runter,
# sonst suggeriert die Meldung unten einen saubereren Zustand als er ist.
sudo systemctl stop ninlight-station 2>/dev/null || true
sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true
echo "Fehler: ninlight-station ist nach dem Start nicht aktiv - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2
exit 1
fi
;;
off)
sudo systemctl stop ninlight-station 2>/dev/null || true
sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true
if nmcli -t -f NAME con show | grep -qx "$HOME_CONNECTION"; then
if sudo nmcli con up "$HOME_CONNECTION" >/dev/null; then
echo "Station aus, wlan0 zurueck im Heim-WLAN ($HOME_CONNECTION)"
else
echo "Station aus. Zurueckschalten zu '$HOME_CONNECTION' fehlgeschlagen - wlan0-Status pruefen." >&2
fi
else
echo "Station aus. Profil '$HOME_CONNECTION' fehlt, wlan0 bleibt ungenutzt - der Zugang ueber Kabel ist davon nicht betroffen." >&2
fi
;;
status)
printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || echo inaktiv)"
printf 'wlan0 : %s\n' "$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2)"
printf 'Adresse: %s\n' "$(ip -4 -br addr show wlan0 2>/dev/null | awk '{print $3}')"
echo "Images :"
images | while read -r f; do
printf ' %-34s %s\n' "$(basename "$f")" \
"$(python3 - "$f" <<'PY'
import pathlib, sys
t = pathlib.Path(sys.argv[1]).read_bytes()[-512:]
print(t[21:37].rstrip(b"\x00").decode("ascii", "replace") if t[:4] == b"NINL" else "kein Trailer")
PY
)"
done
[ -r /run/ninlight-station/status.json ] && { echo "Zuletzt ausgeliefert:"; sed 's/^/ /' /run/ninlight-station/status.json; }
;;
*)
echo "Aufruf: ninlight-station on|off|status" >&2
exit 2
;;
esac