HOST_NAME=$(hostname -s) DAY=$(date +%u) # 1-7, MO is 1 WEEKLY_DAY=7 # SA->SO NAS_MOUNT_POINT="$(realpath $HERE/../../..)" # Server/bin/backup NAS_ROOT="$NAS_MOUNT_POINT/backups" LOG_FILE_PATH="$NAS_MOUNT_POINT/Server/log/backup" log_file_name="$LOG_FILE_PATH/$HOST_NAME-$(date '+%Y-%m-%d_%H%M%S')" LOG_FILE="${log_file_name}.log" LOG_KEEP_DAYS=14 RESTORE_PATH="$NAS_MOUNT_POINT/Server/restore" BACKUP_MASTER_CHECK="/var/containers/vol/caddy" NAS_HOSTNAME=synology NAS_IP=10.4.2.20 NAS_SCRIPT=/volume1/nfs/Server/bin/backup/main.sh PASSPHRASE=L1rfCXu8PHMQsGL0HJWwzQYpCT7f5VboislOTHMaEV EXT=tgz.gpg LIST_EXT=list #DOCKER="/usr/bin/docker compose -f /var/docker/run/docker-compose.yml --ansi never" SYNO_USB=/usr/syno/bin/synousbdisk SYSTEMCTL_USER="/usr/bin/systemctl --user --machine=container@" SYSTEMCTL="/usr/bin/systemctl" PODMAN=" sudo -u container podman" CONTAINER_EXEC_USER=" $PODMAN exec" CONTAINER_SVC_UPDATE_USER=" $PODMAN auto-update" CONTAINER_EXEC="podman exec" LOG_RECIPIENT=hb242@pm.me INFO=INFO WARN=WARN ERROR=ERROR DEBUG=DEBUG SUCCESS="*** success" INF="---" WRN=":::" ERR="###" DBG="===" # ----- logging ----- # Log-Datei schreiben. # # $1 - Typ, z.B. INFO, WARN, ERROR, etc. # $2 ff. - die Meldung, die ins Log geschreiben wird # log() { local type=$1 #if [ "$type" == "ERROR" ]; then ERROR=0; fi shift 1 echo "$(date '+%d.%m.%Y %H:%M:%S') - $type $@" | tee -a $LOG_FILE >/dev/null } # Reduziertes Log für automatische Läufe # # Das Log wird am Ende des Laufs als Mail verschickt. Die Meldungen # werden zusätzlich im Standard-Log protokolliert. # Dieses log wird nur geschrieben, wenn $MAIL_LOG_FILE definiert ist. # # $1 - Typ, z.B. INFO, WARN, ERROR, etc. # $2 ff. - die Meldung, die ins Log geschreiben wird # mail_log() { local type=$1 shift if [ "$MAIL_LOG_FILE" ]; then echo "$(date '+%d.%m.%Y %H:%M:%S') - $type $@" >> $MAIL_LOG_FILE fi log "$type" "$@" } # Reduziertes Log senden # send_mail_log() { local subject="$@" log $DEBUG "$DBG send_mail_log()" log $DEBUG "$DBG > $HERE/mail.sh $LOG_RECIPIENT \"[$HOST_NAME] $subject\" $MAIL_LOG_FILE" $HERE/mail.sh $LOG_RECIPIENT "[$HOST_NAME] $subject" $MAIL_LOG_FILE >>$LOG_FILE 2>&1 } # Alte Logs löschen # # Dateien älter als $LOG_KEEP_DAYS Tage löschen # prune_logs() { log $DEBUG "$DBG prune_logs()" log $DEBUG "$DBG > find \"$LOG_FILE_PATH\" -type f -mtime +$LOG_KEEP_DAYS -delete" find "$LOG_FILE_PATH" -type f -mtime +$LOG_KEEP_DAYS -delete >>$LOG_FILE 2>&1 if [ $? -ne 0 ]; then # Fehler nur protokollieren, kein Grund abzubrechen mail_log $ERROR "$ERR error deleting old log files." fi } # ----- db dump ----- #dump_mariadb() { # return 0 # log $DEBUG "$DBG dump_mariadb( $@ )" # local dump_dir=$1 # local mysqlconnect="--user=root --password=snafu" # # log $DEBUG "$DBG > \$DOCKER exec mysql /usr/bin/mariadb --silent --user=xxx --password=xxx -e \"show databases\" -B" # for db in $($DOCKER exec mysql /usr/bin/mariadb --silent $mysqlconnect -e "show databases" -B 2>>$LOG_FILE | grep -v information_schema 2>>$LOG_FILE | grep -v performance_schema 2>>$LOG_FILE | sed -e 's/\r//' 2>>$LOG_FILE); do # log $DEBUG "$DBG > \$DOCKER exec mysql /usr/bin/mariadb-dump --hex-blob --user=xxx --password=xxx --databases $db" # $DOCKER exec mysql /usr/bin/mariadb-dump --hex-blob $mysqlconnect --databases $db >$dump_dir/$db.sql 2>>$LOG_FILE # is_error $? false && return 1 # done # return 0 #} dump_postgresql() { local pg_container=$1 local dump_dir=$2 log $DEBUG "$DBG dump_postgresql( $@ )" delete_dir $dump_dir check_directory $dump_dir # users and roles log $DEBUG "$DBG > \$CONTAINER_EXEC_USER -u=postgres $pg_container pg_dumpall --globals-only" $CONTAINER_EXEC_USER -u=postgres $pg_container pg_dumpall --globals-only > $dump_dir/_globals.sql 2>>$LOG_FILE log $DEBUG "$DBG \$CONTAINER_EXEC_USER -u=postgres $pg_container psql -t -c \"select datname from pg_database where not datistemplate\"" for db in $($CONTAINER_EXEC_USER -u=postgres $pg_container psql -t -c "select datname from pg_database where not datistemplate" 2>>$LOG_FILE); do log $DEBUG "$DBG > \$CONTAINER_EXEC_USER -u=postgres $pg_container pg_dump --create $db" $CONTAINER_EXEC_USER -u=postgres $pg_container pg_dump --create $db > $dump_dir/$db.sql 2>>$LOG_FILE is_error $? false && return 1 done return 0 } # ----- helper functions ----- # Für Backup und Export muss das Script mit root-Rechten # ausgeführt werden. # check_root() { if [ "$(id -u)" -ne 0 ]; then log $ERROR "$ERR script must be run as superuser." echo "****" echo "***** script must be run as superuser! *****" echo "****" exit 100 fi } start_containers() { local let rc=0 log $DEBUG "$DBG start_containers()" # if [ -f /var/docker/run/docker-compose.yml ]; then # log $DEBUG "$DBG > \$DOCKER up -d" # $DOCKER up -d >>$LOG_FILE 2>&1 # let rc=rc+$? # fi if [ -d /var/containers ]; then do_start_stop_containers start let rc=rc+$? fi return $rc } stop_containers() { local let rc=0 log $DEBUG "$DBG stop_containers()" # if [ -f /var/docker/run/docker-compose.yml ]; then # log $DEBUG "$DBG > \$DOCKER down" # $DOCKER down >>$LOG_FILE 2>&1 # let rc=rc+$? # fi if [ -d /var/containers ]; then do_start_stop_containers stop let rc=rc+$? fi return $rc } do_start_stop_containers() { local cmd=$1 local let rc=0 log $DEBUG "$DBG do_start_stop_containers() $cmd" if [ ! -z "$ROOT_CONTAINERS" ]; then log $DEBUG "$DBG > $SYSTEMCTL $cmd $ROOT_CONTAINERS" $SYSTEMCTL $cmd $ROOT_CONTAINERS >>$LOG_FILE 2>&1 let rc=rc+$? fi if [ ! -z "$USER_CONTAINERS" ]; then log $DEBUG "$DBG > $SYSTEMCTL_USER $cmd $USER_CONTAINERS" $SYSTEMCTL_USER $cmd $USER_CONTAINERS >>$LOG_FILE 2>&1 let rc=rc+$? fi return $rc } update_services() { log $DEBUG "$DBG update_services()" mail_log $INFO "$INF update services" local upd=$($CONTAINER_SVC_UPDATE_USER 2>&1 | tee -a $LOG_FILE | egrep 'registry\s+true' | sed 's/^.* \([^[:space:]]*\/[^[:space:]]*\).*$/\1/g') check_pipe_rc ${PIPESTATUS[@]} local rc=$? for i in $upd; do mail_log $INFO "$INF $i" done return $rc } check_services() { log $DEBUG "$DBG check_services()" mail_log $INFO "$INF check services" $HERE/check.sh >>$LOG_FILE 2>&1 return $? } # Zugriffsrechte für Logs und Backups setzen # fix_acls() { log $DEBUG "$DBG fix_acls()" log $DEBUG "$DBG > chmod -R a+r $NAS_ROOT" chmod -R a+r $NAS_ROOT >>$LOG_FILE 2>&1 log $DEBUG "$DBG > chmod -R a+r $LOG_FILE_PATH" chmod -R a+r $LOG_FILE_PATH >>$LOG_FILE 2>&1 } # Script per ssh auf Server $1 im Hintergrund starten # # ssh start in background # -> https://stackoverflow.com/questions/29142/getting-ssh-to-execute-a-command-in-the-background-on-target-machine#comment107117479_29172 # # $1 - Server # $@ - Script incl. Parameter # run_script_remote() { local server=$1 shift ssh $server "sudo sh -c 'nohup $@ &>/dev/null mv \"$source\"/* \"$target\"" mv "$source"/* "$target" >>$LOG_FILE 2>&1 return $? } # Ist $1 in der Liste der restlichen args $2, $3 ... # # zB. if is_in "foo" "foo" "bar" "foo bar" ; then ... # Leere Liste liefert immer false # is_in() { local search="$1" shift 1 local e for e in "$@"; do [[ "$e" == "$search" ]] && return 0; done return 1 } # Alle Verzechnisnamen in einem Verzeichnis holen. # # $1 - Verzeichnis in dem gesucht wird als absoluter Pfad # $2 ff. - auszuschliessende Verzeichnisse (kann leer bleiben) # iterate_dirs_in() { local dirname local root=$1 shift 1 for dir in $(find "$root" -maxdepth 1 -type d \! -wholename "$root"); do dirname=$(basename $dir) if ! is_in "$dirname" "$@"; then echo "$dirname" fi done } # create directory if it not exists # # $1 - dir to check # # returns 0 for success, 1 otherwise # check_directory() { log $DEBUG "$DBG check_directory( $@ )" local dir=$1 if [ -d "$1" ]; then return 0 else log $DEBUG "$DBG > mkdir -p \"$1\"" mkdir -p "$1" >>$LOG_FILE 2>&1 && return 0 log $ERROR "$ERR could not create directory $1!" return 1 fi } # Verzeichnis löschen # # Im Fehlerfall wird rc=1 zurückgegeben # # $1 - zu löschendes Verzeichnis # # returns 0 for success, 1 otherwise # delete_dir() { log $DEBUG "$DBG delete_dir( $@ )" local dir=$1 log $DEBUG "$DBG > rm -rf \"$dir\"/*" rm -rf "$dir"/* >>$LOG_FILE 2>&1 && return 0 # return error, so caller can decide what to do return 1 } # ----- backup ----- # Slashes "/" im Pfadnamen durch Dashes "-" ersetzen, so dass der String als Dateiname verwendet werden kann. # # $1 - path backup_file_prefix() { echo "$1" | sed -E 's/^\///' | sed -E 's/\//-/g' } # Verzeichnise unter $1 in .tgz.gpg-Dateien sichern. # # $1 - Verzeichnis, das gesichert wird: alle Verzeichnisse darunter werden einzeln als .tgz.gpg-Datei gesichert # $2 - Zielverzeichnis für .tgz.gpg-Dateien # $3 ff. - Verzeichnisse, die nicht gesichert werden sollen (kann leer bleiben) # # returns 0 for success, 1 otherwise (Fehlerprotokollierung muss aufrufende fn erledigen) # backup_all_dirs() { log $DEBUG "$DBG backup_all_dirs( $@ )" local root=$1 local target=$2 shift 2 local prefix=$(backup_file_prefix $root) local tar_file local dir for dir in $(iterate_dirs_in "$root" "$@"); do tar_file="$target/$prefix-$dir.$EXT" backup_dir "$root" "$tar_file" "$dir" if [ $? -ne 0 ]; then return 1; fi create_list "$tar_file" "$dir" if [ $? -ne 0 ]; then return 1; fi test_integrity "$tar_file" if [ $? -ne 0 ]; then return 1; fi done return 0 } # Einzelnes Verzeichnis sichern. # # $1 - Pfad um Verzeichnis, das gesichert werden soll # $2 - Zielpfad und -name für das Archiv # $2 - zu sicherndes Verzeichnis # # returns 0 for success, 1 otherwise # backup_dir() { log $DEBUG "$DBG backup_dir( $@ )" local root=$1 local archive=$2 local dir=$3 log $DEBUG "$DBG > tar -c -z --numeric-owner --one-file-system --sort=name --exclude \"@eaDir\" -C \"$root\" \"$dir\" | gpg --batch -c --passphrase xxxx -o \"$archive\"" tar -c -z --numeric-owner --one-file-system --sort=name --exclude "@eaDir" -C "$root" "$dir" 2>>$LOG_FILE | gpg --batch -c --passphrase $PASSPHRASE -o "$archive" 2>>$LOG_FILE check_pipe_rc ${PIPESTATUS[@]} return $? } ## # auto backup handling # # hostname-scripte setzen vars und definieren Funktionen backup_daily1, 2, usw. und backup_weekly1, 2, usw. # in den Funktionen werden die vars für den einzelnen backup gesetzt (target, excludes, usw.) # hier wird dann dass hostname-script gesourced (s.o.) und in einer Schleife werden die # entsprechend en backup_*-Funktionen aufgerufen # # returns 0 for success, 1 otherwise # do_auto_backup() { log $DEBUG "$DBG do_auto_backup( $@ )" local func=$1 local count=1 local rc if ! $func$count 2>/dev/null ; then # Seiteneffekt: $func$count wird ausgeführt log $ERROR "$ERR keine Backup-Function $func$count() für $HOST_NAME." exit 1 # kein Mail-Benachrichtigung! fi set_log_info mail_log $INFO "$INF prepare backup" before_backup is_error $? && return 1 delete_dir "$BACKUP_TARGET" mail_log $INFO "$INF check backup directory" check_directory "$BACKUP_TARGET" is_error $? && return 1 until ! $func$count 2>/dev/null # Seiteneffekt: $func$count wird ausgeführt (für nächste Schleife) do mail_log $INFO "$INF backing up directories in $BACKUP_SOURCE" backup_all_dirs "$BACKUP_SOURCE" "$BACKUP_TARGET" "${BACKUP_EXCLUDE_DIRS[@]}" is_error $? && return 1 let "count += 1" done } set_log_info() { local targ if [ -z "$NAS_TARGET" ]; then targ=$BACKUP_TARGET else targ=$NAS_TARGET fi mail_log $INFO ">>> " mail_log $INFO ">>> backup to : $targ" mail_log $INFO ">>> log file : $LOG_FILE" mail_log $INFO ">>> " } backup_daily() { # "master"-Server startet Backup auf NAS if [ -d $BACKUP_MASTER_CHECK ]; then log $DEBUG "$DBG starting daily backup on $NAS_HOSTNAME" run_script_remote $NAS_IP "$NAS_SCRIPT -d" # alte Logs löschen (nur auf einem Server ausführen) prune_logs fi # ggf. weekly backup parallel starten (z.ZT. nur auf NAS) if [ $HAS_WEEKLY -a $WEEKLY_DAY -eq $DAY ]; then log $DEBUG "$DBG starting weekly backup" sleep 2 # wg. log name nohup $THIS_SCRIPT -w >>$LOG_FILE 2>&1 >>>>>>>>> daily auto backup $VER for $HOST_NAME <<<<<<<<<<" do_auto_backup backup_daily # after_backup() muss immer laufen, damit ggf. docker wieder gestartet werden kann mail_log $INFO "$INF finishing backup" after_backup is_error $? mail_log $INFO ">>>>>>>>>> end of auto backup" fix_acls send_mail_log "Daily Backup" } backup_weekly() { mail_log $INFO ">>>>>>>>>> weekly auto backup $VER for $HOST_NAME <<<<<<<<<<" do_auto_backup backup_weekly mail_log $INFO "$INF finishing backup" after_backup is_error $? mail_log $INFO ">>>>>>>>>> end of auto backup" fix_acls send_mail_log "Weekly Backup" } # ----- integrity ----- # Integrität des Archivs testen, indem es entschlüsselt, entpackt # und der Inhalt auf /dev/null ausgegeben wird. # # $1 - Archiv # # returns 0 for success, 1 otherwise # test_integrity() { log $DEBUG "$DBG test_integrity( $@ )" local archive=$1 log $DEBUG "$DBG > gpg --batch --decrypt --passphrase xxxx \"$archive\" | tar -xzO" gpg --batch --decrypt --passphrase $PASSPHRASE "$archive" 2>>$LOG_FILE | tar -xzO >/dev/null 2>>$LOG_FILE check_pipe_rc ${PIPESTATUS[@]} return $? } # ----- restore ----- # Restore aus Archiv # # Wird nach $RESTORE_PATH ausgegeben und mit root-Rechten # auf der NAS ausgeführt. # # $1 - Archiv, mit Pfad ab $NAS_ROOT # $2 ff. - zurückzusichernde Dateien/Verzeichnisse # restore() { log $DEBUG "$DBG restore( $@ )" local archive=$1 shift echo "--- Restore ---" echo "--- archive: $archive" echo "--- files: $@" echo "--- to: $RESTORE_PATH" echo echo "Restore started on NAS, see e-mail" echo run_script_remote $NAS_IP "$NAS_SCRIPT --rx $archive $@" } # Restore # # Beim Restore einzelner Dateien wird nur die ownership der Datei # restored, nicht die des Pfades zur Datei! # do_restore() { if [ "$HOST_NAME" != "$NAS_HOSTNAME" ]; then restore $@ exit fi local archive=$1 shift mail_log $INFO ">>>>>>>>>> restore $VER for $HOST_NAME <<<<<<<<<<" mail_log $INFO ">>> restore from: $archive" mail_log $INFO ">>> files : $@" mail_log $INFO ">>> restore to : $RESTORE_PATH" mail_log $INFO ">>> log file : $LOG_FILE" log $DEBUG "$DBG > gpg --batch --decrypt --passphrase xxxx \"$NAS_ROOT/${archive/#\//}\" | tar -xzv --directory $RESTORE_PATH $@" gpg --batch --decrypt --passphrase $PASSPHRASE "$NAS_ROOT/${archive/#\//}" 2>>$LOG_FILE | tar -xzv --directory $RESTORE_PATH $@ >>$LOG_FILE 2>&1 check_pipe_rc ${PIPESTATUS[@]} is_error $? mail_log $INFO ">>>>>>>>>> end of restore" send_mail_log "Restore" } # ----- archive content ------ # Inhalt des Archivs in eine Textdatei ausgeben. # # $1 - Archiv # # returns 0 for success, 1 otherwise # create_list() { log $DEBUG "$DBG create_list( $@ )" local archive=$1 log $DEBUG "$DBG > gpg --batch --decrypt --passphrase xxxx \"$archive\" | tar -tzv | tee \"$archive.$LIST_EXT\"" gpg --batch --decrypt --passphrase $PASSPHRASE "$archive" 2>>$LOG_FILE | tar -tzv 2>>$LOG_FILE | tee "$archive.$LIST_EXT" >/dev/null 2>>$LOG_FILE check_pipe_rc ${PIPESTATUS[@]} return $? } # Liste der Archives auf stdout ausgeben # # Zeigt alle Archive incl. Verzeichnis (ohne $NAS_ROOT) an # # z.B. # $NAS_ROOT = /volume1/nfs/backups # ... # /daily/synology/backup.3/volume1-nfs-Server.tgz.gpg # /weekly/synology/volume1-media-books.tgz.gpg # # returns 0 for success, 1 otherwise # list_archives() { log $DEBUG "$DBG list_archives()" local target=$1 local targ=${NAS_ROOT//\//\\\/} # slashes escapen log $DEBUG "$DBG > find \"$NAS_ROOT\" -type f -name \"*.$EXT\" | sed -E \"s/^$targ//\"" find "$NAS_ROOT" -type f -name "*.$EXT" 2>>$LOG_FILE | sed -E "s/^$targ//" 2>>$LOG_FILE | sort check_pipe_rc ${PIPESTATUS[@]} return $? } # Inhalt des Archivs $1 auf stdout ausgeben # # $1 - Archiv-Datei mit Pfad ab $NAS_ROOT # # returns 0 for success, 1 otherwise # list_archive_content() { log $DEBUG "$DBG list_archive_content( $@ )" local archive=$1 log $DEBUG "$DBG > cat \"$NAS_ROOT/${archive/#\//}.$LIST_EXT\"" cat "$NAS_ROOT/${archive/#\//}.$LIST_EXT" 2>>$LOG_FILE } # ----- export ----- # Synology-NAS (0) or not (1 - linux)? # is_synology() { if [ -f $SYNO_USB ]; then return 0 else return 1 fi } # Find exfat formatted device # # returns device name # get_usb_device() { if is_synology; then echo $(get_usb_device_syno) else echo $(get_usb_device_linux) fi } # USB-HDD auf Synology-NAS ermitteln # get_usb_device_syno() { log $DEBUG "$DBG get_usb_device_syno()" log $DEBUG "$DBG > $SYNO_USB -enum | grep -v devices | wc -l" if [ "$($SYNO_USB -enum 2>>$LOG_FILE | grep -v devices 2>>$LOG_FILE | wc -l 2>>$LOG_FILE)" != "1" ]; then mail_log $ERROR "$ERR no USB device or too many found @$HOST_NAME." return 1 fi log $DEBUG "$DBG > $SYNO_USB -enum | grep -v devices" echo $($SYNO_USB -enum 2>>$LOG_FILE | grep -v devices 2>>$LOG_FILE) return 0 } # exfat-formatierte USB-HDD auf Linux-Server ermitteln # get_usb_device_linux() { log $DEBUG "$DBG get_usb_device_linux()" local fstype=exfat local lsblk="lsblk -o KNAME,TYPE,SIZE,MODEL,FSTYPE" local device log $DEBUG "$DBG $lsblk | grep $fstype | wc -l" if [ "$($lsblk 2>>$LOG_FILE | grep $fstype 2>>$LOG_FILE | wc -l 2>>$LOG_FILE)" != "1" ]; then mail_log $ERROR "$ERR could not query devices." return 1 fi log $DEBUG "$DBG $lsblk | grep $fstype | sed -E 's/^(\S*).*$/\1/'" device=$($lsblk 2>>$LOG_FILE | grep $fstype 2>>$LOG_FILE | sed -E 's/^(\S*).*$/\1/' 2>>$LOG_FILE) if [ -z "$device" ]; then mail_log $ERROR "$ERR no exfat USB device found." return 1 fi echo $device return 0 } # Mount UsB-HDD # # returns mount point # mount_usb_device() { if is_synology; then echo $(mount_usb_device_syno $1) else echo $(mount_usb_device_linux $1) fi } # Check if auto mounted device is exfat and return mount point # (fn for consitence with linux fn) # # $1 - device name # returns mount point # mount_usb_device_syno() { log $DEBUG "$DBG mount_usb_device_syno( $@ )" local device=$1 log $DEBUG "$DBG > $SYNO_USB -info $device | grep -i \"file system\" | sed -E \"s/.*:\s*(.*)\s*/\1/\"" local fs=$($SYNO_USB -info $device 2>>$LOG_FILE | grep -i "file system" 2>>$LOG_FILE | sed -E "s/.*:\s*(.*)\s*/\1/" 2>>$LOG_FILE) if [ "$fs" != "exfat" ]; then mail_log $ERROR "$ERR device $device not exfat formatted." return 1 fi # get mount point log $DEBUG "$DBG > $SYNO_USB -info $device | grep -i \"mount path\" | sed -E \"s/.*:\s*(.*)\s*/\1/\"" local mount_point=$($SYNO_USB -info $device 2>>$LOG_FILE | grep -i "mount path" 2>>$LOG_FILE | sed -E "s/.*:\s*(.*)\s*/\1/" 2>>$LOG_FILE) if [ $? -ne 0 ] || [ ! -d $mount_point ]; then mail_log $ERROR "$ERR mount point \"$mount_point\" does not exist." return 1 fi echo $mount_point return 0 } # USB-HDD auf Linux-Server mounten # # $1 - device (ohne /dev/) # return moint point # mount_usb_device_linux() { log $DEBUG "$DBG mount_usb_device_linux( $@ )" local device=$1 local mount_point=/var/mnt/backup check_directory $mount_point if [ $? -ne 0 ]; then mail_log $ERROR "$ERR mount point \"$mount_point\" does not exist." return 1 fi log $DEBUG "$DBG mount /dev/$device $mount_point" mount /dev/$device $mount_point >>$LOG_FILE 2>&1 if [ $? -ne 0 ]; then mail_log $ERROR "$ERR mounting /dev/$device failed." return 1 fi echo $mount_point return 0 } # unmount USB-HDD # umount_usb_device() { if is_synology; then umount_usb_device_syno $1 return $? else umount_usb_device_linux $1 return $? fi } # USB-HDD auf Synology-NAS unmounten # # $1 - device name # umount_usb_device_syno() { local device=$1 log $DEBUG "$DBG umount_usb_device_syno( $@ )" log $DEBUG "$DBG > $SYNO_USB -umount $device" $SYNO_USB -umount $device >>$LOG_FILE 2>&1 return $? } # USB-HDD auf Linux-Server unmounten # # $1 - device (ohne /dev/) # umount_usb_device_linux() { local device=/dev/$1 log $DEBUG "$DBG umount_usb_device_linux( $@ )" log $DEBUG "$DBG > umount $device" umount $device >>$LOG_FILE 2>&1 return $? } # USB-HDD suchen und Backup-Typ ermitteln # # Der Typ wird über eine Datei auf der USB-HDD festgelegt: # /.backup#.tag -> z.B. /.backup#int1.tag => Typ=int1 # # $1 - mount point # check_backup_device() { log $DEBUG "$DBG check_backup_drive( $@ )" local mount_point=$1 local tagP1=".backup#" local tagP2=".tag" log $DEBUG "$DBG > ls $mount_point/${tagP1}*${tagP2} | wc -l" if [ "$(ls $mount_point/${tagP1}*${tagP2} 2>>$LOG_FILE | wc -l 2>>$LOG_FILE)" != "1" ]; then mail_log $ERROR "$ERR no tag found on device at $mount_point." return 1 fi log $DEBUG "$DBG > ls $mount_point/${tagP1}*${tagP2} | sed -E \"s/.*$tagP1(.*)$tagP2/\1/\"" local tag=$(ls $mount_point/${tagP1}*${tagP2} 2>>$LOG_FILE | sed -E "s/.*$tagP1(.*)$tagP2/\1/" 2>>$LOG_FILE) if [ $? -ne 0 ]; then mail_log $ERROR "$ERR no tag found on device at $mount_point." return 1 fi echo $tag return 0 } # Datei-Export auf Synology-NAS ausführen # # TODO mount, check, unmount muss raus -> fn macht synology + linux => if host == synology... # fn's ausser unmount liefern string|null => if -z error # do_export() { log $DEBUG "$DBG do_export ()" mail_log $INFO "$INF checking USB device." # get device local device=$(get_usb_device) if [ -z $device ]; then return 1 fi # check if exfat | mount & get mount point local mount_point=$(mount_usb_device $device) if [ -z $mount_point ]; then return 1 fi # find export type local backup_type=$(check_backup_device $mount_point) if [ -z $backup_type ]; then return 1 fi mail_log $INFO "$SUCCESS" local source_path case $backup_type in int1|int2|1|2) # daily + weekly backups -> Auslagerung 1+2, Backup 1+2 if is_synology; then source_path="/volume1/nfs/backups" else source_path="/var/srv/nas/backups" fi ;; serien) # Serien -> Auslagerung if is_synology; then source_path="/volume1/media/serien" else source_path="/var/srv/media/serien" fi ;; video) # Video -> Auslagerung if is_synology; then source_path="/volume1/media/video" else source_path="/var/srv/media/video" fi ;; ?*) echo "ERROR unknown backup type $backup_type" return 1 ;; esac mail_log $INFO ">>> " mail_log $INFO ">>> export type : $backup_type" mail_log $INFO ">>> export from : $source_path" mail_log $INFO ">>> log file : $LOG_FILE" mail_log $INFO ">>> " mail_log $INFO "$INF exporting $source_path -> $mount_point" log $DEBUG "$DBG rsync -rti --one-file-system --delete --stats --exclude=@eaDir --exclude='*'.$LIST_EXT $source_path $mount_point" rsync -rti --one-file-system --delete --stats --exclude=@eaDir --exclude='*'.$LIST_EXT $source_path $mount_point >>$LOG_FILE 2>&1 is_error $? mail_log $INFO "$INF unmounting USB device $device." umount_usb_device $device is_error $? } # Export Backups bzw. Medien-Verzeichnisse auf USB-HDD # # Der Export wird nur auf der NAS ausgeführt # (und wird ggf. dort remote gestartet) # export_backups() { if [ "$HOST_NAME" != "$NAS_HOSTNAME" ]; then run_script_remote $NAS_IP "$NAS_SCRIPT -e" exit fi mail_log $INFO ">>>>>>>>>> export $VER <<<<<<<<<<" do_export mail_log $INFO ">>>>>>>>>> end of export" send_mail_log "Export" }