Files
SODEW/sodew-bash-main/libs/commands/backup.sh
T
2026-08-18 09:40:08 +02:00

384 lines
15 KiB
Bash

backupLabel="[Backup]"
# Creates archive-based backups for all OpenLiteSpeed virtual hosts.
# [$1] (path): destination directory.
# [$2] (type): archive type: zip or tar.
function cmdBackupSitesArchive() {
local path
path=$(backupPathGenerate "$1")
local type="${2:-$backupType}"
local error vhostList total
run vhostList error openlitespeedConfigVhostList || { printDanger "Failed get list of vhosts: $error"; return 1; }
total=$(grep -c . <<< "$vhostList")
printSection "Config"
printDotText "Target" "all ($total)"
printDotText "Type" "$type"
printDotText "Path" "$path"
printSection "Executing"
local domain label num i=0 lockFile tmpDir
maxJobs="${backupParallelJobs:-1}"
tmpDir=$(mktemp -d) || { printDanger "Failed to create temp directory"; return 1; }
lockFile="$tmpDir/.lock"
while read -r domain; do
((i++))
num=$(printf "%0${#total}d" "$i")
label="$num: $fontBlue$domain$fontReset"
while (( $(jobs -rp | wc -l) >= maxJobs )); do
wait -n 2>/dev/null || true
done
(
local jobError
if runError jobError backupSite "$domain" "$path" "$type"; then
{ flock 9; printDotText "$label" "$labelDone"; } 9>>"$lockFile"
else
touch "$tmpDir/$i"
{ flock 9; printDotText "$label" "$labelFail"; printDanger "$jobError"; } 9>>"$lockFile"
fi
) &
done < <(awk 'NF' <<< "$vhostList")
wait
local failed=0 f
for f in "$tmpDir"/[0-9]*; do
[[ -f "$f" ]] || break
((failed++))
done
rm -rf "$tmpDir"
printSection "Summary"
printDotText "Total" "$fontBlue$total$fontReset"
printDotText "Successful" "$fontGreen$((total-failed))$fontReset"
printDotText "Failed" "$fontRed$failed$fontReset"
}
# Creates rsync-based backups for all sites; first daily backup is full, later ones use hard links.
# [$1] (path): destination directory.
function cmdBackupSitesRsync() {
local path
path=$(backupPathGenerate "$1")
printSection "Config"
printDotText "Target" "all"
printDotText "Path" "$path"
local error vhostList
run vhostList error openlitespeedConfigVhostList || { printDanger "Failed get list of vhosts: $error"; return 1; }
printText "Files..."
if [[ "$path" == *"$backupFirstDir" ]]; then
runError error rsync -a --mkpath -- "$olsVhostsPath/" "$path" || printDanger "$error"
else
runError error rsync -a --link-dest="$backupDailyPath/$backupFirstDir" -- "$olsVhostsPath/" "$path" || printDanger "$error"
fi
printText "Databases..."
while read -r domain; do
runError error backupSiteDatabase "$domain" "$path/$domain.sql.tar.gz" || printDanger "$error"
done < <(awk 'NF' <<< "$vhostList")
printText "Configs..."
while read -r domain; do
runError error cp -p -- "$olsVhostsConfigPath/$domain.conf" "$path/$domain.conf" || printDanger "$error"
done < <(awk 'NF' <<< "$vhostList")
}
# Runs a backup for all sites using the configured or given backup type.
# [$1] (path): destination directory.
# [$2] (type): backup type: zip, tar, or rsync.
function cmdBackupSites() {
local path="$1"
path=$(backupPathGenerate "$path")
local type="${2:-$backupType}"
case "$type" in
zip|tar)
cmdBackupSitesArchive "$path" "$type" || return 1
;;
rsync)
cmdBackupSitesRsync "$path" || return 1
;;
*)
printSection "Config"
printDanger "Unknown backup type: $type"
return 1
;;
esac
}
function cmdBackupArchiveDispatch() {
printSection "Config"
[[ -n "$backupArchivePath" ]] || { printDotText "Path" "$labelNull"; return 1; }
printDotText "Path" "$backupArchivePath"
[[ -n "$backupArchiveInterval" ]] || { printDotText "Period" "$labelNull"; return 1; }
printDotText "Period" "$backupArchiveInterval"
[[ -n "$backupArchiveDirPattern" ]] || { printDotText "Pattern" "$labelNull"; return 1; }
printDotText "Pattern" "$backupArchiveDirPattern"
mkdir -p -- "$backupArchivePath" || { printDanger "Failed to create archive path"; return 1; }
local dirList archiveList dailyList error
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
printSection "Directories found"
local archiveCount archiveRemoveCount i=0 num label dirDate dirDays archiveRemoveList=""
archiveCount=$(grep -c . <<< "$archiveList" || true)
if [[ "$archiveCount" -gt 0 ]]; then
while read -r dir; do
((++i))
num=$(printf "%0${#archiveCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if (( i == archiveCount )); then
printDotText "$label" "${fontGreen}save$fontReset"
elif (( i == archiveCount - 1 )); then
dirDate="${dir%$backupArchiveSuffix}"
dirDays=$(( $(timeDiff "$dirDate" "$appDate") / 86400 ))
if (( dirDays >= backupArchiveInterval )); then
printDotText "$label" "${fontRed}delete$fontReset"
archiveRemoveList+=$'\n'"$dir"
else
printDotText "$label" "${fontGreen}save$fontReset"
fi
else
printDotText "$label" "${fontRed}delete$fontReset"
archiveRemoveList+=$'\n'"$dir"
fi
done < <(awk 'NF' <<< "$archiveList")
archiveRemoveCount=$(grep -c . <<< "$archiveRemoveList" || true)
if [[ "$archiveRemoveCount" -gt 0 ]]; then
printSection "Deleting Directories"
while read -r dir; do
label="$backupArchivePath/$dir"
if [[ -n "$dir" ]]; then
if rm -rf -- "$backupArchivePath/$dir" &>/dev/null; then
printDotText "$label" "$labelDone"
else
printDotText "$label" "$labelFail"
fi
fi
done < <(awk 'NF' <<< "$archiveRemoveList")
fi
fi
# Promote oldest daily (excluding today) to archive
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
archiveCount=$(grep -c . <<< "$archiveList" || true)
if (( archiveCount < 2 )); then
local oldestDaily
oldestDaily=$(head -1 <<< "$dailyList" || true)
[[ -n "$oldestDaily" ]] || return 0
[[ "$oldestDaily" != "$appDate" ]] || return 0
printSection "Promote Directories"
mv -- "$backupDailyPath/$oldestDaily" "$backupArchivePath/${oldestDaily}$backupArchiveSuffix" || {
printDotText "$backupDailyPath/$oldestDaily" "$labelFail"
printDanger "Failed to promote daily to archive: $oldestDaily"
return 1
}
printDotText "$backupDailyPath/$oldestDaily" "$labelDone"
fi
}
function cmdBackupDailyDispatch() {
printSection "Config"
[[ -n "$backupDailyPath" ]] || { printDotText "Path" "$labelNull"; return 1; }
printDotText "Path" "$backupDailyPath"
[[ -n "$backupDailyKeep" ]] || { printDotText "Keep" "$labelNull"; return 1; }
printDotText "Keep" "$backupDailyKeep"
[[ -n "$backupDailyDirPattern" ]] || { printDotText "Pattern" "$labelNull"; return 1; }
printDotText "Pattern" "$backupDailyDirPattern"
mkdir -p -- "$backupDailyPath" || { printDanger "Failed to create archive path"; return 1; }
local dirList dailyList error
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -v "$appDate" | grep -E -- "$backupDailyDirPattern" | sort || true)
printSection "Directories found"
local dailyCount dailyRemoveCount i=0 num label dailyRemoveList=""
dailyCount=$(grep -c . <<< "$dailyList" || true)
if [[ "$dailyCount" -gt 0 ]]; then
while read -r dir; do
((++i))
num=$(printf "%0${#dailyCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if [[ "$dir" == "$appDate" ]]; then
printDotText "$label" "${fontGray}skipped$fontReset"
elif (( dailyCount - backupDailyKeep >= i )); then
printDotText "$label" "${fontRed}delete$fontReset"
dailyRemoveList+=$'\n'"$dir"
else
printDotText "$label" "${fontGreen}save$fontReset"
fi
done < <(awk 'NF' <<< "$dailyList")
dailyRemoveCount=$(grep -c . <<< "$dailyRemoveList" || true)
if [[ "$dailyRemoveCount" -gt 0 ]]; then
printSection "Deleting Directories"
while read -r dir; do
label="$backupDailyPath/$dir"
if [[ -n "$dir" ]]; then
if rm -rf -- "$backupDailyPath/$dir" &>/dev/null; then
printDotText "$label" "$labelDone"
else
printDotText "$label" "$labelFail"
fi
fi
done < <(awk 'NF' <<< "$dailyRemoveList")
fi
fi
}
function cmdBackupDispatch() {
local second=0 interval=0
printText "$fontMagenta[Step 1 Processing of archive backups on external storage]$fontReset"
cmdStorageBackupArchiveDispatch
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 2 Processing of daily backups on external storage]$fontReset"
cmdStorageBackupDailyDispatch
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 3 Processing of archive backups on host (SKIP...)]$fontReset"
# cmdBackupArchiveDispatch
# seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
# printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
# interval="$seconds"
printRow
printText "$fontMagenta[Step 4 Processing of daily backups on host]$fontReset"
cmdBackupDailyDispatch
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 5 Creating a full daily backup]$fontReset"
cmdBackupSites
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 6 Synchronization with external storage (daily backups)]$fontReset"
cmdStorageBackupDailySyncLast
seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
interval="$seconds"
printRow
printText "$fontMagenta[Step 7 Synchronization with external storage (archive backups) (SKIP...)]$fontReset"
# cmdStorageBackupArchiveSyncLast
# seconds=$(timeDiff "$appDate ${appTime//-/:}" "$(date +%Y-%m-%d) $(date +%H:%M:%S)")
# printDotText "Complete" "$(date +%Y-%m-%d) $(date +%H:%M:%S) $fontBlue$((seconds-interval))s$fontReset"
}
# Runs a backup for a single site or all sites.
# [$1] (target): site domain or "all".
# [$2] (path): destination directory.
# [$3] (type): backup type.
function cmdBackupRun() {
local target="$1"
[[ -n "$target" ]] || { printRow; printDanger "Target not specified"; return 1; }
local path="$2"
path=$(backupPathGenerate "$path")
local type="${3:-$backupType}"
if [[ "$target" == "all" ]]; then
cmdBackupSites "$path" "$type" || return 1
else
printSection "Config"
printDotText "Target" "$target"
printDotText "Type" "$type"
printDotText "Path" "$path"
printSection "Executing"
local label error
if runError error backupSite "$target" "$path" "$type"; then
printDotText "$target" "$labelDone"
else
printDotText "$target" "$labelFail"
printDanger "$error"
fi
fi
}
function cmdBackupList() {
printRow
local dirList archiveList dailyList backupList backupCount error
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
backupList=$(printf '%s\n' "$archiveList" "$dailyList" | awk 'NF' | sort)
backupCount=$(grep -c . <<< "$backupList" || true)
local i=0 num dir label
while read -r dir; do
((++i))
num=$(printf "%0${#backupCount}d" "$i")
label="$num: $fontBlue$dir$fontReset"
if listContains "$dir" "$archiveList"; then
printDotText "$label" "${fontGreen}archive$fontReset"
elif listContains "$dir" "$dailyList"; then
printDotText "$label" "${fontGreen}daily$fontReset"
else
printDotText "$label" "$labelUnknown"
fi
done < <(awk 'NF' <<< "$backupList")
}
function cmdBackupSize() {
local dirList file size total=0 archiveList dailyList
printSection "Archive: $backupArchivePath"
run dirList error fileList "$backupArchivePath" d || { printDanger "$error"; return 1; }
archiveList=$(printf '%s\n' "$dirList" | grep -E -- "$backupArchiveDirPattern" | sort || true)
while IFS= read -r file; do
size=$(pathSize "$backupArchivePath/$file" "gb")
printDotText "$file" "$size Gb"
(( total += size ))
done <<< "$archiveList"
printSection "Daily: $backupDailyPath"
run dirList error fileList "$backupDailyPath" d || { printDanger "$error"; return 1; }
dailyList=$(printf '%s\n' "$dirList" | grep -E -- "$backupDailyDirPattern" | sort || true)
while IFS= read -r file; do
size=$(pathSize "$backupDailyPath/$file" "gb")
printDotText "$file" "$size Gb"
(( total += size ))
done <<< "$dailyList"
printRow
printDotText "total" "$total Gb"
}