feat: using a more unified structure for the different Backup options

This commit is contained in:
Peritia 2025-09-26 13:23:13 +02:00
parent 0a6c6bb70d
commit f4aca9b1a0

View file

@ -20,6 +20,9 @@ PV_BIN="@PV_BIN@"
DU_BIN="@DU_BIN@" DU_BIN="@DU_BIN@"
BZIP2_BIN="@BZIP2_BIN@" BZIP2_BIN="@BZIP2_BIN@"
XZ_BIN="@XZ_BIN@" XZ_BIN="@XZ_BIN@"
WC_BIN="@WC_BIN@"
FIND_BIN="@FIND_BIN@"
# Convenience wrappers # Convenience wrappers
rsync_cmd="$RSYNC_BIN" rsync_cmd="$RSYNC_BIN"
@ -35,11 +38,29 @@ pv_cmd="$PV_BIN"
du_cmd="$DU_BIN" du_cmd="$DU_BIN"
bzip2_cmd="$BZIP2_BIN" bzip2_cmd="$BZIP2_BIN"
xz_cmd="$XZ_BIN" xz_cmd="$XZ_BIN"
wc_cmd="$WC_BIN"
find_cmd="$FIND_BIN"
# PATH extension # PATH extension
# (only figured that out later if you add it here it can actually just use the bin) # (only figured that out later if you add it here it can actually just use the bin)
# So you can easily just switch out the "*_cmd" with the "normal" name # So you can easily just switch out the "*_cmd" with the "normal" name
export PATH="$(dirname "$GZIP_BIN")":"$(dirname "$ZSTD_BIN")":"$(dirname "$PV_BIN")":"$(dirname "$DU_BIN")":"$(dirname "$BZIP2_BIN")":"$(dirname "$XZ_BIN")":"$PATH" # Extend PATH with all injected binaries so we can call them directly
for bin in \
"$GZIP_BIN" \
"$ZSTD_BIN" \
"$PV_BIN" \
"$DU_BIN" \
"$BZIP2_BIN" \
"$XZ_BIN" \
"$WC_BIN" \
"$FIND_BIN"
do
export PATH="$(dirname "$bin"):$PATH"
done
# Defaults # Defaults
REBOOT=false REBOOT=false
@ -49,15 +70,17 @@ DESTINATION="/srv/minecraft/backups/unknown"
PURE=false PURE=false
FORMAT="tar" FORMAT="tar"
COMPRESSION="gzip" COMPRESSION="gzip"
PROGRESS_INTERVAL=5 # default to 5 seconds
# Usage # Usage
usage() { usage() {
cat <<EOF cat <<EOF
Usage: $0 [--reboot] [--sleep <seconds>] [--full] [--destination <path>] [--pure] Usage: $0 [--reboot] [--sleep <seconds>] [--full] [--destination <path>] [--pure]
[--format <tar|zip>] [--compression <gzip|bzip2|xz|zstd>] [--format <tar|zip>] [--compression <gzip|bzip2|xz|zstd>] [--progressInterval <seconds>]
--reboot Stop server before backup and start afterwards [DOES NOT WORK] --reboot Stop server before backup and start afterwards [DOES NOT WORK]
--sleep N Wait N seconds with countdown announcements --sleep N Wait N seconds with countdown announcements
--progressInterval N Wait N seconds with interval announcements (default: 5)
--full Backup entire server directory (default: world only) --full Backup entire server directory (default: world only)
--destination X Backup target directory (default: /srv/minecraft/backups/unknown) --destination X Backup target directory (default: /srv/minecraft/backups/unknown)
--pure Use rsync to copy files (no compression, symlinks resolved) --pure Use rsync to copy files (no compression, symlinks resolved)
@ -78,6 +101,7 @@ while [[ $# -gt 0 ]]; do
--pure) echo "[DEBUG] Flag: --pure"; PURE=true; shift ;; --pure) echo "[DEBUG] Flag: --pure"; PURE=true; shift ;;
--format) echo "[DEBUG] Flag: --format $2"; FORMAT="$2"; shift 2 ;; --format) echo "[DEBUG] Flag: --format $2"; FORMAT="$2"; shift 2 ;;
--compression) echo "[DEBUG] Flag: --compression $2"; COMPRESSION="$2"; shift 2 ;; --compression) echo "[DEBUG] Flag: --compression $2"; COMPRESSION="$2"; shift 2 ;;
--progressInterval) echo "[DEBUG] Flag: --progressInterval $2"; PROGRESS_INTERVAL="$2"; shift 2 ;;
--help) usage ;; --help) usage ;;
*) echo "[ERROR] Unknown option: $1"; usage ;; *) echo "[ERROR] Unknown option: $1"; usage ;;
esac esac
@ -170,215 +194,155 @@ countdown() {
do_backup() { do_backup() {
#echo "[DEBUG] Entering do_backup with args: $*" local backup_source=""
local source="" local backup_destination=""
local destination="" local backup_compression="gzip"
local compression="gzip" local backup_format="tar"
local format="tar" local backup_pure=false
local pure=false local progress_interval=$PROGRESS_INTERVAL
# parse args # Parse args
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--source) source="$2"; shift 2 ;; --source) backup_source="$2"; shift 2 ;;
--destination) destination="$2"; shift 2 ;; --destination) backup_destination="$2"; shift 2 ;;
--compression) compression="$2"; shift 2 ;; --compression) backup_compression="$2"; shift 2 ;;
--format) format="$2"; shift 2 ;; --format) backup_format="$2"; shift 2 ;;
--pure) pure=true; shift ;; --pure) backup_pure=true; shift ;;
*) echo "[ERROR] Unknown option to do_backup: $1"; return 1 ;; *) echo "[ERROR] Unknown option to do_backup: $1"; return 1 ;;
esac esac
done done
if [[ -z "$backup_source" || -z "$backup_destination" ]]; then
if [[ -z "$source" || -z "$destination" ]]; then
echo "[ERROR] Missing --source or --destination" echo "[ERROR] Missing --source or --destination"
return 1 return 1
fi fi
local timestamp="$(date +%Y%m%d-%H%M%S)" local timestamp="$(date +%Y%m%d-%H%M%S)"
local full_source="$DATA_DIR/$source" local full_source_path="$DATA_DIR/$backup_source"
local basename="$(basename "$source")" local source_basename="$(basename "$backup_source")"
local archive="" local archive_path=""
local ext="" local archive_ext=""
if [[ ! -d "$full_source_path" ]]; then
if [[ ! -d "$full_source" ]]; then echo "[ERROR] Source directory not found: $full_source_path"
echo "[ERROR] Source directory not found: $full_source"
return 1 return 1
fi fi
mkdir -p "$destination" mkdir -p "$backup_destination"
if [[ "$pure" == true ]]; then # PURE (rsync) backup
local target_dir="$destination/${basename}-${timestamp}" if [[ "$backup_pure" == true ]]; then
echo "[INFO] Performing pure rsync backup to $target_dir" local target_path="$backup_destination/${source_basename}-${timestamp}"
echo "#####" echo "[INFO] Performing pure rsync backup to $target_path"
local last_percentage=-1 local progress_last_percent=-1
local progress_last_time=$(date +%s)
"$rsync_cmd" -rptgoDL --delete --info=progress2 --stats \ "$rsync_cmd" -rptgoDL --delete --info=progress2 --stats \
"$full_source/" "$target_dir/" 2>&1 | \ "$full_source_path/" "$target_path/" 2>&1 | \
while IFS= read -r -d $'\r' chunk; do while IFS= read -r -d $'\r' chunk; do
# Extract percentage
if [[ $chunk =~ ([0-9]{1,3})% ]]; then if [[ $chunk =~ ([0-9]{1,3})% ]]; then
current_percentage=${BASH_REMATCH[1]} local progress_percent=${BASH_REMATCH[1]}
# Print only if percentage changed local now=$(date +%s)
if [[ $current_percentage -ne $last_percentage ]]; then if [[ $progress_percent -ne $progress_last_percent ]] && \
echo -e "Progress: ${current_percentage}%\r" | say "$@" (( now - progress_last_time >= progress_interval )); then
last_percentage=$current_percentage say "Progress: ${progress_percent}%"
progress_last_percent=$progress_percent
progress_last_time=$now
fi fi
fi fi
done done
return 0 return 0
fi fi
#echo "[DEBUG] Using archive mode: format=$format compression=$compression" # ARCHIVE backup
# Archive/compression backup case "$backup_format" in
case "$format" in
tar) tar)
# Map compression → extension and tar invocation (using only *_cmd vars) case "$backup_compression" in
case "$compression" in none) archive_ext="tar"; tar_create=( "$tar_cmd" -cvf ) ;;
none) gzip) archive_ext="tar.gz"; tar_create=( "$tar_cmd" --use-compress-program="$gzip_cmd" -cvf ) ;;
ext="tar" bzip2) archive_ext="tar.bz2";tar_create=( "$tar_cmd" --use-compress-program="$bzip2_cmd" -cvf ) ;;
tar_create=( "$tar_cmd" -cvf ) xz) archive_ext="tar.xz"; tar_create=( "$tar_cmd" --use-compress-program="$xz_cmd" -cvf ) ;;
;; zstd) archive_ext="tar.zst";tar_create=( "$tar_cmd" --use-compress-program="$zstd_cmd" -cvf ) ;;
gzip) *) echo "[ERROR] Unsupported tar compression: $backup_compression" >&2; return 1 ;;
ext="tar.gz"
tar_create=( "$tar_cmd" --use-compress-program="$gzip_cmd" -cvf )
;;
bzip2)
ext="tar.bz2"
tar_create=( "$tar_cmd" --use-compress-program="$bzip2_cmd" -cvf )
;;
xz)
ext="tar.xz"
tar_create=( "$tar_cmd" --use-compress-program="$xz_cmd" -cvf )
;;
zstd)
ext="tar.zst"
tar_create=( "$tar_cmd" --use-compress-program="$zstd_cmd" -cvf )
;;
*)
echo "[ERROR] Unsupported tar compression: $compression" >&2
exit 1
;;
esac esac
archive="$destination/${basename}-${timestamp}.${ext}" archive_path="$backup_destination/${source_basename}-${timestamp}.${archive_ext}"
sources=( $source ) echo "[INFO] Creating tar archive $archive_path"
for s in "${sources[@]}"; do
if [[ ! -e "$DATA_DIR/$s" ]]; then
echo "[ERROR] Source not found under DATA_DIR: $DATA_DIR/$s" >&2
exit 1
fi
done
# Count total items for progress (files + dirs) local progress_total=$(find "$full_source_path" | wc -l)
total_items=0 local progress_done=0
for s in "${sources[@]}"; do local progress_last_percent=-1
count=$(find "$DATA_DIR/$s" | wc -l) local progress_last_time=$(date +%s)
total_items=$(( total_items + count ))
done
current=0
echo "[INFO] Creating $archive"
last_percent=-1
last_info_time=$(date +%s)
# seconds between info logs
interval=2
( (
"${tar_create[@]}" "$archive" -C "$DATA_DIR" "${sources[@]}" "${tar_create[@]}" "$archive_path" -C "$DATA_DIR" "$backup_source"
) 2>&1 | while read -r line; do ) 2>&1 | while read -r line; do
if [[ -n "$line" ]]; then if [[ -n "$line" ]]; then
current=$(( current + 1 )) progress_done=$(( progress_done + 1 ))
if (( total_items > 0 )); then if (( progress_total > 0 )); then
percent=$(( current * 100 / total_items )) local progress_percent=$(( progress_done * 100 / progress_total ))
local now=$(date +%s)
# echo full percent: if (( progress_percent != progress_last_percent )) && \
#echo "[DEBUG] Progress: ${percent}%" (( now - progress_last_time >= progress_interval )); then
now=$(date +%s) say "Progress: ${progress_percent}%"
if (( percent != last_percent )) && (( now - last_info_time >= interval )); then progress_last_percent=$progress_percent
#echo "[INFO] Progress: ${percent}%" progress_last_time=$now
say "Progress: ${percent}%"
last_percent=$percent
last_info_time=$now
fi fi
fi fi
fi fi
done done
# Ensure 100% gets printed once at the end [[ $progress_last_percent -lt 100 ]] && say "Progress: 100%"
if (( last_percent < 100 )); then echo "[INFO] Tar archive created: $archive_path"
#echo "[INFO] Progress: 100%"
say "Progress: 100%"
fi
echo "[INFO] Tar archive created: $archive"
;; ;;
zip) zip)
ext="zip" archive_path="$backup_destination/${source_basename}-${timestamp}.zip"
archive="$destination/${basename}-${timestamp}.${ext}" echo "[INFO] Creating zip archive $archive_path"
echo "[INFO] Creating zip archive $archive"
# Count both files and directories local progress_total=$(find "$full_source_path" | wc -l)
total_items=$(find "$DATA_DIR/$source" | wc -l) local progress_done=0
current=0 local progress_last_percent=-1
local progress_last_time=$(date +%s)
last_percent=-1
last_info_time=$(date +%s)
interval=2 # seconds between info logs
( (
cd "$DATA_DIR" cd "$DATA_DIR"
"$zip_cmd" -r "$archive" "$source" "$zip_cmd" -r "$archive_path" "$backup_source"
) 2>&1 | while read -r line; do ) 2>&1 | while read -r line; do
if [[ $line =~ adding: ]]; then if [[ $line =~ adding: ]]; then
current=$((current+1)) progress_done=$(( progress_done + 1 ))
if (( total_items > 0 )); then if (( progress_total > 0 )); then
percent=$(( current * 100 / total_items )) local progress_percent=$(( progress_done * 100 / progress_total ))
local now=$(date +%s)
#echo "[DEBUG] Progress: ${percent}%" if (( progress_percent != progress_last_percent )) && \
(( now - progress_last_time >= progress_interval )); then
now=$(date +%s) say "Progress: ${progress_percent}%"
if (( percent != last_percent )) && (( now - last_info_time >= interval )); then progress_last_percent=$progress_percent
#echo "[INFO] Progress: ${percent}%" progress_last_time=$now
say "Progress: ${percent}%"
last_percent=$percent
last_info_time=$now
fi fi
fi fi
fi fi
done done
# Ensure 100% gets printed once at the end [[ $progress_last_percent -lt 100 ]] && say "Progress: 100%"
if (( last_percent < 100 )); then echo "[INFO] Zip archive created: $archive_path"
#echo "[INFO] Progress: 100%"
say "Progress: 100%"
fi
echo "[INFO] Zip archive created: $archive"
;; ;;
*) *)
echo "[ERROR] Unsupported format: $format" echo "[ERROR] Unsupported format: $backup_format"
return 1 return 1
;; ;;
esac esac
echo "[INFO] Backup completed: $archive" echo "[INFO] Backup completed: $archive_path"
return 0 return 0
} }
# MAIN # MAIN
#echo "[DEBUG] FULL=$FULL" #echo "[DEBUG] FULL=$FULL"
@ -400,7 +364,6 @@ if (( SLEEP_TIME > 0 )); then
countdown "$SLEEP_TIME" countdown "$SLEEP_TIME"
fi fi
mkdir -p "$DESTINATION" mkdir -p "$DESTINATION"
echo "[INFO] Running backup of $BACKUP_MODE to $DESTINATION..." echo "[INFO] Running backup of $BACKUP_MODE to $DESTINATION..."
@ -416,5 +379,4 @@ else
exit 1 exit 1
fi fi
say "Backup ($BACKUP_MODE) completed successfully." say "Backup ($BACKUP_MODE) completed successfully."