#!/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 if [ -r "$CONF" ]; then # shellcheck disable=SC1090 . "$CONF" elif [ -e "$CONF" ]; then # $CONF ist 0600 root:root, damit das Passwort nicht fuer jeden lesbar # ist. Der dokumentierte Aufruf "ssh ninlight-station on" laeuft # aber als normaler Benutzer - "sudo cat" liest die Datei ein, ohne ihre # Rechte aufzuweichen. eval "$(sudo cat "$CONF")" else echo "Fehler: $CONF fehlt - zuerst tools/station/setup_pi.sh ausfuehren" >&2 exit 1 fi 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