Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to create full backups instead of per-target #44

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 83 additions & 39 deletions acts
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions acts.conf.sample
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down