diff --git a/macos/backup-bfds-storage-offsite/backup-bfds-storage-offsite.sh b/macos/backup-bfds-storage-offsite/backup-bfds-storage-offsite.sh new file mode 100755 index 0000000..88bd0b2 --- /dev/null +++ b/macos/backup-bfds-storage-offsite/backup-bfds-storage-offsite.sh @@ -0,0 +1,158 @@ +#!/bin/bash + +# Sichert die Freigaben von bfds auf die OFFSITE-Platte (VeraCrypt-Partition). +# macOS-Portierung von robocopy-bfds-offsite/bfds-offsite-backup.cmd. +# Kompatibel mit dem mit macOS ausgelieferten Bash 3.2. +# +# Ablauf: VeraCrypt-Partition entschluesseln und einbinden, SMB-Freigaben von +# bfds per rsync kopieren, Partition wieder aushaengen. +# +# Voraussetzung: VeraCrypt (+ macFUSE) installiert. +# Optional vorab setzen: DEVICE=/dev/disk4s1 ./backup-bfds-storage-offsite.sh + +PATH="/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/homebrew/bin" +export PATH + +SHARES=( + # photos + # rob + # movies + # music + # peggy + ebooks + ) + + +VOLUME_LABEL="${VOLUME_LABEL:-NASOFFSITE1}" +VERACRYPT="${VERACRYPT:-/Applications/VeraCrypt.app/Contents/MacOS/VeraCrypt}" +MOUNT_POINT="${MOUNT_POINT:-/Volumes/$VOLUME_LABEL}" +SOURCE_HOST="${SOURCE_HOST:-bfds}" +DEVICE="${DEVICE:-}" + + +MOUNTED_BY_US=0 + +log() { + printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" +} + +die() { + log "FEHLER: $*" + exit 1 +} + +unmount_volume() { + [ "$MOUNTED_BY_US" = "1" ] || return 0 + log "Partition wird ausgehaengt" + "$VERACRYPT" --text --dismount "$MOUNT_POINT" --non-interactive \ + || log "WARNUNG: Aushaengen fehlgeschlagen, bitte in VeraCrypt manuell trennen" + MOUNTED_BY_US=0 +} + +# Die Disk-Nummer wechselt (disk4, disk6, ...). Gesucht wird daher die +# Partition 1 auf externen, physischen Platten, die >= MIN_SIZE_GB gross sind +# und nicht von macOS eingebunden werden koennen (Typ "Microsoft Basic Data", +# verschluesselt). Nur bei genau einem Treffer wird sie verwendet. +MIN_SIZE_GB="${MIN_SIZE_GB:-3000}" + +find_offsite_partition() { + local disk part bytes + local found=() + for disk in $(diskutil list external physical | awk '/^\/dev\/disk/ {gsub(/[:]/,"",$1); sub("/dev/","",$1); print $1}'); do + part="${disk}s1" + diskutil info "$part" >/dev/null 2>&1 || continue + diskutil list "$disk" | grep -q "Microsoft Basic Data.*${part}\$" || continue + bytes="$(diskutil info "$disk" | sed -n 's/.*Disk Size:.*(\([0-9]*\) Bytes).*/\1/p' | head -1)" + [ -n "$bytes" ] && [ "$bytes" -ge $((MIN_SIZE_GB * 1000000000)) ] || continue + # Schon eingebundene Partition (normales Dateisystem) ist nicht die gesuchte + [ -z "$(diskutil info "$part" | sed -n 's/^ *Mount Point: *\(.\+\)$/\1/p')" ] || continue + found+=("/dev/$part") + done + if [ "${#found[@]}" -ne 1 ]; then + log "FEHLER: Keine eindeutige OFFSITE-Partition gefunden (Treffer: ${#found[@]})" >&2 + return 1 + fi + log "OFFSITE-Partition erkannt: ${found[0]}" >&2 + echo "${found[0]}" +} + +trap 'unmount_volume' EXIT +trap 'log "Abgebrochen"; exit 130' INT TERM + +# ************************************************** +# Einbinden der VeraCrypt Partition +# ************************************************** +[ -x "$VERACRYPT" ] || die "VeraCrypt nicht gefunden: $VERACRYPT" + +if [ -d "$MOUNT_POINT" ] && mount | grep -q " on $MOUNT_POINT "; then + log "Festplatte $VOLUME_LABEL ist bereits eingebunden: $MOUNT_POINT" +else + if [ -z "$DEVICE" ]; then + DEVICE="$(find_offsite_partition)" || exit 1 + fi + # "disk4s1" und "/dev/disk4s1" akzeptieren + case "$DEVICE" in + disk*) DEVICE="/dev/$DEVICE" ;; + esac + [ -e "$DEVICE" ] || die "Device nicht gefunden: $DEVICE" + + read -r -s -p "Bitte geben Sie das Passwort fuer die Festplatte $VOLUME_LABEL ein: " VOLUME_PASSWORD + echo + + log "Festplatte $VOLUME_LABEL wird entschluesselt und eingebunden" + "$VERACRYPT" --text --non-interactive --mount "$DEVICE" "$MOUNT_POINT" \ + --password="$VOLUME_PASSWORD" --pim=0 --keyfiles="" --protect-hidden=no \ + || die "Einbinden mit VeraCrypt fehlgeschlagen" + unset VOLUME_PASSWORD + MOUNTED_BY_US=1 +fi + +[ -d "$MOUNT_POINT" ] || die "Festplatte $VOLUME_LABEL nicht gefunden unter $MOUNT_POINT" + +# ************************************************** +# Kopieren der SMB-Freigaben +# ************************************************** +EXIT_CODE=0 + +run_rsync() { + local share="$1" + local source="/Volumes/$share" + + if [ ! -d "$source" ]; then + log "Binde SMB-Freigabe smb://$SOURCE_HOST/$share ein" + # Unter sudo als eigentlicher Benutzer oeffnen, damit dessen Keychain/Finder genutzt wird + if [ -n "${SUDO_USER:-}" ]; then + sudo -u "$SUDO_USER" open "smb://$SOURCE_HOST/$share" + else + open "smb://$SOURCE_HOST/$share" + fi + local i + for i in 1 2 3 4 5 6 7 8 9 10; do + [ -d "$source" ] && break + sleep 1 + done + fi + if [ ! -d "$source" ]; then + log "WARNUNG: Quelle nicht gefunden, bitte SMB-Freigabe oeffnen: smb://$SOURCE_HOST/$share" + EXIT_CODE=1 + return + fi + + log "Sichere $share" + # -r Unterverzeichnisse inkl. leerer, -l Symlinks, -t Zeitstempel (wie /COPY:DAT), + # --partial setzt abgebrochene Uebertragungen fort (wie /z). Kein --delete: + # robocopy /E spiegelt ebenfalls nicht. + rsync -rlt -h -v --progress --partial --stats --exclude=".DS_Store" --exclude="._.DS_Store" "$source/" "$MOUNT_POINT/$share/" + local rc=$? + if [ "$rc" -ne 0 ]; then + log "WARNUNG: rsync fuer $share endete mit Code $rc" + EXIT_CODE=1 + fi +} + +for share in "${SHARES[@]}"; do + run_rsync "$share" +done + +log "Backupprozess beendet" +exit "$EXIT_CODE" diff --git a/macos/backup-bfds-storage-offsite/install-spotlight-app.sh b/macos/backup-bfds-storage-offsite/install-spotlight-app.sh new file mode 100755 index 0000000..29872f5 --- /dev/null +++ b/macos/backup-bfds-storage-offsite/install-spotlight-app.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +# Erstellt ~/Applications/BFDS Offsite Backup.app, damit das Backup per +# Spotlight ("bfds offsite backup") gestartet werden kann. Die App oeffnet +# ein Terminal-Fenster und startet dort launch-backup.sh (Header, dann sudo-Start) +# (Passwort-Abfragen und Ausgabe brauchen ein Terminal). +# Kompatibel mit dem mit macOS ausgelieferten Bash 3.2. + +set -euo pipefail + +APP_NAME="${APP_NAME:-BFDS Offsite Backup}" +APP_DIR="${APP_DIR:-$HOME/Applications}" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +BACKUP_SCRIPT="$SCRIPT_DIR/launch-backup.sh" +APP_PATH="$APP_DIR/$APP_NAME.app" + +[ -x "$BACKUP_SCRIPT" ] || { echo "FEHLER: $BACKUP_SCRIPT nicht ausfuehrbar" >&2; exit 1; } + +mkdir -p "$APP_DIR" +rm -rf "$APP_PATH" + +# Pfad fuer AppleScript-String und Shell quoten +SHELL_CMD="$(printf '%q' "$BACKUP_SCRIPT")" +SHELL_CMD="${SHELL_CMD//\\/\\\\}" +SHELL_CMD="${SHELL_CMD//\"/\\\"}" + +/usr/bin/osacompile -o "$APP_PATH" < 0 then exit repeat + delay 0.1 + end repeat + if (count of windows) > 0 then + do script "$SHELL_CMD" in window 1 + else + do script "$SHELL_CMD" + end if + end if +end tell +EOF + +echo "Installiert: $APP_PATH" +echo "Spotlight: \"bfds offsite backup\" eintippen (Indexierung kann kurz dauern)." + +# Standardmaessig heisst die App intern "applet" (so erscheint sie auch in den +# Datenschutz-Listen). Ausfuehrbare Datei, Bundle-Name und -ID umbenennen und neu signieren. +PLIST="$APP_PATH/Contents/Info.plist" +EXEC_NAME="BFDS-Offsite-Backup" +mv "$APP_PATH/Contents/MacOS/applet" "$APP_PATH/Contents/MacOS/$EXEC_NAME" +PB=/usr/libexec/PlistBuddy +"$PB" -c "Set :CFBundleExecutable $EXEC_NAME" "$PLIST" +"$PB" -c "Set :CFBundleName $APP_NAME" "$PLIST" +"$PB" -c "Add :CFBundleIdentifier string de.streber24.bfds-offsite-backup" "$PLIST" 2>/dev/null \ + || "$PB" -c "Set :CFBundleIdentifier de.streber24.bfds-offsite-backup" "$PLIST" +"$PB" -c "Add :CFBundleDisplayName string $APP_NAME" "$PLIST" 2>/dev/null \ + || "$PB" -c "Set :CFBundleDisplayName $APP_NAME" "$PLIST" +/usr/bin/codesign --force --deep --sign - "$APP_PATH" + +# Voller Festplattenzugriff laesst sich nicht per Script erteilen (macOS schuetzt +# die Freigabe, nur MDM oder der Nutzer selbst kann sie setzen). Deshalb: Einstellungen +# oeffnen und die App im Finder markieren, zum Hineinziehen in die Liste. +echo +echo "Bitte jetzt einmalig 'Voller Festplattenzugriff' fuer die App erteilen:" +echo " App aus dem Finder-Fenster in die Liste ziehen (oder + klicken) und Schalter aktivieren." +open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles" +open -R "$APP_PATH" diff --git a/macos/backup-bfds-storage-offsite/launch-backup.sh b/macos/backup-bfds-storage-offsite/launch-backup.sh new file mode 100755 index 0000000..de83030 --- /dev/null +++ b/macos/backup-bfds-storage-offsite/launch-backup.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# Startet das Backup aus der Spotlight-App: zeigt zuerst einen Willkommens- +# Header samt Hinweis auf die sudo-Passwortabfrage, dann das eigentliche Script. +# Kompatibel mit dem mit macOS ausgelieferten Bash 3.2. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" + +clear +cat <<'BANNER' +##################################################### +## ## +## BFDS OFFSITE BACKUP ## +## ## +## NAS-Freigaben -> verschluesselte Platte ## +## ## +##################################################### + +Willkommen! + + 1. Zuerst wird jetzt Dein MAC-PASSWORT (sudo) abgefragt, + damit das Programm gestartet werden kann. + Beim Tippen erscheinen keine Zeichen. + 2. Danach folgt das Passwort der VeraCrypt-Festplatte. + +BANNER + +sudo "$SCRIPT_DIR/backup-bfds-storage-offsite.sh" +status=$? + +echo +read -r -n 1 -s -p "Fertig (Exit-Code $status). Taste druecken zum Schliessen... " +echo +exit "$status"