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" }