401 lines
10 KiB
Bash
Executable File
401 lines
10 KiB
Bash
Executable File
VER="2.0.0-beta-4"
|
|
|
|
HOST_NAME=$(hostname)
|
|
DAY=$(date +%u)
|
|
|
|
PASSPHRASE=1234567890
|
|
EXT=tgz.gpg
|
|
LIST_EXT=txt
|
|
|
|
INFO=INFO
|
|
ERROR=ERROR
|
|
DEBUG=DEBUG
|
|
SUCCESS="*** success"
|
|
INF="---"
|
|
ERR="###"
|
|
DBG="==="
|
|
|
|
# docker compose ausführen
|
|
#
|
|
# $1 ff. - compose parameter, z.B. up -d
|
|
#
|
|
run_docker() {
|
|
log $DEBUG "$DBG run_docker $@"
|
|
|
|
#DEBUG ohne docker stop/start
|
|
return 0
|
|
|
|
log $DEBUG "$DBG > /usr/bin/docker compose -f /var/docker/run/docker-compose.yml --ansi never $@"
|
|
/usr/bin/docker compose -f /var/docker/run/docker-compose.yml --ansi never $@ >>$LOG_FILE 2>&1
|
|
return $?
|
|
}
|
|
|
|
start_docker() {
|
|
log $DEBUG "$DBG start_docker"
|
|
return $(run_docker up -d)
|
|
}
|
|
|
|
stop_docker() {
|
|
log $DEBUG "$DBG stop_docker"
|
|
return $(run_docker down)
|
|
}
|
|
|
|
# check if function $1 exists
|
|
#
|
|
# returns true/false
|
|
#
|
|
isFunction() { declare -Ff "$1" ; }
|
|
|
|
# Log-Datei schreiben.
|
|
#
|
|
# Die Ausgabe geht auch auf STDOUT, dadurch kann die vollständige Ausgabe,
|
|
# einschließlich Status- und Fehlermeldungen der Programme in ein erweitertes
|
|
# Log für die Fehlersuche umgeleitet werden.
|
|
#
|
|
# $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
|
|
|
|
}
|
|
|
|
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" "$@"
|
|
}
|
|
|
|
# check if $1 is error (!=0) and write result to log
|
|
#
|
|
# $1 - return code to check
|
|
# $2 - if set don't log success
|
|
#
|
|
# returns 0 if error, 1 otherwise!
|
|
#
|
|
is_error() {
|
|
local rc=$1
|
|
local mlog=$2
|
|
if [ $rc -eq 0 ]; then
|
|
if [ "$mlog" ]; then
|
|
mail_log $INFO "$SUCCESS"
|
|
fi
|
|
return 1
|
|
else
|
|
mail_log $ERROR "$ERR an error occured see $LOG_FILE"
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# check return code of piped programs
|
|
#
|
|
# $1, ff. - return codes
|
|
#
|
|
check_pipe_rc() {
|
|
local result=0
|
|
local count=1
|
|
local result_str
|
|
for rc in $@; do
|
|
if [ $rc -ne 0 ] ; then
|
|
result=1
|
|
result_str="${result_str}#${count} "
|
|
fi
|
|
let "count += 1"
|
|
done
|
|
if [ $result -ne 0 ]; then
|
|
log $ERROR "$ERR in pipe $result_str"
|
|
fi
|
|
return $result
|
|
}
|
|
|
|
# Dateien vom Server zur NAS verschieben
|
|
#
|
|
# $1 - Verzeichnis auf Server
|
|
# $2 - Ziel-Verzeichnis auf der NAS
|
|
#
|
|
copy_to_nas() {
|
|
log $DEBUG "$DBG copy_to_nas $@"
|
|
local source="$1"
|
|
local target="$2"
|
|
delete_dir "$target"
|
|
if [ $(check_directory "$target") -gt 0 ]; then
|
|
log $ERROR "$ERR directory \"$target\" not found"
|
|
return 1
|
|
fi
|
|
log $DEBUG "$DBG > 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
|
|
#
|
|
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
|
|
#
|
|
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
|
|
}
|
|
|
|
# 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)
|
|
#
|
|
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"
|
|
mail_log $INFO "$INF directory: $root/$dir"
|
|
backup_dir "$root" "$tar_file" "$dir"
|
|
is_error $? false && return 1
|
|
|
|
#mail_log $INFO "$INF list directory"
|
|
create_list "$tar_file" "$dir"
|
|
is_error $? false && return 1
|
|
|
|
#mail_log $INFO "$INF test integrity of $tar_file"
|
|
test_integrity "$tar_file"
|
|
is_error $? false && return 1
|
|
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
|
|
#
|
|
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 --exclude \"@eaDir\" -C \"$root\" \"$dir\" | gpg --batch -c --passphrase xxxx -o \"$archive\""
|
|
tar -c -z --numeric-owner --one-file-system --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
|
|
#
|
|
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
|
|
mail_log $ERROR "$ERR keine Backup-Function $func$count() für $HOST_NAME."
|
|
exit 1
|
|
fi
|
|
|
|
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[@]}"
|
|
rc=$?
|
|
# error logging in backup_all_dirs
|
|
if [ $rc -gt 0 ]; then
|
|
return 1
|
|
fi
|
|
|
|
let "count += 1"
|
|
done
|
|
mail_log $INFO "$INF finishing backup"
|
|
after_backup
|
|
is_error $? && return 1
|
|
}
|
|
|
|
backup_daily() {
|
|
mail_log $INFO ">>>>>>>>>> daily auto backup $VER for $HOST_NAME <<<<<<<<<<"
|
|
do_auto_backup backup_daily
|
|
mail_log $INFO ">>>>>>>>>> end of auto backup"
|
|
# TODO mail log
|
|
}
|
|
backup_weekly() {
|
|
mail_log $INFO ">>>>>>>>>> weekly auto backup $VER for $HOST_NAME <<<<<<<<<<"
|
|
do_auto_backuo backup_weekly
|
|
mail_log $INFO ">>>>>>>>>> end of auto backup"
|
|
# TODO mail log
|
|
}
|
|
|
|
export_backups() {
|
|
# TODO
|
|
echo
|
|
# check host (only nathan|hamiller?)
|
|
# check USB drive
|
|
# del USB drive (?)
|
|
# rsync USB -> 1|2, video, serien
|
|
# mail log
|
|
}
|
|
|
|
# Inhalt des Archivs in eine Textdatei ausgeben.
|
|
#
|
|
# $1 - Archiv
|
|
#
|
|
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 $?
|
|
}
|
|
|
|
# Integrität des Archivs testen, indem es entschlüsselt, entpackt
|
|
# und der Inhalt auf /dev/null ausgegeben wird.
|
|
#
|
|
# $1 - Archiv
|
|
#
|
|
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 aus Archiv
|
|
#
|
|
# Wird nach pwd ausgegeben!
|
|
#
|
|
# $1 - Archiv, ggf. mit Pfad
|
|
# $2 ff. - zurückzusichernde Dateien/Verzeichnisse
|
|
#
|
|
restore() {
|
|
log $DEBUG "$DBG restore $@"
|
|
local archive=$1
|
|
shift 1
|
|
log $DEBUG "$DBG > gpg --batch --decrypt --passphrase xxxx \"$archive\" | tar -xzv $@"
|
|
gpg --batch --decrypt --passphrase $PASSPHRASE "$archive" 2>>$LOG_FILE | tar -xzv $@ >/dev/null 2>>$LOG_FILE
|
|
check_pipe_rc ${PIPESTATUS[@]}
|
|
return $?
|
|
}
|
|
|
|
# Liste der Archives auf stdout ausgeben
|
|
#
|
|
# Zeigt alle Archive incl. Verzeichnis (ohne $1) an
|
|
#
|
|
# z.B.
|
|
# $1 = /volume1/nfs/backups
|
|
# ...
|
|
# /daily/synology/backup.3/volume1-nfs-Server.tgz.gpg
|
|
# /weekly/synology/volume1-media-books.tgz.gpg
|
|
#
|
|
# $1 - Verzeichnis unter dem alle Backups liegen
|
|
#
|
|
list_archives() {
|
|
log $DEBUG "$DBG list_archives $@"
|
|
local target=$1
|
|
local targ=${target//\//\\\/} # slashes escapen
|
|
log $DEBUG "$DBG > find \"$target\" -type f -name \"*.$EXT\" | grep \"${HOST_NAME}/\" | sed -E \"s/^$targ//\""
|
|
find "$target" -type f -name "*.$EXT" 2>>$LOG_FILE | grep "${HOST_NAME}/" 2>>$LOG_FILE | sed -E "s/^$targ//" 2>>$LOG_FILE
|
|
check_pipe_rc ${PIPESTATUS[@]}
|
|
return $?
|
|
}
|
|
|
|
# Inhalt des Archivs $2 auf stdout ausgeben
|
|
#
|
|
# $1 - Verzeichnis unter dem alle Backups liegen
|
|
# $2 - Archiv-Datei mit Pfad ab $1
|
|
#
|
|
list_archive_content() {
|
|
log $DEBUG "$DBG list_archive_content $@"
|
|
local root=$1
|
|
local archive=$2
|
|
log $DEBUG "$DBG > cat \"$root/${archive/#\//}.$LIST_EXT\""
|
|
cat "$root/${archive/#\//}.$LIST_EXT" 2>>$LOG_FILE
|
|
}
|