feat(station): Unterbefehle fuer Migrationsmodus und Freigabe
ninlight-station bekommt recovery/migrate/arm/listen als Unterbefehle statt nur on/off/status; "on" bleibt Kurzform fuer "recovery on". Eine gemeinsame ap_switch()-Funktion faehrt vor jedem Wechsel erst jedes andere Stationsprofil herunter und prueft per nmcli nach, dass auf wlan0 wirklich nur eines aktiv ist - die beiden Zugangspunkte (Rettung/Migration) schliessen sich damit gegenseitig aus. Der Modus wandert per /run/ninlight-station/mode an den (ohnehin neu gestarteten) Dienst statt ueber die Konfigurationsdatei; arm/listen schreiben die Steuerdatei aus Task 3 mit demselben atomaren 0644-Muster wie server.py selbst. station.conf.example und die Pflichtschluesselpruefung bekommen die neuen Migrations- und Anzeige-Schluessel dazu.
This commit is contained in:
+285
-56
@@ -1,14 +1,27 @@
|
||||
#!/bin/sh
|
||||
# Schaltet die ninlight-Recovery-Station an und aus.
|
||||
# Schaltet die ninlight-Station (Rettungs- oder Migrationsmodus) 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).
|
||||
# jedem Recovery-Versuch). Der Migrationsmodus (Zugangspunkt "qconnex-firmware",
|
||||
# oeffentlich bekanntes Passwort aus der alten Klyqa-Firmware) verschiebt diese
|
||||
# Vertrauensgrenze noch weiter nach aussen - siehe README.md.
|
||||
#
|
||||
# Die beiden Zugangspunkte schliessen sich gegenseitig aus: ap_switch() faehrt
|
||||
# immer erst jedes andere Stationsprofil herunter, bevor es das gewuenschte
|
||||
# hochfaehrt, und prueft danach per nmcli nach, dass wirklich nur eines aktiv
|
||||
# ist.
|
||||
set -eu
|
||||
|
||||
CONF=/etc/ninlight-station.conf
|
||||
# Ueberschreibbar fuer Tests (dasselbe Prinzip wie NINLIGHT_STATION_ROOT/_BIN/_UNIT
|
||||
# in deploy.sh): so kann tests/test_cli.sh gegen ein Sandbox-Verzeichnis statt
|
||||
# gegen echte Systempfade laufen.
|
||||
CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf}
|
||||
RUN_DIR=${NINLIGHT_RUN_DIR:-/run/ninlight-station}
|
||||
STATION_LIB=${NINLIGHT_STATION_LIB:-/opt/ninlight-station}
|
||||
|
||||
if [ -r "$CONF" ]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$CONF"
|
||||
@@ -23,7 +36,8 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for _key in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION; do
|
||||
for _key in SSID PSK IMAGE_DIR HOME_CONNECTION AP_CONNECTION \
|
||||
MIGRATE_AP_CONNECTION MIGRATE_SSID MIGRATE_PSK EPD_REVISION; do
|
||||
eval "test -n \"\${$_key-}\"" || {
|
||||
echo "Fehler: $CONF: Schluessel $_key fehlt oder ist leer - tools/station/setup_pi.sh ausfuehren" >&2
|
||||
exit 1
|
||||
@@ -32,8 +46,9 @@ 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
|
||||
# $1 = NetworkManager-Profilname, der existieren muss.
|
||||
nmcli -t -f NAME con show | grep -qx "$1" || {
|
||||
echo "Fehler: NetworkManager-Profil '$1' fehlt - tools/station/setup_pi.sh ausfuehren" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
@@ -42,51 +57,142 @@ 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
|
||||
migrate_images() {
|
||||
find "$IMAGE_DIR/migrate" -maxdepth 1 -name '*.img' 2>/dev/null | sort
|
||||
}
|
||||
|
||||
# Faehrt erst jedes andere Stationsprofil herunter, dann (falls $1 nicht leer ist)
|
||||
# das gewuenschte hoch, und prueft danach per nmcli nach, dass wirklich genau
|
||||
# dieses Profil auf wlan0 aktiv ist. Weicht es ab, geht alles wieder herunter und
|
||||
# die Funktion bricht mit Fehler ab - das ist die Absicherung fuer Fall 15 der
|
||||
# Testliste: zwei gleichzeitig aktive Stationsprofile auf demselben Interface
|
||||
# duerfen nie vorkommen.
|
||||
#
|
||||
# $1 leer bedeutet "nur alles herunterfahren" (Grundlage fuer "off").
|
||||
ap_switch() {
|
||||
_target=${1:-}
|
||||
for _p in "$AP_CONNECTION" "$MIGRATE_AP_CONNECTION"; do
|
||||
if [ -n "$_target" ] && [ "$_p" = "$_target" ]; then
|
||||
continue
|
||||
fi
|
||||
sudo nmcli con down "$_p" >/dev/null 2>&1 || true
|
||||
done
|
||||
[ -n "$_target" ] || return 0
|
||||
sudo nmcli con up "$_target" >/dev/null
|
||||
_active=$(nmcli -t -f GENERAL.CONNECTION device show wlan0 2>/dev/null | cut -d: -f2)
|
||||
if [ "$_active" != "$_target" ]; then
|
||||
sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true
|
||||
sudo nmcli con down "$MIGRATE_AP_CONNECTION" >/dev/null 2>&1 || true
|
||||
echo "Fehler: wlan0 zeigt nach dem Umschalten auf '$_target' unerwartet '$_active' - beide Stationsprofile wieder heruntergefahren" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Schreibt $RUN_DIR/mode atomar mit 0644 (Temp-Datei im selben Verzeichnis, dann
|
||||
# umbenennen) - server.py liest sie bei jedem Dienststart per --mode-file.
|
||||
write_mode() {
|
||||
sudo mkdir -p "$RUN_DIR"
|
||||
_tmp="$RUN_DIR/.mode-$$"
|
||||
printf '%s' "$1" | sudo install -m 0644 /dev/stdin "$_tmp" || return 1
|
||||
sudo mv -f "$_tmp" "$RUN_DIR/mode"
|
||||
}
|
||||
|
||||
# Schreibt $RUN_DIR/control.json atomar mit 0644 - dasselbe Muster (Temp-Datei per
|
||||
# "install" anlegen, dann umbenennen) und dieselben Rechte wie server.py's eigenes
|
||||
# write_control(), sonst kann der Dienst die Datei anschliessend nicht mehr
|
||||
# ueberschreiben oder das CLI sie nicht mehr lesen.
|
||||
#
|
||||
# $1 = phase (auto|listen|armed), $2 = armed_target oder leer (-> null im JSON).
|
||||
write_control() {
|
||||
sudo mkdir -p "$RUN_DIR"
|
||||
_tmp="$RUN_DIR/.control-$$.json"
|
||||
if [ -n "${2:-}" ]; then
|
||||
_json=$(printf '{"phase": "%s", "armed_target": "%s"}' "$1" "$2")
|
||||
else
|
||||
_json=$(printf '{"phase": "%s", "armed_target": null}' "$1")
|
||||
fi
|
||||
printf '%s' "$_json" | sudo install -m 0644 /dev/stdin "$_tmp" || return 1
|
||||
sudo mv -f "$_tmp" "$RUN_DIR/control.json"
|
||||
}
|
||||
|
||||
# Startet (neu) und wartet kurz auf Lebenszeichen. $1 = das gerade hochgefahrene
|
||||
# AP-Profil, das bei einem Fehlschlag wieder heruntergefahren wird.
|
||||
start_and_verify() {
|
||||
# Eine fruehere Absturzserie darf den naechsten ehrlichen Versuch nicht
|
||||
# blockieren (systemd verweigert "start" sonst sofort mit "start-limit-hit").
|
||||
# blockieren (systemd verweigert "start"/"restart" 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
|
||||
if ! sudo systemctl restart ninlight-station; then
|
||||
# Der Neustart 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
|
||||
sudo nmcli con down "$1" >/dev/null 2>&1 || true
|
||||
echo "Fehler: ninlight-station liess sich nicht (neu) starten - Zugangspunkt wieder abgeschaltet. Details: journalctl -u ninlight-station" >&2
|
||||
exit 1
|
||||
fi
|
||||
# "systemctl start" meldet bei Type=simple schon Erfolg, sobald der Prozess
|
||||
# "systemctl restart" 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)"
|
||||
# Mit dem oeffentlichen Default-Passwort (im Repo dokumentiert) kann jeder in
|
||||
# Funkreichweite, der es kennt, jeder Lampe beliebige Firmware unterschieben - das ist
|
||||
# die eine Haelfte der Betriebsregeln aus README.md/§9, die andere (Station wieder
|
||||
# ausschalten) stand hier schon.
|
||||
if [ "$PSK" = "ninlight-recovery" ]; then
|
||||
echo "Warnung: PSK ist noch der oeffentliche Default 'ninlight-recovery' - eigenes Passwort in /etc/ninlight-station.conf setzen." >&2
|
||||
fi
|
||||
echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist."
|
||||
else
|
||||
if ! systemctl is-active --quiet ninlight-station; then
|
||||
# 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
|
||||
sudo nmcli con down "$1" >/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)
|
||||
}
|
||||
|
||||
recovery_on() {
|
||||
need_profile "$AP_CONNECTION"
|
||||
n=$(images | wc -l | tr -d ' ')
|
||||
[ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR enthaelt kein Image - die Station antwortet auf alles mit 404" >&2
|
||||
ap_switch "$AP_CONNECTION"
|
||||
write_mode "recovery"
|
||||
start_and_verify "$AP_CONNECTION"
|
||||
echo "Station an: SSID $SSID auf 192.168.8.1, $n Image(s)"
|
||||
# Mit dem oeffentlichen Default-Passwort (im Repo dokumentiert) kann jeder in
|
||||
# Funkreichweite, der es kennt, jeder Lampe beliebige Firmware unterschieben - das ist
|
||||
# die eine Haelfte der Betriebsregeln aus README.md/§9, die andere (Station wieder
|
||||
# ausschalten) steht unten.
|
||||
if [ "$PSK" = "ninlight-recovery" ]; then
|
||||
echo "Warnung: PSK ist noch der oeffentliche Default 'ninlight-recovery' - eigenes Passwort in /etc/ninlight-station.conf setzen." >&2
|
||||
fi
|
||||
echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist."
|
||||
}
|
||||
|
||||
migrate_on() {
|
||||
# $1 = "--listen" oder leer.
|
||||
_phase=auto
|
||||
if [ "${1:-}" = "--listen" ]; then
|
||||
_phase=listen
|
||||
elif [ -n "${1:-}" ]; then
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
need_profile "$MIGRATE_AP_CONNECTION"
|
||||
n=$(migrate_images | wc -l | tr -d ' ')
|
||||
[ "$n" -gt 0 ] || echo "Warnung: $IMAGE_DIR/migrate enthaelt keine Migrationsimages - die Station antwortet auf alles mit 404" >&2
|
||||
# Faehrt ninlight-recovery (falls es laeuft) herunter, BEVOR qconnex-firmware
|
||||
# hochkommt, und prueft danach nach, dass wirklich nur eines aktiv ist.
|
||||
ap_switch "$MIGRATE_AP_CONNECTION"
|
||||
write_mode "migration"
|
||||
write_control "$_phase" "" || {
|
||||
echo "Fehler: $RUN_DIR/control.json konnte nicht geschrieben werden" >&2
|
||||
exit 1
|
||||
}
|
||||
start_and_verify "$MIGRATE_AP_CONNECTION"
|
||||
echo "Migration an: SSID $MIGRATE_SSID auf 192.168.8.1, Phase $_phase, $n Migrationsimage(s)"
|
||||
echo "Achtung: das Passwort dieses Zugangspunkts ist aus der alten Firmware oeffentlich bekannt - siehe README.md." >&2
|
||||
echo "Nicht vergessen: 'ninlight-station off', wenn du fertig bist."
|
||||
}
|
||||
|
||||
full_off() {
|
||||
sudo systemctl stop ninlight-station 2>/dev/null || true
|
||||
sudo nmcli con down "$AP_CONNECTION" >/dev/null 2>&1 || true
|
||||
ap_switch ""
|
||||
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)"
|
||||
@@ -96,14 +202,109 @@ off)
|
||||
else
|
||||
echo "Station aus. Profil '$HOME_CONNECTION' fehlt, wlan0 bleibt ungenutzt - der Zugang ueber Kabel ist davon nicht betroffen." >&2
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
}
|
||||
|
||||
listen_cmd() {
|
||||
write_control "listen" "" || {
|
||||
echo "Fehler: $RUN_DIR/control.json konnte nicht geschrieben werden" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "Phase: listen"
|
||||
}
|
||||
|
||||
arm_cmd() {
|
||||
_target=${1:-}
|
||||
if [ -z "$_target" ]; then
|
||||
_status_file="$RUN_DIR/status.json"
|
||||
if ! _target=$(python3 - "$_status_file" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
try:
|
||||
with open(sys.argv[1]) as f:
|
||||
d = json.load(f)
|
||||
except (OSError, ValueError):
|
||||
sys.exit(1)
|
||||
t = (d.get("last_seen") or {}).get("target")
|
||||
if not t:
|
||||
sys.exit(1)
|
||||
print(t)
|
||||
PY
|
||||
); then
|
||||
echo "Fehler: kein zuletzt erkanntes Modell (last_seen) in $_status_file - Ziel angeben: ninlight-station arm <target>" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if ! python3 - "$STATION_LIB" "$_target" <<'PY'
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, sys.argv[1])
|
||||
import products
|
||||
|
||||
target = sys.argv[2]
|
||||
sys.exit(0 if any(p.target == target for p in products.PRODUCTS.values()) else 1)
|
||||
PY
|
||||
then
|
||||
echo "Fehler: unbekanntes Ziel '$_target' - gueltige Ziele stehen in products.py" >&2
|
||||
exit 1
|
||||
fi
|
||||
write_control "armed" "$_target" || {
|
||||
echo "Fehler: $RUN_DIR/control.json konnte nicht geschrieben werden" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "Freigegeben: $_target"
|
||||
}
|
||||
|
||||
status_cmd() {
|
||||
# "systemctl is-active" schreibt den Zustand ("inactive", "failed", ...) auf stdout und
|
||||
# liefert dabei einen Rueckgabewert ungleich 0, sobald der Dienst nicht "active" ist -
|
||||
# ein "|| echo inaktiv" wuerde also zusaetzlich zur ersten Zeile ausgeloest und beide
|
||||
# landen in der Ausgabe. Der Zustand selbst ist die Auskunft; "status" ist damit
|
||||
# erfolgreich, sobald es ihn ermitteln konnte, unabhaengig vom Dienstzustand - deshalb
|
||||
# am Ende explizit exit 0.
|
||||
# erfolgreich, sobald es ihn ermitteln konnte, unabhaengig vom Dienstzustand.
|
||||
_status_file="$RUN_DIR/status.json"
|
||||
# "existiert nicht" (Station lief noch nie) und "existiert, ist aber nicht lesbar"
|
||||
# sind zwei verschiedene Zustaende und duerfen nicht beide stillschweigend im
|
||||
# "-r"-Test verschwinden: sonst faellt ein Rechte-Fehler auf status.json (siehe
|
||||
# server.py, schreibt als root) nie auf, weil die Ausgabe genauso aussieht wie
|
||||
# "noch nie ausgeliefert". Das gilt auch fuer Modus/Phase/Erkannt unten.
|
||||
if [ -r "$_status_file" ]; then
|
||||
_info=$(python3 - "$_status_file" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
try:
|
||||
with open(sys.argv[1]) as f:
|
||||
d = json.load(f)
|
||||
except (OSError, ValueError):
|
||||
d = {}
|
||||
last_seen = d.get("last_seen")
|
||||
if last_seen:
|
||||
seen = "{} ({})".format(
|
||||
last_seen.get("product_name") or last_seen.get("target", "?"),
|
||||
last_seen.get("lamp_version", "?"),
|
||||
)
|
||||
else:
|
||||
seen = "-"
|
||||
print(d.get("mode", "-"))
|
||||
print(d.get("phase", "-"))
|
||||
print(seen)
|
||||
PY
|
||||
)
|
||||
_modus=$(printf '%s\n' "$_info" | sed -n '1p')
|
||||
_phase=$(printf '%s\n' "$_info" | sed -n '2p')
|
||||
_erkannt=$(printf '%s\n' "$_info" | sed -n '3p')
|
||||
elif [ -e "$_status_file" ]; then
|
||||
_modus="unbekannt (Datei nicht lesbar)"
|
||||
_phase="unbekannt (Datei nicht lesbar)"
|
||||
_erkannt="unbekannt (Datei nicht lesbar)"
|
||||
else
|
||||
_modus="-"
|
||||
_phase="-"
|
||||
_erkannt="-"
|
||||
fi
|
||||
printf 'Modus : %s\n' "$_modus"
|
||||
printf 'Phase : %s\n' "$_phase"
|
||||
printf 'Erkannt: %s\n' "$_erkannt"
|
||||
printf 'Dienst : %s\n' "$(systemctl is-active ninlight-station 2>/dev/null || true)"
|
||||
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}')"
|
||||
@@ -117,26 +318,54 @@ print(t[21:37].rstrip(b"\x00").decode("ascii", "replace") if t[:4] == b"NINL" el
|
||||
PY
|
||||
)"
|
||||
done
|
||||
# "status" soll mit 0 enden, sobald es seine Auskunft geben konnte, auch wenn noch nie
|
||||
# ausgeliefert wurde - ohne das "|| true" waere der Exit-Code des Skripts hier der des
|
||||
# fehlgeschlagenen Tests (1), weil das die letzte Anweisung im Zweig ist.
|
||||
#
|
||||
# "existiert nicht" (Station lief noch nie) und "existiert, ist aber nicht lesbar"
|
||||
# sind zwei verschiedene Zustaende und duerfen nicht beide stillschweigend im
|
||||
# "-r"-Test verschwinden: sonst faellt ein Rechte-Fehler auf status.json (siehe
|
||||
# server.py, schreibt als root) nie auf, weil die Ausgabe genauso aussieht wie
|
||||
# "noch nie ausgeliefert".
|
||||
STATUS_FILE=/run/ninlight-station/status.json
|
||||
if [ -r "$STATUS_FILE" ]; then
|
||||
if [ -r "$_status_file" ]; then
|
||||
echo "Zuletzt ausgeliefert:"
|
||||
sed 's/^/ /' "$STATUS_FILE"
|
||||
elif [ -e "$STATUS_FILE" ]; then
|
||||
echo "Fehler: $STATUS_FILE existiert, ist aber nicht lesbar (Rechte pruefen)" >&2
|
||||
sed 's/^/ /' "$_status_file"
|
||||
elif [ -e "$_status_file" ]; then
|
||||
echo "Fehler: $_status_file existiert, ist aber nicht lesbar (Rechte pruefen)" >&2
|
||||
fi
|
||||
# "status" soll mit 0 enden, sobald es seine Auskunft geben konnte, auch wenn noch nie
|
||||
# ausgeliefert wurde - ohne das explizite exit 0 waere der Exit-Code des Skripts hier
|
||||
# der des fehlgeschlagenen "-r"-Tests, weil das die letzte Anweisung im Zweig ist.
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Aufruf: ninlight-station on|off|status" >&2
|
||||
exit 2
|
||||
;;
|
||||
}
|
||||
|
||||
usage() {
|
||||
echo "Aufruf: ninlight-station on|recovery {on|off}|migrate {on [--listen]|off}|arm [<target>]|listen|off|status" >&2
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
on)
|
||||
recovery_on
|
||||
;;
|
||||
recovery)
|
||||
case "${2:-}" in
|
||||
on) recovery_on ;;
|
||||
off) full_off ;;
|
||||
*) usage; exit 2 ;;
|
||||
esac
|
||||
;;
|
||||
migrate)
|
||||
case "${2:-}" in
|
||||
on) migrate_on "${3:-}" ;;
|
||||
off) full_off ;;
|
||||
*) usage; exit 2 ;;
|
||||
esac
|
||||
;;
|
||||
arm)
|
||||
arm_cmd "${2:-}"
|
||||
;;
|
||||
listen)
|
||||
listen_cmd
|
||||
;;
|
||||
off)
|
||||
full_off
|
||||
;;
|
||||
status)
|
||||
status_cmd
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
Reference in New Issue
Block a user