Files
nchsdhg-agathe-backup/scripts/agathe_backup.sh
2026-03-11 08:28:29 +01:00

297 lines
7.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Tomedo-only Backup:
# WireGuard hoch, CIFS mounten (inkl. Stale-Handle-Fix),
# danach nur Tomedo Snapshot + Tomedo Files per rsync.
# Zusätzlich: Pushover-Benachrichtigung bei Erfolg/Fehler.
# Wenn wg0 schon aktiv ist, wird es nicht erneut gestartet und beim Cleanup
# nur dann beendet, wenn dieses Skript es selbst hochgebracht hat.
# Schutz gegen parallele Ausführung per flock.
set -Eeuo pipefail
# -----------------------------
# Konfiguration
# -----------------------------
WG_IF="wg0"
VPN_TEST_IP="10.202.101.10"
WG_WAS_STARTED_BY_SCRIPT=0
LOCK_FILE="/var/lock/agathe_backup.lock"
CIFS_SHARE="//10.202.101.10/nchsdhg"
CIFS_MOUNTPOINT="/mnt/nchsdhg_agathe"
CIFS_USER="nchsdhg"
CIFS_PASS="mugkeN-zexdab-9gyfky"
CIFS_CRED_FILE="" # z.B. "/etc/samba/cred_nchsdhg"
# -----------------------------
# Pushover
# -----------------------------
PUSHOVER_USER_TOKEN="uFBKJ1LmL3eMUbHZs2ktLjSH8RyJ2Z"
PUSHOVER_API_TOKEN="a3xzevfk8vwpbj6wp6duzbwy43pcmx"
PUSHOVER_TITLE="nchsdhg"
# -----------------------------
# Tomedo Backup
# -----------------------------
TOMEDO_SRC_ROOT="/mnt/TomedoBackup"
TOMEDO_LAST_FILE="${TOMEDO_SRC_ROOT}/lastFilesBackup"
TOMEDO_DEST_ROOT="${CIFS_MOUNTPOINT}/TomedoBackup"
TOMEDO_MACOS_EXCLUDES="${TOMEDO_DEST_ROOT}/macos.excludes"
TOMEDO_FILES_EXCLUDES="${TOMEDO_DEST_ROOT}/files.excludes"
# -----------------------------
# Hilfsfunktionen
# -----------------------------
log() { echo "[$(date +'%F %T')] $*"; }
send_pushover() {
local message="$1"
local priority="${2:-0}"
if command -v curl >/dev/null 2>&1; then
curl -s \
--form-string "token=${PUSHOVER_API_TOKEN}" \
--form-string "user=${PUSHOVER_USER_TOKEN}" \
--form-string "title=${PUSHOVER_TITLE}" \
--form-string "message=${message}" \
--form-string "priority=${priority}" \
https://api.pushover.net/1/messages.json >/dev/null || true
else
log "WARNUNG: curl fehlt, Pushover konnte nicht gesendet werden."
fi
}
need_root() {
if [[ "${EUID}" -ne 0 ]]; then
echo "Bitte als root ausführen (z.B. sudo $0)."
exit 1
fi
}
acquire_lock() {
exec 200>"$LOCK_FILE"
if ! flock -n 200; then
log "Eine andere Instanz läuft bereits. Abbruch."
send_pushover "Backup wurde nicht gestartet, weil bereits eine Instanz läuft."
exit 0
fi
echo "$$" 1>&200 || true
log "Lock gesetzt: $LOCK_FILE"
}
cleanup() {
set +e
log "== Cleanup =="
if mountpoint -q "$CIFS_MOUNTPOINT"; then
log "Unmount: $CIFS_MOUNTPOINT"
umount "$CIFS_MOUNTPOINT" 2>/dev/null || umount -l "$CIFS_MOUNTPOINT" || true
else
log "Mountpoint nicht gemountet: $CIFS_MOUNTPOINT"
fi
if [[ "$WG_WAS_STARTED_BY_SCRIPT" -eq 1 ]]; then
if wg show "$WG_IF" &>/dev/null; then
log "WireGuard down: $WG_IF"
wg-quick down "$WG_IF" >/dev/null 2>&1 || true
else
log "WireGuard war bereits nicht mehr aktiv: $WG_IF"
fi
else
log "WireGuard bleibt aktiv (nicht von diesem Skript gestartet)."
fi
}
on_error() {
local exit_code=$?
local line_no="${1:-unknown}"
log "FEHLER in Zeile ${line_no}, Exit-Code ${exit_code}"
send_pushover "Backup FEHLER auf $(hostname): Zeile ${line_no}, Exit-Code ${exit_code}" 1
trap - EXIT
cleanup
exit "$exit_code"
}
on_exit() {
cleanup
}
trap 'on_error $LINENO' ERR
trap on_exit EXIT
check_deps() {
local deps=(wg-quick wg mount rsync ping mountpoint umount date sleep id mkdir rm dirname curl hostname flock cat)
for d in "${deps[@]}"; do
command -v "$d" >/dev/null 2>&1 || { echo "Fehlt: $d"; exit 1; }
done
}
bringup_wg() {
log "== WireGuard prüfen: $WG_IF =="
if wg show "$WG_IF" &>/dev/null; then
log "WireGuard ist bereits aktiv: $WG_IF"
else
log "== WireGuard up: $WG_IF =="
wg-quick up "$WG_IF"
WG_WAS_STARTED_BY_SCRIPT=1
fi
log "== Prüfe VPN/Konnektivität zu $VPN_TEST_IP =="
for i in {1..10}; do
if ping -c1 -W1 "$VPN_TEST_IP" >/dev/null 2>&1; then
log "OK: $VPN_TEST_IP erreichbar."
return 0
fi
sleep 1
done
log "FEHLER: $VPN_TEST_IP nicht erreichbar."
exit 1
}
_mount_cifs_with_opts() {
local opts="$1"
if [[ -n "$CIFS_CRED_FILE" ]]; then
log "Mount mit credentials file: $CIFS_CRED_FILE"
mount -t cifs "$CIFS_SHARE" "$CIFS_MOUNTPOINT" -o "credentials=${CIFS_CRED_FILE},${opts}"
else
log "Mount mit username/pass (klartext) + opts: ${opts}"
mount -t cifs "$CIFS_SHARE" "$CIFS_MOUNTPOINT" -o "username=${CIFS_USER},password=${CIFS_PASS},${opts}"
fi
}
mount_cifs() {
log "== CIFS mount: $CIFS_SHARE -> $CIFS_MOUNTPOINT =="
if mountpoint -q "$CIFS_MOUNTPOINT"; then
log "Mountpoint ist gemountet -> versuche umount"
umount "$CIFS_MOUNTPOINT" 2>/dev/null || umount -l "$CIFS_MOUNTPOINT" || true
fi
if ! mkdir -p "$CIFS_MOUNTPOINT" 2>/dev/null; then
log "WARNUNG: Mountpoint kaputt (stale handle). Versuche umount -l und neu anlegen."
umount -l "$CIFS_MOUNTPOINT" 2>/dev/null || true
mkdir -p "$(dirname "$CIFS_MOUNTPOINT")"
rm -rf "$CIFS_MOUNTPOINT" 2>/dev/null || true
mkdir -p "$CIFS_MOUNTPOINT"
fi
local opts_base="uid=$(id -u),gid=$(id -g),iocharset=utf8,vers=3.0"
local opts_robust="${opts_base},cache=strict,actimeo=30,echo_interval=30"
set +e
_mount_cifs_with_opts "$opts_robust"
local rc=$?
set -e
if [[ $rc -eq 0 ]]; then
log "OK gemountet (robust)."
return 0
fi
log "WARNUNG: CIFS mount (robust) fehlgeschlagen (rc=$rc). Fallback auf Basis-Optionen."
set +e
_mount_cifs_with_opts "$opts_base"
rc=$?
set -e
if [[ $rc -eq 0 ]]; then
log "OK gemountet (base)."
return 0
fi
log "FEHLER: CIFS mount fehlgeschlagen (rc=$rc)."
exit 1
}
ensure_cifs() {
if ! mountpoint -q "$CIFS_MOUNTPOINT"; then
log "WARNUNG: CIFS nicht gemountet -> remount"
mount_cifs
fi
if ! mountpoint -q "$CIFS_MOUNTPOINT"; then
log "FEHLER: CIFS Remount fehlgeschlagen."
exit 1
fi
}
rsync_tomedo_backup() {
ensure_cifs
log "== Tomedo Backup starten =="
if [[ ! -f "$TOMEDO_LAST_FILE" ]]; then
log "FEHLER: Datei fehlt: $TOMEDO_LAST_FILE"
exit 1
fi
local last_backup
last_backup="$(cat "$TOMEDO_LAST_FILE")"
if [[ -z "$last_backup" ]]; then
log "FEHLER: $TOMEDO_LAST_FILE ist leer"
exit 1
fi
local src_snapshot="${TOMEDO_SRC_ROOT}/${last_backup}/"
local dst_snapshot="${TOMEDO_DEST_ROOT}/${last_backup}"
local src_files="${TOMEDO_SRC_ROOT}/files/"
local dst_files="${TOMEDO_DEST_ROOT}/files"
if [[ ! -d "$src_snapshot" ]]; then
log "FEHLER: Tomedo Snapshot-Ordner fehlt: $src_snapshot"
exit 1
fi
if [[ ! -d "$src_files" ]]; then
log "FEHLER: Tomedo files-Ordner fehlt: $src_files"
exit 1
fi
if [[ ! -f "$TOMEDO_MACOS_EXCLUDES" ]]; then
log "FEHLER: Exclude-Datei fehlt: $TOMEDO_MACOS_EXCLUDES"
exit 1
fi
if [[ ! -f "$TOMEDO_FILES_EXCLUDES" ]]; then
log "FEHLER: Exclude-Datei fehlt: $TOMEDO_FILES_EXCLUDES"
exit 1
fi
mkdir -p "$dst_snapshot" "$dst_files"
log "== rsync Tomedo Snapshot: $src_snapshot -> $dst_snapshot =="
rsync -r -l -t -O --info=progress2 \
--exclude-from "$TOMEDO_MACOS_EXCLUDES" \
"$src_snapshot" \
"$dst_snapshot"
log "== rsync Tomedo Files: $src_files -> $dst_files =="
rsync -r -l -t -O --info=progress2 \
--exclude-from "$TOMEDO_MACOS_EXCLUDES" \
--exclude-from "$TOMEDO_FILES_EXCLUDES" \
"$src_files" \
"$dst_files"
log "== Tomedo Backup fertig: $last_backup =="
}
# -----------------------------
# Main
# -----------------------------
need_root
check_deps
acquire_lock
bringup_wg
mount_cifs
rsync_tomedo_backup
log "== Fertig. =="
send_pushover "Backup erfolgreich auf $(hostname) abgeschlossen."