From 55f9fe9a0ce6ee86b27a2101350f674b26650bd2 Mon Sep 17 00:00:00 2001 From: Jens Bannmann <jens.b@web.de> Date: Thu, 26 Mar 2020 17:39:59 +0100 Subject: [PATCH] Add option to create full backups instead of per-target --- acts | 122 ++++++++++++++++++++++++++++++++--------------- acts.conf.sample | 5 ++ 2 files changed, 88 insertions(+), 39 deletions(-) diff --git a/acts b/acts index 43b24e9..cb58901 100755 --- a/acts +++ b/acts @@ -92,6 +92,7 @@ verbose="${verbose=0}" hostname="${hostname=$(hostname -s)}" uselocaltime="${uselocaltime=0}" backuptargets="${backuptargets=}" +fullbackups="${fullbackups=0}" prebackupscript="${prebackupscript=}" postbackupscript="${postbackupscript=}" tarsnap="${tarsnap:=tarsnap}" @@ -109,6 +110,10 @@ if [ -z "$backuptargets" ]; then die "config-error message=\"\$backuptargets is not set in acts.conf.\"" fi +if [ "$fullbackups" -ne 0 ] && [ "$fullbackups" -ne 1 ]; then + die "config-error message=\"invalid \$fullbackups in acts.conf; valid options are 0 (one archive per target) or 1 (one archive).\"" +fi + if [ "$uselocaltime" = 0 ]; then utc="-u" elif [ "$uselocaltime" = 1 ]; then @@ -127,7 +132,7 @@ if [ -d "$lockfile" ]; then else if output=$(mkdir "$lockfile" 2>&1); then echo $$ >"$lockfile/pid" - log_debug "message=\"Created $lockfile/ with 'pid' referencing $$\"" + log_debug "message=\"Created $lockfile with 'pid' referencing $$\"" else die "acts-error message=\"Can't create lock $lockfile: $output\"" fi @@ -191,36 +196,39 @@ if [ -n "$prebackupscript" ]; then fi # PART 3: Backup -backuprc=0 # Notice any failed backups -backuplist="" # Maintain list of successfully created archives -for dir in $backuptargets; do - log_debug "message=\"Starting backup of $dir\"" - archive_starttime=$(date +%s) - nicedirname=$(prettyprint archive_dirname "$dir") - logdirname=$(prettyprint log_dirname "$dir") - dailyarchive="$hostname-daily-$today$nicedirname" - monthlyarchive="$hostname-monthly-$today$nicedirname" - yearlyarchive="$hostname-yearly-$today$nicedirname" +# create_backup [archivesuffix] [logdirpath] [logarchivelabel] [dirarguments] +# create_backup "etcopt" "/etc/opt" "/etc/opt" "etc/opt" +create_backup() { + cb_archivesuffix="$1" + cb_dirlogging="$2" + cb_logarchivelabel="$3" + cb_dirarguments="$4" + + dailyarchive="$hostname-daily-$today$cb_archivesuffix" + monthlyarchive="$hostname-monthly-$today$cb_archivesuffix" + yearlyarchive="$hostname-yearly-$today$cb_archivesuffix" + + log_verbose "backup-start type=daily ${cb_dirlogging}name=$dailyarchive" + archive_starttime=$(date +%s) - log_verbose "backup-start type=daily dir=/$dir name=$dailyarchive" # Uncontrolled expansion is bad, but we have little choice. See https://github.com/koalaman/shellcheck/wiki/Sc2086 # shellcheck disable=SC2086 - if output="$(calltarsnap -c -f "$dailyarchive" -C / $tarsnapbackupoptions "$dir" 2>&1)"; then + if output="$(calltarsnap -c -f "$dailyarchive" -C / $tarsnapbackupoptions $cb_dirarguments 2>&1)"; then backuplist="$backuplist $dailyarchive" else log_output=$(prettyprint log_output "$output") - log_message "backup-error type=daily dir=$logdirname output=\"$log_output\"" + log_message "backup-error type=daily ${cb_dirlogging}output=\"$log_output\"" backuprc=1 fi archive_endtime=$(date +%s) - log_verbose "backup-finish type=daily dir=$logdirname duration=$((archive_endtime - archive_starttime))s" + log_verbose "backup-finish type=daily ${cb_dirlogging}duration=$((archive_endtime - archive_starttime))s" # Determine whether other backup levels need to be created too # If so, copy them from $dailyarchive - if ! echo "$archives" | grep -E -q "^$hostname-yearly-$year-.+$nicedirname$"; then + if ! echo "$archives" | grep -E -q "^$hostname-yearly-$year-.+${cb_archivesuffix}$"; then # No yearly backup - log_debug "message=\"Copying $logdirname daily archive to yearly archive\"" + log_debug "message=\"Copying ${cb_logarchivelabel}daily archive to yearly archive\"" archive_starttime=$(date +%s) # shellcheck disable=SC2086 if output="$(calltarsnap -c -f "$yearlyarchive" $tarsnapbackupoptions "@@$dailyarchive" 2>&1)"; then @@ -231,12 +239,12 @@ for dir in $backuptargets; do backuprc=1 fi archive_endtime=$(date +%s) - log_verbose "copy-finish type=yearly dir=$logdirname name=$yearlyarchive duration=$((archive_endtime - archive_starttime))s" + log_verbose "copy-finish type=yearly ${cb_dirlogging}name=$yearlyarchive duration=$((archive_endtime - archive_starttime))s" fi - if ! echo "$archives" | grep -E -q "^$hostname-monthly-$year-$month-.+$nicedirname$"; then + if ! echo "$archives" | grep -E -q "^$hostname-monthly-$year-$month-.+${cb_archivesuffix}$"; then # No monthly backup - log_debug "message=\"Copying $logdirname daily archive to monthly archive\"" + log_debug "message=\"Copying ${cb_logarchivelabel}daily archive to monthly archive\"" archive_starttime=$(date +%s) # shellcheck disable=SC2086 if output="$(calltarsnap -c -f "$monthlyarchive" $tarsnapbackupoptions "@@$dailyarchive" 2>&1)"; then @@ -247,9 +255,30 @@ for dir in $backuptargets; do backuprc=1 fi archive_endtime=$(date +%s) - log_verbose "copy-finish type=monthly dir=$logdirname name=$monthlyarchive duration=$((archive_endtime - archive_starttime))s" + log_verbose "copy-finish type=monthly ${cb_dirlogging}name=$monthlyarchive duration=$((archive_endtime - archive_starttime))s" fi -done +} + +backuprc=0 # Notice any failed backups +backuplist="" # Maintain list of successfully created archives + +if [ "$fullbackups" = 0 ]; then + for dir in $backuptargets; do + log_debug "message=\"Starting backup of $dir\"" + + archivesuffix=$(prettyprint archive_dirname "$dir") + logdirname=$(prettyprint log_dirname "$dir") + dirlogging="dir=$logdirname " + logarchivelabel="$logdirname " + + create_backup "$archivesuffix" "$dirlogging" "$logarchivelabel" "$dir" + done +elif [ "$fullbackups" = 1 ]; then + log_debug "message=\"Starting full backup of $backuptargets\"" + + create_backup "--full" "" "" "$backuptargets" +fi + # Update the archive listing log_debug "Listing tarsnap archives before deleting old backups" archives=$(calltarsnap --list-archives | sort -n) @@ -259,40 +288,55 @@ if [ "$backuprc" != "0" ]; then die "acts-tarsnap-error One of the backups failed -- not deleting old backups" fi +# prune_backup_levels [archivesuffix] +# prune_backup_levels etcopt +prune_backup_levels() { + pbl_archivesuffix=$1 + + prune_backups $pbl_archivesuffix yearly $yearlybackups + prune_backups $pbl_archivesuffix monthly $monthlybackups + prune_backups $pbl_archivesuffix daily $dailybackups +} + +# prune_backups [archivesuffix] [backuplevel] [max] +# prune_backups etcopt monthly 12 prune_backups() { - backuplevel=$1 max=$2 + pb_archivesuffix=$1 + pb_backuplevel=$2 + pb_max=$3 - if [ "$max" -gt 0 ]; then - backupslist=$(echo "$archives" | grep -E "$hostname-$backuplevel-.+$nicedirname$" | sort -rn) + if [ "$pb_max" -gt 0 ]; then + backupslist=$(echo "$archives" | grep -E "$hostname-$pb_backuplevel-.+${pb_archivesuffix}$" | sort -rn) numberofbackups=$(printf '%s' "$backupslist" | awk 'END{print NR}') - if [ "$numberofbackups" -gt "$max" ]; then - log_debug "message=\"More than $max $backuplevel backups, deleting the oldest\"" - echo "$backupslist" | tail -n +"$((max + 1))" | while read -r archiveprefixtodel; do + if [ "$numberofbackups" -gt "$pb_max" ]; then + log_debug "message=\"More than $pb_max $pb_backuplevel backups, deleting the oldest\"" + echo "$backupslist" | tail -n +"$((pb_max + 1))" | while read -r archiveprefixtodel; do log_verbose "message=\"Deleting backup prefix $archiveprefixtodel*\"" echo "$archives" | grep -E "^$archiveprefixtodel" | while read -r archivetodel; do log_debug "message=\"Deleting backup $archivetodel\"" if ! output="$(calltarsnap -d -f "$archivetodel" 2>&1)"; then log_output=$(prettyprint log_output "$output") - log_message "delete-error type=$backuplevel output=\"$log_output\"" + log_message "delete-error type=$pb_backuplevel output=\"$log_output\"" fi done done else - log_debug "message=\"Found $numberofbackups $backuplevel backups; not deleting any\"" + log_debug "message=\"Found $numberofbackups $pb_backuplevel backups; not deleting any\"" fi else - log_debug "message=\"Keeping all $backuplevel backups indefinitely; not deleting any\"" + log_debug "message=\"Keeping all $pb_backuplevel backups indefinitely; not deleting any\"" fi } -for dir in $backuptargets; do - log_debug "message=\"Checking $dir for old backups to delete\"" - nicedirname=$(prettyprint archive_dirname "$dir") - - prune_backups yearly $yearlybackups - prune_backups monthly $monthlybackups - prune_backups daily $dailybackups -done +if [ "$fullbackups" = 0 ]; then + for dir in $backuptargets; do + log_debug "message=\"Checking $dir for old backups to delete\"" + prune_backup_levels $(prettyprint archive_dirname "$dir") + done +elif [ "$fullbackups" = 1 ]; then + log_debug "message=\"Checking for old full backups to delete\"" + prune_backup_levels "--full" +fi # Run the post-backup script if [ -n "$postbackupscript" ]; then diff --git a/acts.conf.sample b/acts.conf.sample index 03bd296..d8bd61d 100644 --- a/acts.conf.sample +++ b/acts.conf.sample @@ -7,6 +7,11 @@ # Default: unset #backuptargets="var etc home root" +# fullbackups +# Create only one archive per run that includes all backup directories. +# Default: 0 (i.e. create one archive per target) +#fullbackups=1 + # tarsnap # What command to call for 'tarsnap'. # Anything that should be used every time (like --configfile)