#!/bin/bash
# ====================================================
# Nextcloud Backup Script
# ====================================================
# Beschreibung:
# Erstellt ein vollständiges Backup der Nextcloud-Installation
# inkl. Datenbank-Dump und verpackt alles in eine TGZ-Datei.
#
# https://chat.deepseek.com/a/chat/s/b683b7a5-0ad1-49b8-b63e-a11aff2b5e52
#
# es gib ein logfile für alle Fehler die auftreten
# die Meldungen kommen nur wenn debug eingeschaltet ist (debug > 0)
#
#
####### Speicherplatz berechnen !!!!!!!!!!
### benoetigter_Speicherplatz=$(tar -czf - datei "$(basename "$nc_path")" |wc -c)
### echo -e "benoetigter_Speicherplatz=$benoetigter_Speicherplatz"
### exit
#
####### eigentlich sollte die Sicherung auf einem anderen Gerät erfolgen
### wenn das Laufwerk defekt ist, ist auch die Sicherung futsch
### Frage: Netzlaufwerk, ftp-Laufwerk, USB-Stick ????
#
####### die Funktion limit_backups sollte vor dem Anlegen der Backups
### aufgerufen werden damit bei grossen Backups beurteil werden kann
### wieviel Speicherplatz wahrscheinlich benötigt werden wird
### -- wenn schon ein Backup vorhanden ist
### -- steht die wahrscheinlich ja fest
#
#
#
#
########################################################
debug=0
backup_start=$(date +%s)
###### Pfade anpassen: #######################
## wird in Zukunft durch die Funktion web_app_type ermittelt
#~ NC_PATH="/var/www/html/nc.dcb-charlot.de"
BACKUP_BASE="/home/pi/sicherung"
MAX_BACKUPS=2
SKRIPTEVERZEICHNIS=/usr/local/bin
###### abgeleitete Variable ##################
## sourcen der Funktionsdateien ---------------------
function funktionen_nachladen(){
if [[ -e "$1" ]];then
source "$1"
if [[ $? -ne 0 ]]; then echo "Sourcing fehlgeschlagen"; exit; fi
else
echo -e "die Datei $1 Funktionen wurde nicht gefunden"
echo -e "das Programm wird beendet!\n\n"
exit
fi
}
funktionen_nachladen "${SKRIPTEVERZEICHNIS}/detect_web_apps.sh"
funktionen_nachladen "${SKRIPTEVERZEICHNIS}/zeitdifferenz.sh"
funktionen_nachladen "${SKRIPTEVERZEICHNIS}/limit_backups.sh"
funktionen_nachladen "${SKRIPTEVERZEICHNIS}/in_arbeit.sh"
nc_path=$(web_app_type 'nextcloud')
# timestamp für den Archivnamen (Format: Jahr-Monat-Tag_Stunde-Minute-Sekunde)
timestamp=$(date +"%Y%m%d_%H%M")
backup_name="${nc_path##*/}-${timestamp}.tar.gz"
backup_file="${BACKUP_BASE}/${backup_name}"
LOG_FILE="$BACKUP_BASE/${nc_path##*/}.log"
# Temporärer Speicherort für den Datenbank-Dump
tmp_dump="${nc_path}/nextcloud_db_dump.sql"
## du_muss_root_sein
if [[ $(id -u) -ne 0 ]];then
[[ $debug -ne 0 ]] && echo -e "root Privilegien erforderlich!\n --> ${BASH_SOURCE##*/}:$LINENO\n\n";
echo -e "$timestamp root Privilegien erforderlich!\n --> ${BASH_SOURCE##*/}:$LINENO\n\n" >> $LOG_FILE
exit;
fi
#~ if [[ debug -eq 9 ]];then
if [[ debug -gt 0 ]];then
echo -e ""
echo -e " nc_path=$nc_path\n"
echo -e "BACKUP_BASE=$BACKUP_BASE"
echo -e "backup_file=$backup_file"
echo -e " LOG_FILE=$LOG_FILE"
echo -e ""
echo -e "backup_name=$backup_name"
echo -e " tmp_dump=$tmp_dump"
echo -e ""
fi
#=======================================================================
function linie { for ((i = 1 ; i < $(tput cols) ; i++));do echo -n "-"; done; echo;}
## gib die aktelle Fehlermeldung aus
fehler_meldung() {
if [[ $# -eq 0 ]];then
echo -e "$timestamp Abbruch ohne Fehlermeldung" >> $LOG_FILE
cleanup
fi
if [[ $debug -ne 0 ]];then
echo -e "$timestamp $1";
fi
echo -e "$timestamp $1" >> $LOG_FILE
cleanup
}
# Funktion: Fehlerbehandlung und Wartungsmodus ausschalten
cleanup() {
if [ -f "$tmp_dump" ]; then
rm -f "$tmp_dump"
fi
# Wartungsmodus deaktivieren (falls aktiv)
if [ -f "${nc_path}/config/config.php" ]; then
sudo -u www-data php "${nc_path}/occ" maintenance:mode --off &>/dev/null
fi
echo "$timestamp Backup abgebrochen oder fehlgeschlagen." >> $LOG_FILE
if [[ $debug -ne 0 ]];then
echo -e "\ndas Logfile ($LOG_FILE)"
linie
tail $LOG_FILE;
linie
fi
exit 1
}
# Trap für Abbruchsignale (Ctrl+C, Fehler)
## ruft die cleanup-Funktion auf ##
trap cleanup ERR INT TERM
#=======================================================================
echo -e "Nextcloud-Backup ${nc_path}"
in_arbeit "Start"
# Prüfen, ob Nextcloud-Pfad existiert
if [ ! -d "${nc_path}" ]; then
fehler_meldung "Fehler: Nextcloud-Pfad '$nc_path' existiert nicht!"
cleanup
fi
# Backup-Zielverzeichnis erstellen, falls nicht vorhanden
mkdir -p "${BACKUP_BASE}" || {
fehler_meldung "Fehler: Kann Zielverzeichnis '$BACKUP_BASE' nicht erstellen."
cleanup
}
[[ $debug -ne 0 ]] && echo -e "===== Nextcloud Backup gestartet: $(date) =====";
# 1. Nextcloud in den Wartungsmodus versetzen
[[ $debug -ne 0 ]] && echo -e "Wartungsmodus aktivieren...";
sudo -u www-data php "${nc_path}/occ" maintenance:mode --on &>/dev/null
if [ $? -ne 0 ]; then
fehler_meldung "Fehler: Kann Wartungsmodus nicht aktivieren."
cleanup
fi
# 2. Datenbank-Zugangsdaten aus config.php extrahieren
CONFIG_FILE="${nc_path}/config/config.php"
if [ ! -f "${CONFIG_FILE}" ]; then
fehler_meldung "Fehler: Nextcloud-config: ${CONFIG_FILE} nicht gefunden!"
cleanup
fi
datadirectory=$(cat $CONFIG_FILE |grep datadirectory |sed 's/ //g' |cut -d"'" -f4)
if [[ ! -e "${datadirectory}" ]];then
fehler_meldung "Fehler: Nextcloud $datadirectory nicht gefunden!"
cleanup
fi
# Absolute, aufgelöste Pfade (keine relativen, keine Symlinks)
abs_nc=$(realpath "$nc_path")
abs_data=$(realpath "$datadirectory")
# Prüfen, ob data_dir innerhalb von nextcloud_dir liegt
if [[ "$abs_data" == "$abs_nc"/* ]]; then
[[ $debug -ne 0 ]] && echo -e "data-Verzeichnis liegt innerhalb der Nextcloud-Installation!";
datadirectory=""
else
[[ $debug -ne 0 ]] && echo -e "OK: data-Verzeichnis liegt außerhalb der Installation.";
fi
# Extrahiere 'dbname', 'dbuser', 'dbpassword' mit grep/sed
DB_NAME=$(grep "'dbname'" "$CONFIG_FILE" | sed "s/.*'dbname' => '\([^']*\)'.*/\1/")
DB_USER=$(grep "'dbuser'" "$CONFIG_FILE" | sed "s/.*'dbuser' => '\([^']*\)'.*/\1/")
DB_PASS=$(grep "'dbpassword'" "$CONFIG_FILE" | sed "s/.*'dbpassword' => '\([^']*\)'.*/\1/")
if [ -z "$DB_NAME" ] || [ -z "$DB_USER" ] || [ -z "$DB_PASS" ]; then
fehler_meldung "Fehler: Datenbank-Zugangsdaten konnten nicht ausgelesen werden."
cleanup
fi
if [[ -z "noch nicht fertig" ]];then
## Überprfen ob das zu erwartende Backup-file noch auf des Medium passt
MIN_FREIER_PLATZ=.80
prefix="${backup_file%${timestamp}.tar.gz}"
SIZE=$(stat -c%s "$(ls -t ${prefix}*.tar.gz |head -n1)")
freier_platz=$(df --block-size=1 --output=avail "$BACKUP_BASE" | tail -n1)
prozent_frei=$(echo "scale=2; $SIZE * 100 / $freier_platz" | bc)
echo "$SIZE Bytes"
echo "$freier_platz Bytes verfügbar"
echo "Das entspricht $prozent_frei des aktuell freien Platzes."
echo -e "-------"
# Verfügbarer und gesamter Speicherplatz im Zielverzeichnis (Medium)
#~ freier_platz=$(df --block-size=1 --output=avail "$BACKUP_BASE" | tail -n1)
exit
fi
# 3. MySQL-Dump erstellen
[[ $debug -ne 0 ]] && echo -e "Erstelle Datenbank-Dump (${DB_NAME}) ...";
mysqldump --single-transaction --quick --skip-lock-tables \
-u "$DB_USER" -p"$DB_PASS" "$DB_NAME" > "$tmp_dump"
if [ $? -ne 0 ] || [ ! -s "$tmp_dump" ]; then
fehler_meldung "Fehler: Datenbank-Dump fehlgeschlagen oder leer."
cleanup
fi
[[ $debug -ne 0 ]] && echo -e "Datenbank-Dump erfolgreich erstellt: $(du -h "$tmp_dump" | cut -f1)";
# 4. Alle Dateien (inkl. Dump) in ein TGZ-Archiv packen
[[ $debug -ne 0 ]] && echo -e "Erstelle tar.gz-Archiv: $backup_name";
# Wechsle ins übergeordnete Verzeichnis von nc_path, damit der Pfad relativ wird
# (Beim Entpacken entsteht wieder das Verzeichnis nc.dcb-charlot.de)
cd "$(dirname "$nc_path")" || cleanup
#~ tar -czf "$backup_file" "$(basename "$nc_path")" "$(basename "$datadirectory")"
tar -czf "$backup_file" $nc_path $datadirectory $SKRIPTEVERZEICHNIS &>/dev/null
if [ $? -ne 0 ]; then
fehler_meldung "Fehler: Fehler beim Erstellen des Tar-Archivs."
cleanup
fi
[[ $debug -ne 0 ]] && echo -e "Backup-Datei erstellt: $backup_file";
[[ $debug -ne 0 ]] && echo -e "Größe: $(du -h "$backup_file" | cut -f1)";
# 5. Temporären Dump löschen
rm -f "$tmp_dump"
## Anzahl der Backups begranzen
limit_backups "$backup_file" $MAX_BACKUPS "$timestamp"
# 6. Wartungsmodus deaktivieren
[[ $debug -ne 0 ]] && echo -e "Wartungsmodus deaktivieren...";
sudo -u www-data php "${nc_path}/occ" maintenance:mode --off &>/dev/null
backup_ende=$(date +%s)
backupdauer=$(zeitdifferenz "$backup_start" "$backup_ende")
echo -e "\nDauer des Backups war ${backupdauer} \nes wurden $(du -h $backup_file |sed 's/\t\+/ /g;s/ \+/ /g'| cut -d' ' -f1 ) Speicherplatz belegt\n"
in_arbeit "Ende"
[[ $debug -ne 0 ]] && echo -e "===== Backup erfolgreich abgeschlossen: $(date) =====";
exit 0