Compare commits
13 Commits
33482f373e
...
doc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f88a0df2d9 | ||
|
|
ed1ac2364a | ||
| 48cea89a36 | |||
|
|
04b741b28b | ||
| f72287637b | |||
|
|
189bd41ea1 | ||
| a9e9cf15a0 | |||
|
|
45f496590d | ||
| 6f85457ed9 | |||
|
|
61ebf046b8 | ||
|
|
7cbd5cd5be | ||
|
|
2daaadc3b3 | ||
|
|
1473eed0e0 |
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2024 scriptos
|
Copyright (c) 2025 scriptos
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
|||||||
97
README.md
97
README.md
@@ -1,9 +1,102 @@
|
|||||||
# template_repository
|
# 🧩 LiveKit IP Watch
|
||||||
|
|
||||||
|
Ein einfaches Bash-Script, das bei dynamischen Internetanschlüssen (z. B. DSL) automatisch die öffentliche IP-Adresse ermittelt, in die LiveKit-`config.yaml` einträgt und anschließend den Dienst neu startet.
|
||||||
|
Nach dem Neustart prüft das Script per HTTP-Healthcheck, ob LiveKit wieder erreichbar ist.
|
||||||
|
|
||||||
|
Dieses Repository ist **begleitend zu meinem Blogbeitrag**, den du hier lesen kannst:
|
||||||
|
👉 [https://www.cleveradmin.de/blog/2025/10/livekit-dynamische-ip-automatisieren](https://www.cleveradmin.de/blog/2025/10/livekit-dynamische-ip-automatisieren)
|
||||||
|
|
||||||
Wichtig: Link für Lizenz anpassen.
|
---
|
||||||
|
|
||||||
|
## ⚙️ Konfiguration
|
||||||
|
|
||||||
|
Öffne die Scriptdatei `livekit-ip-watch.v1.sh` und passe folgende Variablen an:
|
||||||
|
|
||||||
|
| Variable | Beschreibung |
|
||||||
|
|---------|--------------|
|
||||||
|
| `CONFIG_FILE` | Pfad zur LiveKit-Konfigurationsdatei (`config.yaml`). |
|
||||||
|
| `RUNTIME` | Modus: `"compose"` für Docker Compose oder `"docker"` für Plain Docker. |
|
||||||
|
| `COMPOSE_FILE_PATH` | Vollständiger Pfad zur `docker-compose.yaml`, falls `RUNTIME="compose"`. |
|
||||||
|
| `COMPOSE_SERVICE` | Name des LiveKit-Services in deiner Compose-Datei. |
|
||||||
|
| `CONTAINER_NAME` | Nur relevant, wenn `RUNTIME="docker"` – Name des Containers. |
|
||||||
|
| `ENABLE_IPV6` | `true` für IPv6, `false` für IPv4. |
|
||||||
|
| `WAIT_AFTER_RESTART` | Wartezeit (Sekunden) vor dem Healthcheck. |
|
||||||
|
| `HEALTHCHECK_DOMAIN` | Nur den DNS angeben, z. B. `rtc.matrix.techniverse.net`. Das Script prüft automatisch `/livekit/sfu/`. |
|
||||||
|
| `HEALTHCHECK_TIMEOUT` | Timeout in Sekunden für den HTTP-Check. |
|
||||||
|
| `NTFY_URL` / `NTFY_TOKEN` | Optional für Benachrichtigungen via ntfy. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Voraussetzungen
|
||||||
|
|
||||||
|
- bash
|
||||||
|
- git
|
||||||
|
- yq (optional, aber empfohlen für saubere YAML-Bearbeitung)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Verwendung
|
||||||
|
|
||||||
|
Script herunterladen und starten:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.techniverse.net/scriptos/livekit-ip-watch.git
|
||||||
|
cd livekit-ip-watch
|
||||||
|
chmod +x livekit-ip-watch.v1.sh
|
||||||
|
bash livekit-ip-watch.v1.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Beispielausgabe:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
➜ bash livekit-ip-watch.v1.sh
|
||||||
|
[2025-10-27 19:56:30] INFO: Neue IP erkannt: 152.53.118.246 (alt: none)
|
||||||
|
[2025-10-27 19:56:30] INFO: Backup erstellt: /home/scripts/livekit-ip-watch/backups/config.yaml.20251027195630.bak
|
||||||
|
[2025-10-27 19:56:30] WARN: yq nicht gefunden – Fallback-Editor wird verwendet.
|
||||||
|
[2025-10-27 19:56:30] INFO: Restart des Dienstes...
|
||||||
|
[+] Restarting 1/1
|
||||||
|
✔ Container matrix-element-call-livekit Started 46.8s
|
||||||
|
[2025-10-27 19:57:17] INFO: Warte 20 Sekunden...
|
||||||
|
[2025-10-27 19:57:37] INFO: Healthcheck OK.
|
||||||
|
```
|
||||||
|
|
||||||
|
[](https://asciinema.techniverse.net/a/68)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⏰ Automatische Ausführung (Cronjob)
|
||||||
|
|
||||||
|
Damit die IP regelmäßig geprüft und aktualisiert wird, kannst du das Script z. B. alle 15 Minuten ausführen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
Und füge diese Zeile hinzu:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
*/15 * * * * /home/scripts/livekit-ip-watch/livekit-ip-watch.v1.sh >> /var/log/livekit-ip-watch.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧠 Hinweis
|
||||||
|
|
||||||
|
Das Script erstellt bei jeder Änderung ein Backup der `config.yaml` und führt bei einem Fehler automatisch ein Rollback durch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Autor: Patrick Asmus
|
||||||
|
Repository: https://git.techniverse.net/scriptos/livekit-ip-watch.git
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💬 Support & Community
|
||||||
|
|
||||||
|
Du hast Fragen, brauchst Unterstützung bei der Einrichtung oder möchtest dich einfach mit anderen austauschen, die ähnliche Projekte betreiben? Dann schau gerne in unserer Techniverse Community vorbei:
|
||||||
|
|
||||||
|
👉 **Matrix-Raum:** [#community:techniverse.net](https://matrix.to/#/#community:techniverse.net)
|
||||||
|
Wir freuen uns auf deinen Besuch und helfen dir gerne weiter!
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
|||||||
316
livekit-ip-watch.v1.sh
Normal file
316
livekit-ip-watch.v1.sh
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Beschreibung: Aktualisiert livekit.yaml (rtc.ips.includes) bei IP-Wechsel (IPv4 ODER IPv6),
|
||||||
|
# restarte den LiveKit-Dienst (Docker oder Docker Compose), wartet konfigurierbar und prüft Health.
|
||||||
|
# Mit Backup + Rollback, optionalen ntfy-Notifications, YAML-Update via yq (Fallback eingebaut).
|
||||||
|
# Synapse: https://git.techniverse.net/scriptos/livekit-ip-watch.git
|
||||||
|
# Autor: Patrick Asmus
|
||||||
|
# Web: https://www.cleveradmin.de
|
||||||
|
# Version: 1.2.1
|
||||||
|
# Datum: 27.10.2025
|
||||||
|
# Modifikation: Funktion "detect_compose_cmd" nachträglich committed.
|
||||||
|
#####################################################
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Konfiguration (Variablen)
|
||||||
|
############################
|
||||||
|
|
||||||
|
# Pfad zur LiveKit-Konfiguration
|
||||||
|
CONFIG_FILE="/home/docker-container/matrix-rtc/data/matrix-element-call-livekit/config.yaml"
|
||||||
|
|
||||||
|
# Betriebsmodus: "compose" oder "docker"
|
||||||
|
RUNTIME="compose"
|
||||||
|
|
||||||
|
# Compose: Kompletter Pfad zur Compose-Datei + Service-Name
|
||||||
|
COMPOSE_FILE_PATH="/home/docker-container/matrix-rtc/docker-compose.yaml"
|
||||||
|
COMPOSE_SERVICE="matrix-element-call-livekit"
|
||||||
|
|
||||||
|
# Plain-Docker: Container-Name (nur genutzt, wenn RUNTIME="docker")
|
||||||
|
CONTAINER_NAME="matrix-element-call-livekit"
|
||||||
|
|
||||||
|
# IP-Variante: IPv4 oder IPv6
|
||||||
|
ENABLE_IPV6=false
|
||||||
|
|
||||||
|
# Wartezeit nach Neustart (Sekunden), dann Healthcheck
|
||||||
|
WAIT_AFTER_RESTART=20
|
||||||
|
|
||||||
|
# Healthcheck-Konfiguration: HTTP
|
||||||
|
HEALTHCHECK_DOMAIN="rtc.matrix.techniverse.net"
|
||||||
|
HEALTHCHECK_TIMEOUT=3
|
||||||
|
|
||||||
|
# ntfy (optional)
|
||||||
|
NTFY_URL=""
|
||||||
|
NTFY_TOKEN=""
|
||||||
|
|
||||||
|
############################
|
||||||
|
# Hauptscript
|
||||||
|
############################
|
||||||
|
|
||||||
|
# Skriptverzeichnis, Lock- und State-Dateien
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
LOCKFILE="$SCRIPT_DIR/livekit-ip-watch.lock"
|
||||||
|
STATE_FILE_V4="$SCRIPT_DIR/last_ip_v4"
|
||||||
|
STATE_FILE_V6="$SCRIPT_DIR/last_ip_v6"
|
||||||
|
BACKUP_DIR="$SCRIPT_DIR/backups"
|
||||||
|
mkdir -p "$BACKUP_DIR"
|
||||||
|
|
||||||
|
log() { printf '[%(%Y-%m-%d %H:%M:%S)T] %s\n' -1 "$*" >&2; }
|
||||||
|
die() { log "ERROR: $*"; notify "error" "$*"; exit 1; }
|
||||||
|
warn() { log "WARN: $*"; notify "warn" "$*"; }
|
||||||
|
info() { log "INFO: $*"; }
|
||||||
|
|
||||||
|
# Ntfy
|
||||||
|
notify() {
|
||||||
|
local level="${1:-info}"
|
||||||
|
shift || true
|
||||||
|
local msg="${*:-}"
|
||||||
|
[[ -z "$NTFY_URL" ]] && return 0
|
||||||
|
local hdr=(-H "Title: livekit-ip-watch" -H "Priority: default" -H "Tags: $level")
|
||||||
|
[[ -n "$NTFY_TOKEN" ]] && hdr+=(-H "Authorization: Bearer $NTFY_TOKEN")
|
||||||
|
curl -fsS -X POST "${hdr[@]}" -d "$msg" "$NTFY_URL" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Lock
|
||||||
|
acquire_lock() {
|
||||||
|
if command -v flock >/dev/null 2>&1; then
|
||||||
|
exec 9>"$LOCKFILE"
|
||||||
|
if ! flock -n 9; then
|
||||||
|
info "Läuft bereits (Lock vorhanden): $LOCKFILE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
set -o noclobber
|
||||||
|
if ! : > "$LOCKFILE"; then
|
||||||
|
info "Läuft bereits (Lock vorhanden): $LOCKFILE"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
set +o noclobber
|
||||||
|
fi
|
||||||
|
trap 'release_lock' EXIT
|
||||||
|
}
|
||||||
|
|
||||||
|
release_lock() {
|
||||||
|
rm -f "$LOCKFILE" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
# IP-Ermittlung
|
||||||
|
get_ipv4() {
|
||||||
|
local ip
|
||||||
|
ip="$(dig +short myip.opendns.com @resolver1.opendns.com 2>/dev/null || true)"
|
||||||
|
[[ -z "$ip" ]] && ip="$(curl -fsS https://api.ipify.org 2>/dev/null || true)"
|
||||||
|
[[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] || ip=""
|
||||||
|
echo "$ip"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_ipv6() {
|
||||||
|
local ip
|
||||||
|
ip="$(curl -fsS -6 https://api6.ipify.org 2>/dev/null || true)"
|
||||||
|
if [[ "$ip" =~ : ]]; then
|
||||||
|
echo "$ip"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
have_yq() { command -v yq >/dev/null 2>&1; }
|
||||||
|
|
||||||
|
backup_config() {
|
||||||
|
[[ -r "$CONFIG_FILE" ]] || die "Config nicht lesbar: $CONFIG_FILE"
|
||||||
|
local ts backup
|
||||||
|
ts="$(date +%Y%m%d%H%M%S)"
|
||||||
|
backup="$BACKUP_DIR/$(basename "$CONFIG_FILE").$ts.bak"
|
||||||
|
cp -a "$CONFIG_FILE" "$backup"
|
||||||
|
echo "$backup"
|
||||||
|
}
|
||||||
|
|
||||||
|
is_mikefarah_yq() {
|
||||||
|
yq --version 2>&1 | grep -qi 'mikefarah'
|
||||||
|
}
|
||||||
|
|
||||||
|
update_yaml_with_yq() {
|
||||||
|
local cidr="$1"
|
||||||
|
if is_mikefarah_yq; then
|
||||||
|
CIDR="$cidr" yq eval -i '
|
||||||
|
.rtc = (.rtc // {}) |
|
||||||
|
.rtc.ips = (.rtc.ips // {}) |
|
||||||
|
.rtc.ips.includes = [env(CIDR)]
|
||||||
|
' "$CONFIG_FILE"
|
||||||
|
else
|
||||||
|
yq -y -i --arg CIDR "$cidr" '
|
||||||
|
.rtc = (.rtc // {}) |
|
||||||
|
.rtc.ips = (.rtc.ips // {}) |
|
||||||
|
.rtc.ips.includes = [$CIDR]
|
||||||
|
' "$CONFIG_FILE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
update_yaml_fallback() {
|
||||||
|
local cidr="$1"
|
||||||
|
local tmp
|
||||||
|
tmp="$(mktemp)"
|
||||||
|
awk -v newcidr="$cidr" '
|
||||||
|
function indent_of(s, m,sp) { match(s,/^([ ]*)/,m); sp=length(m[1]); return sp }
|
||||||
|
BEGIN { in_rtc=0; rtc_indent=-1; skipping_ips=0; ips_indent=-1; injected=0 }
|
||||||
|
{
|
||||||
|
line=$0
|
||||||
|
ind=indent_of(line)
|
||||||
|
|
||||||
|
if ($0 ~ /^[[:space:]]*rtc:[[:space:]]*$/) {
|
||||||
|
in_rtc=1; rtc_indent=ind; injected=0
|
||||||
|
print line
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in_rtc) {
|
||||||
|
if (skipping_ips) {
|
||||||
|
if (ind <= ips_indent) {
|
||||||
|
if (!injected) {
|
||||||
|
print " ips:"
|
||||||
|
print " includes:"
|
||||||
|
print " - " newcidr
|
||||||
|
injected=1
|
||||||
|
}
|
||||||
|
skipping_ips=0
|
||||||
|
} else { next }
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($0 ~ /^[[:space:]]*ips:[[:space:]]*$/ && ind > rtc_indent) {
|
||||||
|
skipping_ips=1; ips_indent=ind
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ind <= rtc_indent && $0 ~ /^[[:alnum:]]_+:[[:space:]]*$/) {
|
||||||
|
if (!injected) {
|
||||||
|
print " ips:"
|
||||||
|
print " includes:"
|
||||||
|
print " - " newcidr
|
||||||
|
injected=1
|
||||||
|
}
|
||||||
|
in_rtc=0; rtc_indent=-1
|
||||||
|
print line
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
print line
|
||||||
|
next
|
||||||
|
}
|
||||||
|
|
||||||
|
print line
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
if (in_rtc && !injected) {
|
||||||
|
print " ips:"
|
||||||
|
print " includes:"
|
||||||
|
print " - " newcidr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$CONFIG_FILE" > "$tmp"
|
||||||
|
mv "$tmp" "$CONFIG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_compose_cmd() {
|
||||||
|
if command -v docker-compose >/dev/null 2>&1; then
|
||||||
|
echo "docker-compose"
|
||||||
|
else
|
||||||
|
echo "docker compose"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restart_service() {
|
||||||
|
if [[ "$RUNTIME" == "compose" ]]; then
|
||||||
|
local dir file
|
||||||
|
dir="$(dirname "$COMPOSE_FILE_PATH")"
|
||||||
|
file="$(basename "$COMPOSE_FILE_PATH")"
|
||||||
|
local COMPOSE_CMD
|
||||||
|
COMPOSE_CMD="$(detect_compose_cmd)"
|
||||||
|
( cd "$dir" && "$COMPOSE_CMD" -f "$file" restart "$COMPOSE_SERVICE" )
|
||||||
|
else
|
||||||
|
docker restart "$CONTAINER_NAME"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
healthcheck_http() {
|
||||||
|
local url="https://${HEALTHCHECK_DOMAIN}/livekit/sfu/"
|
||||||
|
curl -fsS --max-time "$HEALTHCHECK_TIMEOUT" "$url" >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
acquire_lock
|
||||||
|
|
||||||
|
[[ -f "$CONFIG_FILE" ]] || die "Config-Datei nicht gefunden: $CONFIG_FILE"
|
||||||
|
if [[ "$RUNTIME" == "compose" ]]; then
|
||||||
|
[[ -f "$COMPOSE_FILE_PATH" ]] || die "Compose-Datei nicht gefunden: $COMPOSE_FILE_PATH"
|
||||||
|
[[ -n "$COMPOSE_SERVICE" ]] || die "COMPOSE_SERVICE ist leer."
|
||||||
|
else
|
||||||
|
[[ -n "$CONTAINER_NAME" ]] || die "CONTAINER_NAME ist leer."
|
||||||
|
fi
|
||||||
|
|
||||||
|
local current_ip cidr state_file
|
||||||
|
if [[ "$ENABLE_IPV6" == true ]]; then
|
||||||
|
current_ip="$(get_ipv6)"
|
||||||
|
[[ -z "$current_ip" ]] && die "Konnte keine öffentliche IPv6 ermitteln."
|
||||||
|
cidr="${current_ip}/128"
|
||||||
|
state_file="$STATE_FILE_V6"
|
||||||
|
else
|
||||||
|
current_ip="$(get_ipv4)"
|
||||||
|
[[ -z "$current_ip" ]] && die "Konnte keine öffentliche IPv4 ermitteln."
|
||||||
|
cidr="${current_ip}/32"
|
||||||
|
state_file="$STATE_FILE_V4"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local last_ip=""
|
||||||
|
[[ -f "$state_file" ]] && last_ip="$(cat "$state_file" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ "$current_ip" == "$last_ip" && -n "$last_ip" ]]; then
|
||||||
|
info "IP unverändert: $current_ip"
|
||||||
|
notify "info" "Keine Änderung. Öffentliche IP bleibt $current_ip."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Neue IP erkannt: $current_ip (alt: ${last_ip:-none})"
|
||||||
|
local backup
|
||||||
|
backup="$(backup_config)"
|
||||||
|
info "Backup erstellt: $backup"
|
||||||
|
|
||||||
|
if have_yq; then
|
||||||
|
info "Aktualisiere YAML mit yq → .rtc.ips.includes = [ \"$cidr\" ]"
|
||||||
|
update_yaml_with_yq "$cidr"
|
||||||
|
else
|
||||||
|
warn "yq nicht gefunden – Fallback-Editor wird verwendet."
|
||||||
|
update_yaml_fallback "$cidr"
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "Restart des Dienstes..."
|
||||||
|
restart_service
|
||||||
|
|
||||||
|
info "Warte $WAIT_AFTER_RESTART Sekunden..."
|
||||||
|
sleep "$WAIT_AFTER_RESTART"
|
||||||
|
|
||||||
|
if healthcheck_http; then
|
||||||
|
info "Healthcheck OK."
|
||||||
|
echo "$current_ip" > "$state_file"
|
||||||
|
notify "info" "IP geändert auf $current_ip, Dienst gesund."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
warn "Healthcheck fehlgeschlagen – führe Rollback durch."
|
||||||
|
cp -a "$backup" "$CONFIG_FILE"
|
||||||
|
info "Rollback-Konfiguration wiederhergestellt: $backup"
|
||||||
|
|
||||||
|
info "Restart nach Rollback..."
|
||||||
|
restart_service
|
||||||
|
|
||||||
|
info "Warte $WAIT_AFTER_RESTART Sekunden (Rollback)..."
|
||||||
|
sleep "$WAIT_AFTER_RESTART"
|
||||||
|
|
||||||
|
if healthcheck_http; then
|
||||||
|
warn "Rollback erfolgreich. System läuft wieder mit alter Konfiguration."
|
||||||
|
notify "warn" "Rollback erfolgreich. Bitte prüfen. Alte IP bleibt ${last_ip:-unbekannt}."
|
||||||
|
exit 10
|
||||||
|
else
|
||||||
|
die "Rollback fehlgeschlagen. Manuelles Eingreifen erforderlich."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user