aboutsummaryrefslogtreecommitdiff
path: root/handlers/rsync.in
diff options
context:
space:
mode:
Diffstat (limited to 'handlers/rsync.in')
-rw-r--r--handlers/rsync.in240
1 files changed, 140 insertions, 100 deletions
diff --git a/handlers/rsync.in b/handlers/rsync.in
index 6c772e1..386255e 100644
--- a/handlers/rsync.in
+++ b/handlers/rsync.in
@@ -35,11 +35,10 @@
# mountpoint = backup partition mountpoint or backup main folder (either local or remote)
# backupdir = folder relative do $mountpoint where the backup should be stored (local or remote)
# format = specify backup storage format: short, long or mirror (i.e, no rotations)
-# days = for short storage format, specify the number of backup increments (min = 5)
+# days = for short storage format, specify the number of backup increments (min = 2, set to 1 or less to disable)
# keepdaily = for long storage format, specify the number of daily backup increments
# keepweekly = for long storage format, specify the number of weekly backup increments
# keepmonthly = for long storage format, specify the number of monthly backup increments
-# lockfile = lockfile to be kept during backup execution
# nicelevel = rsync command nice level
# enable_mv_timestamp_bug = set to "yes" if your system isnt handling timestamps correctly
# tmp = temp folder
@@ -60,7 +59,7 @@
# exclude_vserver = vserver-name (valid only if vservers = yes on backupninja.conf)
# numericids = when set to 1, use numeric ids instead of user/group mappings on rsync
# compress = if set to 1, compress data on rsync (remote source only)
-# bandwidthlimit = set a badnwidth limit in KB/s (remote source only)
+# bandwidthlimit = set a bandwidth limit in KB/s (remote source only)
# remote_rsync = remote rsync program (remote source only)
# id_file = ssh key file (remote source only)
# batch = set to "yes" to rsync use a batch file as source
@@ -79,7 +78,7 @@
# port = remote port number (remote destination only)
# user = remote user name (remote destination only)
# id_file = ssh key file (remote destination only)
-# bandwidthlimit = set a badnwidth limit in KB/s (remote destination only)
+# bandwidthlimit = set a bandwidth limit in KB/s (remote destination only)
# remote_rsync = remote rsync program (remote dest only)
# batch = set to "yes" to rsync write a batch file from the changes
# batchbase = folder where the batch file should be written
@@ -125,11 +124,10 @@ function eval_config {
getconf mountpoint
getconf backupdir
getconf format short
- getconf days
+ getconf days 7
getconf keepdaily 5
getconf keepweekly 3
getconf keepmonthly 1
- getconf lockfile
getconf nicelevel 0
getconf enable_mv_timestamp_bug no
getconf tmp /tmp
@@ -230,19 +228,16 @@ function eval_config {
if [ "$dest" != "local" ] && [ "$from" == "remote" ]; then
fatal "When source is remote, destination should be local."
- exit 1
fi
if [ "$from" != "local" ] && [ "$from" != "remote" ]; then
fatal "Invalid source $from"
- exit 1
fi
backupdir="$mountpoint/$backupdir"
if [ "$dest" == "local" ] && [ ! -d "$backupdir" ]; then
- error "Backupdir $backupdir does not exist"
- exit 1
+ fatal "Backupdir $backupdir does not exist"
fi
if [ ! -z "$log" ]; then
@@ -280,9 +275,7 @@ function eval_config {
mv=move_files
fi
- for path in $exclude; do
- excludes="$excludes --exclude=$path"
- done
+ excludes=`echo "$exclude" | @SED@ -e "s/^/--exclude='/g" -e "s/ /' --exclude='/g" -e "s/$/'/"`
}
@@ -293,9 +286,9 @@ function rotate_short {
local keep="$2"
local metadata="`dirname $folder`/metadata"
- if [[ "$keep" -lt 4 ]]; then
- error "Rotate: minimum of 4 rotations"
- exit 1
+ # No rotations
+ if [[ "$keep" -lt 1 ]]; then
+ return
fi
if [ -d $folder.$keep ]; then
@@ -320,6 +313,14 @@ function rotate_short {
$nice $cp -alf /$folder.1/. /$folder.0
fi
+ # Cleanup orphaned metadata
+ for file in `ls $metadata`; do
+ if [ ! -d "`dirname $folder`/$file" ]; then
+ debug "removing orphaned metadata $file"
+ rm -rf $metadata/$file
+ fi
+ done
+
}
function rotate_short_remote {
@@ -328,9 +329,9 @@ function rotate_short_remote {
local metadata="`dirname $folder`/metadata"
local keep="$2"
- if [[ "$2" -lt 4 ]]; then
- error "Rotate: minimum of 4 rotations"
- exit 1
+ # No rotations
+ if [[ "$keep" -lt 1 ]]; then
+ return
fi
(
@@ -358,6 +359,14 @@ function rotate_short_remote {
if [ -d $folder.1 ]; then
$nice $cp -alf /$folder.1/. /$folder.0
fi
+
+ # Cleanup orphaned metadata
+ for file in \`ls $metadata\`; do
+ if [ ! -d "`dirname $folder`/\$file" ]; then
+ echo "Debug: removing orphaned metadata \$file"
+ rm -rf $metadata/\$file
+ fi
+ done
##### END REMOTE SCRIPT #######
EOF
) | (while read a; do passthru $a; done)
@@ -378,8 +387,8 @@ function rotate_long {
local metadata
if [ ! -d "$backuproot" ]; then
- echo "Debug: skipping rotate of $backuproot as it doesn't exist."
- exit
+ warning "Skipping rotate of $backuproot as it doesn't exist."
+ return
fi
for rottype in daily weekly monthly; do
@@ -392,12 +401,12 @@ function rotate_long {
echo "Debug: $dir.1 does not exist, skipping."
continue 1
elif [ ! -f $metadata.1/created ] && [ ! -f $metadata.1/rotated ]; then
- echo "Warning: metadata does not exist for $dir.1. This backup may be only partially completed. Skipping rotation."
+ warning "Warning: metadata does not exist for $dir.1. This backup may be only partially completed. Skipping rotation."
continue 1
fi
# Rotate the current list of backups, if we can.
- oldest=`find $backuproot -maxdepth 1 -type d -name $rottype'.*' | @SED@ 's/^.*\.//' | sort -n | tail -1`
+ oldest=`find $backuproot -maxdepth 1 -type d -name $rottype'.*' | @SED@ -e 's/^.*\.//' | sort -n | tail -1`
[ "$oldest" == "" ] && oldest=0
for (( i=$oldest; i > 0; i-- )); do
if [ -d $dir.$i ]; then
@@ -408,19 +417,27 @@ function rotate_long {
else
created=0
fi
+ # Validate created date
+ if [ -z "$created" ] || echo $created | grep -v -q -e '^[0-9]*$'; then
+ warning "Invalid metadata $created. Skipping rotation."
+ break
+ fi
cutoff_time=$(( now - (seconds*(i-1)) ))
if [ ! $created -gt $cutoff_time ]; then
next=$(( i + 1 ))
if [ ! -d $dir.$next ]; then
- echo "Debug: $rottype.$i --> $rottype.$next"
+ debug "$rottype.$i --> $rottype.$next"
$nice mv $dir.$i $dir.$next
mkdir -p $metadata.$next
date +%c%n%s > $metadata.$next/rotated
+ if [ -f $metadata.$i/created ]; then
+ $nice mv $metadata.$i/created $metadata.$next
+ fi
else
- echo "Debug: skipping rotation of $dir.$i because $dir.$next already exists."
+ debug "skipping rotation of $dir.$i because $dir.$next already exists."
fi
else
- echo "Debug: skipping rotation of $dir.$i because it was created" $(( (now-created)/86400)) "days ago ("$(( (now-cutoff_time)/86400))" needed)."
+ debug "skipping rotation of $dir.$i because it was created" $(( (now-created)/86400)) "days ago ("$(( (now-cutoff_time)/86400))" needed)."
fi
fi
done
@@ -428,38 +445,52 @@ function rotate_long {
max=$((keepdaily+1))
if [ $keepweekly -gt 0 -a -d $backuproot/daily.$max -a ! -d $backuproot/weekly.1 ]; then
- echo "Debug: daily.$max --> weekly.1"
+ debug "daily.$max --> weekly.1"
$nice mv $backuproot/daily.$max $backuproot/weekly.1
mkdir -p $backuproot/metadata/weekly.1
date +%c%n%s > $backuproot/metadata/weekly.1/rotated
+ #if [ -f $backuproot/metadata/daily.$max/created ]; then
+ # $nice mv $backuproot/metadata/daily.$max/created $backuproot/metadata/weekly.1/
+ #fi
fi
max=$((keepweekly+1))
if [ $keepmonthly -gt 0 -a -d $backuproot/weekly.$max -a ! -d $backuproot/monthly.1 ]; then
- echo "Debug: weekly.$max --> monthly.1"
+ debug "weekly.$max --> monthly.1"
$nice mv $backuproot/weekly.$max $backuproot/monthly.1
mkdir -p $backuproot/metadata/monthly.1
date +%c%n%s > $backuproot/metadata/monthly.1/rotated
+ #if [ -f $backuproot/metadata/weekly.$max/created ]; then
+ # $nice mv $backuproot/metadata/weekly.$max/created $backuproot/metadata/weekly.1/
+ #fi
fi
for rottype in daily weekly monthly; do
max=$((keep${rottype}+1))
dir="$backuproot/$rottype"
- oldest=`find $backuproot -maxdepth 1 -type d -name $rottype'.*' | @SED@ 's/^.*\.//' | sort -n | tail -1`
+ oldest=`find $backuproot -maxdepth 1 -type d -name $rottype'.*' | @SED@ -e 's/^.*\.//' | sort -n | tail -1`
[ "$oldest" == "" ] && oldest=0
# if we've rotated the last backup off the stack, remove it.
for (( i=$oldest; i >= $max; i-- )); do
if [ -d $dir.$i ]; then
if [ -d $backuproot/rotate.tmp ]; then
- echo "Debug: removing rotate.tmp"
+ debug "removing rotate.tmp"
$nice rm -rf $backuproot/rotate.tmp
fi
- echo "Debug: moving $rottype.$i to rotate.tmp"
+ debug "moving $rottype.$i to rotate.tmp"
$nice mv $dir.$i $backuproot/rotate.tmp
fi
done
done
+ # Cleanup orphaned metadata
+ for file in `ls $backuproot/metadata`; do
+ if [ ! -d "$backuproot/$file" ]; then
+ debug "removing orphaned metadata $file"
+ rm -rf $backuproot/metadata/$file
+ fi
+ done
+
}
function rotate_long_remote {
@@ -479,7 +510,7 @@ function rotate_long_remote {
now=\`date +%s\`
if [ ! -d "$backuproot" ]; then
- echo "Debug: skipping rotate of $backuproot as it doesn't exist."
+ echo "Fatal: skipping rotate of $backuproot as it doesn't exist."
exit
fi
@@ -498,7 +529,7 @@ function rotate_long_remote {
fi
# Rotate the current list of backups, if we can.
- oldest=\`find $backuproot -maxdepth 1 -type d -name \$rottype'.*' | @SED@ 's/^.*\.//' | sort -n | tail -1\`
+ oldest=\`find $backuproot -maxdepth 1 -type d -name \$rottype'.*' | @SED@ -e 's/^.*\.//' | sort -n | tail -1\`
[ "\$oldest" == "" ] && oldest=0
for (( i=\$oldest; i > 0; i-- )); do
if [ -d \$dir.\$i ]; then
@@ -509,6 +540,11 @@ function rotate_long_remote {
else
created=0
fi
+ # Validate created date
+ if [ -z "\$created" ] || echo \$created | grep -v -q -e '^[0-9]*$'; then
+ echo "Warning: Invalid metadata \$created. Skipping rotation."
+ break
+ fi
cutoff_time=\$(( now - (seconds*(i-1)) ))
if [ ! \$created -gt \$cutoff_time ]; then
next=\$(( i + 1 ))
@@ -517,6 +553,9 @@ function rotate_long_remote {
$nice mv \$dir.\$i \$dir.\$next
mkdir -p \$metadata.\$next
date +%c%n%s > \$metadata.\$next/rotated
+ if [ -f \$metadata.\$i/created ]; then
+ $nice mv \$metadata.\$i/created \$metadata.\$next
+ fi
else
echo "Debug: skipping rotation of \$dir.\$i because \$dir.\$next already exists."
fi
@@ -533,6 +572,9 @@ function rotate_long_remote {
$nice mv $backuproot/daily.\$max $backuproot/weekly.1
mkdir -p $backuproot/metadata/weekly.1
date +%c%n%s > $backuproot/metadata/weekly.1/rotated
+ #if [ -f $backuproot/metadata/daily.\$max/created ]; then
+ # $nice mv $backuproot/metadata/daily.\$max/created $backuproot/metadata/weekly.1/
+ #fi
fi
max=\$((keepweekly+1))
@@ -541,12 +583,15 @@ function rotate_long_remote {
$nice mv $backuproot/weekly.\$max $backuproot/monthly.1
mkdir -p $backuproot/metadata/monthly.1
date +%c%n%s > $backuproot/metadata/monthly.1/rotated
+ #if [ -f $backuproot/metadata/weekly.\$max/created ]; then
+ # $nice mv $backuproot/metadata/weekly.\$max/created $backuproot/metadata/weekly.1/
+ #fi
fi
for rottype in daily weekly monthly; do
max=\$((keep\${rottype}+1))
dir="$backuproot/\$rottype"
- oldest=\`find $backuproot -maxdepth 1 -type d -name \$rottype'.*' | @SED@ 's/^.*\.//' | sort -n | tail -1\`
+ oldest=\`find $backuproot -maxdepth 1 -type d -name \$rottype'.*' | @SED@ -e 's/^.*\.//' | sort -n | tail -1\`
[ "\$oldest" == "" ] && oldest=0
# if we've rotated the last backup off the stack, remove it.
for (( i=\$oldest; i >= \$max; i-- )); do
@@ -560,6 +605,14 @@ function rotate_long_remote {
fi
done
done
+
+ # Cleanup orphaned metadata
+ for file in \`ls $backuproot/metadata\`; do
+ if [ ! -d "$backuproot/\$file" ]; then
+ echo "Debug: removing orphaned metadata \$file"
+ rm -rf $backuproot/metadata/\$file
+ fi
+ done
##### END REMOTE SCRIPT #######
EOF
) | (while read a; do passthru $a; done)
@@ -589,22 +642,19 @@ function setup_long_dirs {
if [ -d $tmpdir ]; then
mv $tmpdir $dir.1
if [ $? == 1 ]; then
- echo "Fatal: could mv $destdir/rotate.tmp $dir.1 on host $host"
- exit 1
+ fatal "Could not move $tmpdir to $dir.1 on host $host"
fi
else
mkdir --parents $dir.1
if [ $? == 1 ]; then
- echo "Fatal: could not create directory $dir.1 on host $host"
- exit 1
+ fatal "Could not create directory $dir.1 on host $host"
fi
fi
if [ -d $dir.2 ]; then
echo "Debug: update links $backuptype.2 --> $backuptype.1"
cp -alf $dir.2/. $dir.1
#if [ $? == 1 ]; then
- # echo "Fatal: could not create hard links to $dir.1 on host $host"
- # exit 1
+ # fatal "Could not create hard links to $dir.1 on host $host"
#fi
fi
fi
@@ -705,7 +755,6 @@ function prepare_storage {
btype=monthly
else
fatal "keeping no backups";
- exit 1
fi
suffix="$btype.1"
@@ -724,7 +773,6 @@ function prepare_storage {
suffix=""
else
fatal "Invalid backup format $format"
- exit 1
fi
}
@@ -768,7 +816,6 @@ function set_batch_mode {
batch_option="--read-batch=$batch_file"
else
fatal "Batch file not found: $batch_file"
- exit 1
fi
elif [ "$batch" == "write" ]; then
mkdir -p `dirname $batch_file`
@@ -785,7 +832,11 @@ function update_metadata {
if [ "$dest" == "local" ]; then
metadata="`dirname $dest_path`/metadata/`basename $dest_path`"
mkdir -p $metadata
- date +%c%n%s > $metadata/created
+ # Use the backup start time and not the time the backup was
+ # finished, otherwise daily rotations might not take place.
+ # If we used backup end time, in the next handler run
+ # we might not have $now - $created >= 24:00
+ echo "$starttime" > $metadata/created
$touch $backupdir/$SECTION/$suffix
else
folder="`echo $dest_path | cut -d : -f 2`"
@@ -795,7 +846,11 @@ function update_metadata {
$ssh_cmd <<EOF
##### BEGIN REMOTE SCRIPT #####
mkdir -p $metadata
- date +%c%n%s > $metadata/created
+ # Use the backup start time and not the time the backup was
+ # finished, otherwise daily rotations might not take place.
+ # If we used backup end time, in the next handler run
+ # we might not have $now - $created >= 24:00
+ echo "$starttime" > $metadata/created
##### END REMOTE SCRIPT #######
EOF
) | (while read a; do passthru $a; done)
@@ -813,7 +868,6 @@ function test_connect {
if [ -z "$host" ] || [ -z "$user" ]; then
fatal "Remote host or user not set"
- exit 1
fi
debug "$ssh_cmd 'echo -n 1'"
@@ -821,52 +875,12 @@ function test_connect {
if [ "$result" != "1" ]; then
fatal "Can't connect to $host as $user."
- exit 1
else
debug "Connected to $host successfully"
fi
}
-function set_lockfile {
-
- if [ ! -z "$lockfile" ]; then
- mkdir -p `dirname $lockfile`
- if ( set -o noclobber; echo "$$" > "$lockfile" ) &> /dev/null; then
- trap 'unset_lockfile' INT TERM EXIT
- else
- fatal "Could not create lockfile $lockfile, exiting"
- fi
- fi
-
-}
-
-function unset_lockfile {
-
- if [ ! -z "$lockfile" ]; then
- $rm -f $lockfile || warning "Could not remove lockfile $lockfile"
- fi
-
-}
-
-function check_lockfile {
-
- local pid process
-
- if [ ! -z "$lockfile" ] && [ -f "$lockfile" ]; then
- pid="`cat $lockfile`"
- process="`ps --no-headers -o comm $pid`"
- if [ "$?" == "0" ] && [ "`ps --no-headers -o comm $$`" == "$process" ]; then
- info "Another backup is running for $lockfile, skipping run"
- exit
- else
- info "Found old lockfile $lockfile, removing it"
- unset_lockfile
- fi
- fi
-
-}
-
function set_filelist {
filelist_flag=""
@@ -887,13 +901,13 @@ function set_filelist {
function set_rsync_options {
- if [ ! -z "$numericids" ]; then
+ if [ "$numericids" != "0" ]; then
rsync_options="$rsync_options --numeric-ids"
fi
if [ "$from" == "local" ] || [ "$dest" == "local" ]; then
# rsync options for local sources or destinations
- rsync_options=($rsync_options)
+ true
fi
if [ "$from" == "remote" ] || [ "$dest" == "remote" ]; then
@@ -912,12 +926,9 @@ function set_rsync_options {
remote_rsync="$remote_rsync --fake-super"
fi
- rsync_options=($rsync_options --rsync-path="$remote_rsync")
-
if [ "$protocol" == "ssh" ]; then
if [ ! -e "$id_file" ]; then
fatal "SSH Identity file $id_file not found"
- exit 1
else
debug RSYNC_RSH=\"$ssh_cmd_base\"
echo RSYNC_RSH=\"$ssh_cmd_base\" >> $log
@@ -927,6 +938,14 @@ function set_rsync_options {
fi
+ # Mangle rsync_options so we can use quotes after all other
+ # options were evaluated.
+ if [ "$from" == "local" ] && [ "$dest" == "local" ]; then
+ rsync_options=($rsync_options)
+ else
+ rsync_options=($rsync_options --rsync-path="$remote_rsync")
+ fi
+
include_vservers
}
@@ -964,8 +983,7 @@ function mount_rw {
if [ -d "$mountpoint" ]; then
mount -o remount,rw $mountpoint
if (($?)); then
- error "Could not mount $mountpoint"
- exit 1
+ fatal "Could not mount $mountpoint"
fi
fi
fi
@@ -1049,17 +1067,38 @@ function end_mux {
}
+function set_pipefail {
+
+ # Save initial pipefail status for later restoration
+ if echo "$SHELLOPTS" | grep -q ":pipefail"; then
+ pipefail="-o"
+ else
+ pipefail="+o"
+ fi
+
+ # Ensure that a non-zero rsync exit status is caught by our handler
+ set -o pipefail
+
+}
+
+function restore_pipefail {
+
+ if [ ! -z "$pipefail" ]; then
+ set $pipefail pipefail
+ fi
+
+}
+
# the backup procedure
eval_config
-check_lockfile
-set_lockfile
set_rsync_options
start_mux
stop_services
mount_rw
-echo "Starting backup at `date`" >> $log
+starttime="`date +%c%n%s`"
+echo "Starting backup at `echo "$starttime" | head -n 1`" >> $log
for SECTION in $include; do
@@ -1070,13 +1109,15 @@ for SECTION in $include; do
set_dest
info "Syncing $SECTION on $dest_path..."
- debug $nice $rsync $rsync_options $filelist_flag $excludes $batch_option $orig $dest_path
- $nice $rsync $rsync_options $filelist_flag $excludes $batch_option $orig $dest_path | tee -a $log
+ debug $nice $rsync "${rsync_options[@]}" $filelist_flag "$excludes" $batch_option $orig $dest_path
+ set_pipefail
+ $nice $rsync "${rsync_options[@]}" $filelist_flag "$excludes" $batch_option $orig $dest_path | tee -a $log
if [ "$?" != "0" ]; then
- warning "Rsync error when trying to transfer $SECTION"
+ fatal "Rsync error when trying to transfer $SECTION"
fi
+ restore_pipefail
update_metadata
done
@@ -1084,7 +1125,6 @@ done
mount_ro
run_fsck
start_services
-unset_lockfile
end_mux
echo "Finnishing backup at `date`" >> $log