+function rotate_short_remote {
+
+ local folder="$1"
+ local metadata="`dirname $folder`/metadata"
+ local keep="$2"
+
+ if [[ "$2" -lt 4 ]]; then
+ error "Rotate: minimum of 4 rotations"
+ exit 1
+ fi
+
+(
+ $ssh_cmd <<EOF
+ ##### BEGIN REMOTE SCRIPT #####
+
+ if [ -d $folder.$keep ]; then
+ $nice mv /$folder.$keep /$folder.tmp
+ fi
+
+ for ((n=$(($keep - 1)); n >= 0; n--)); do
+ if [ -d $folder.\$n ]; then
+ dest=\$((\$n + 1))
+ $nice mv /$folder.\$n /$folder.\$dest
+ touch /$folder.\$dest
+ mkdir -p $metadata/`basename $folder`.\$dest
+ date +%c%n%s > $metadata/`basename $folder`.\$dest/rotated
+ fi
+ done
+
+ if [ -d $folder.tmp ]; then
+ $nice mv /$folder.tmp /$folder.0
+ fi
+
+ if [ -d $folder.1 ]; then
+ $nice $cp -alf /$folder.1/. /$folder.0
+ fi
+ ##### END REMOTE SCRIPT #######
+EOF
+) | (while read a; do passthru $a; done)
+
+}
+
+function rotate_long {
+
+ backuproot="$1"
+ seconds_daily=86400
+ seconds_weekly=604800
+ seconds_monthly=2628000
+ keepdaily=$keepdaily
+ keepweekly=$keepweekly
+ keepmonthly=$keepmonthly
+ now=`date +%s`
+
+ local metadata
+
+ if [ ! -d "$backuproot" ]; then
+ echo "Debug: skipping rotate of $backuproot as it doesn't exist."
+ exit
+ fi
+
+ for rottype in daily weekly monthly; do
+ seconds=$((seconds_${rottype}))
+
+ dir="$backuproot/$rottype"
+ metadata="$backuproot/metadata/$rottype.1"
+ mkdir -p $metadata
+ if [ ! -d $dir.1 ]; then
+ echo "Debug: $dir.1 does not exist, skipping."
+ continue 1
+ elif [ ! -f $metadata/created ] && [ ! -f $metadata/rotated ]; then
+ echo "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" == "" ] && oldest=0
+ for (( i=$oldest; i > 0; i-- )); do
+ if [ -d $dir.$i ]; then
+ if [ -f $metadata/created ]; then
+ created=`tail -1 $metadata/created`
+ elif [ -f $metadata/rotated ]; then
+ created=`tail -1 $metadata/rotated`
+ else
+ created=0
+ 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"
+ $nice mv $dir.$i $dir.$next
+ mkdir -p $backuproot/metadata/$rottype.$next
+ date +%c%n%s > $backuproot/metadata/$rottype.$next/rotated
+ else
+ echo "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)."
+ fi
+ fi
+ done
+ done
+
+ max=$((keepdaily+1))
+ if [ $keepweekly -gt 0 -a -d $backuproot/daily.$max -a ! -d $backuproot/weekly.1 ]; then
+ echo "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
+ 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"
+ $nice mv $backuproot/weekly.$max $backuproot/monthly.1
+ mkdir -p $backuproot/metadata/monthly.1
+ date +%c%n%s > $backuproot/metadata/monthly.1/rotated
+ 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" == "" ] && 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"
+ $nice rm -rf $backuproot/rotate.tmp
+ fi
+ echo "Debug: moving $rottype.$i to rotate.tmp"
+ $nice mv $dir.$i $backuproot/rotate.tmp
+ fi
+ done
+ done
+
+}
+
+function rotate_long_remote {
+
+ local backuproot="$1"
+
+(
+ $ssh_cmd <<EOF
+ ##### BEGIN REMOTE SCRIPT #####
+
+ seconds_daily=86400
+ seconds_weekly=604800
+ seconds_monthly=2628000
+ keepdaily=$keepdaily
+ keepweekly=$keepweekly
+ keepmonthly=$keepmonthly
+ now=\`date +%s\`
+
+ if [ ! -d "$backuproot" ]; then
+ echo "Debug: skipping rotate of $backuproot as it doesn't exist."
+ exit
+ fi
+
+ for rottype in daily weekly monthly; do
+ seconds=\$((seconds_\${rottype}))
+
+ dir="$backuproot/\$rottype"
+ metadata="$backuproot/metadata/\$rottype.1"
+ mkdir -p \$metadata
+ if [ ! -d \$dir.1 ]; then
+ echo "Debug: \$dir.1 does not exist, skipping."
+ continue 1
+ elif [ ! -f \$metadata/created ] && [ ! -f \$metadata/rotated ]; then
+ echo "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" == "" ] && oldest=0
+ for (( i=\$oldest; i > 0; i-- )); do
+ if [ -d \$dir.\$i ]; then
+ if [ -f \$metadata/created ]; then
+ created=\`tail -1 \$metadata/created\`
+ elif [ -f \$metadata/rotated ]; then
+ created=\`tail -1 \$metadata/rotated\`
+ else
+ created=0
+ 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"
+ $nice mv \$dir.\$i \$dir.\$next
+ mkdir -p $backuproot/metadata/\$rottype.\$next
+ date +%c%n%s > $backuproot/metadata/\$rottype.\$next/rotated
+ else
+ echo "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)."
+ fi
+ fi
+ done
+ done
+
+ max=\$((keepdaily+1))
+ if [ \$keepweekly -gt 0 -a -d $backuproot/daily.\$max -a ! -d \$backuproot/weekly.1 ]; then
+ echo "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
+ 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"
+ $nice mv $backuproot/weekly.\$max $backuproot/monthly.1
+ mkdir -p $backuproot/metadata/monthly.1
+ date +%c%n%s > $backuproot/metadata/monthly.1/rotated
+ 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" == "" ] && 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"
+ $nice rm -rf $backuproot/rotate.tmp
+ fi
+ echo "Debug: moving \$rottype.\$i to rotate.tmp"
+ $nice mv \$dir.\$i $backuproot/rotate.tmp
+ fi
+ done
+ done
+ ##### END REMOTE SCRIPT #######
+EOF
+) | (while read a; do passthru $a; done)
+
+}
+
+function setup_long_dirs {
+
+ local destdir=$1
+ local backuptype=$2
+ local dir="$destdir/$backuptype"
+ local tmpdir="$destdir/rotate.tmp"
+ local metadata="$destdir/metadata/$backuptype.1"
+
+ if [ ! -d $destdir ]; then
+ echo "Creating destination directory $destdir..."
+ mkdir -p $destdir
+ fi
+
+ if [ -d $dir.1 ]; then
+ if [ -f $metadata/created ]; then
+ echo "Warning: $dir.1 already exists. Overwriting contents."
+ else
+ echo "Warning: we seem to be resuming a partially written $dir.1"
+ fi
+ else
+ 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
+ fi
+ else
+ mkdir --parents $dir.1
+ if [ $? == 1 ]; then
+ echo "Fatal: could not create directory $dir.1 on host $host"
+ exit 1
+ 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
+ #fi
+ fi
+ fi
+ [ -f $metadata/created ] && rm $metadata/created
+ [ -f $metadata/rotated ] && rm $metadata/rotated
+
+}
+
+function setup_long_dirs_remote {
+
+ local destdir=$1
+ local backuptype=$2
+ local dir="$destdir/$backuptype"
+ local tmpdir="$destdir/rotate.tmp"
+ local metadata="$destdir/metadata/$backuptype.1"
+
+(
+ $ssh_cmd <<EOF
+ ##### BEGIN REMOTE SCRIPT #####
+ if [ ! -d $destdir ]; then
+ echo "Creating destination directory $destdir on $host..."
+ mkdir -p $destdir
+ fi
+
+ if [ -d $dir.1 ]; then
+ if [ -f $metadata/created ]; then
+ echo "Warning: $dir.1 already exists. Overwriting contents."
+ else
+ echo "Warning: we seem to be resuming a partially written $dir.1"
+ fi
+ else
+ 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
+ fi
+ else
+ mkdir --parents $dir.1
+ if [ \$? == 1 ]; then
+ echo "Fatal: could not create directory $dir.1 on host $host"
+ exit 1
+ 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
+ #fi
+ fi
+ fi
+ [ -f $metadata/created ] && rm $metadata/created
+ [ -f $metadata/rotated ] && rm $metadata/rotated
+ ##### END REMOTE SCRIPT #######
+EOF
+) | (while read a; do passthru $a; done)
+
+}
+