#!/bin/bash # # Duplicity Control Tool -- description at far below (after "exec duplicity") if [ $# -lt 2 ] ; then cat <&2 ** Usage: dupltool [options] source url ** Source directory and store url are required, and the url ** MUST be of form "pexpect+scp:/\$HOST/\$REMOTEDIR" EOF exit 1 fi #-- Grab command line arguments DUPL=( $* ) STORE="${DUPL[-1]}" SOURCE="${DUPL[-2]}" #-- Confirm that the STORE url was of expected format if [ -n "${STORE##pexpect+scp:/*/*}" ] ; then echo "** Bad store URL: $STORE" >&2 echo "** Required format: pexpect+scp:/$HOST/$REMOTEDIR" >&2 exit 1 fi ## Helper function to read duplicity pathnames and prefix them with ## their from and to months, as in ${FROM}${TO}:${FILENAME} ## both $FROM and $TO are 6 digits (yyyymm). remote_from_to_month_prefix() { sftp $SFTP <<< "ls" | while read F ; do case "$F" in duplicity-full-signatures.*) echo "${F:26:6}${F:26:6}:$F" ; ;; duplicity-full.*) echo "${F:15:6}${F:15:6}:$F" ; ;; duplicity-inc.*) echo "${F:14:6}${F:34:6}:$F" ; ;; duplicity-new-signatures.*) echo "${F:25:6}${F:45:6}:$F" ; ;; esac done | sort -r } THISMONTH="$(date -u "+%Y%m")" # UTC date SFTP="${STORE##pexpect+scp://}" HOST="${SFTP%%/*}" REMOTEDIR="${SFTP#*/}" SFTP="sftp://$SFTP" # openssh-client=1:8.4p1-5+deb11u1 #SFTP="$HOST:$REMOTEDIR" # openssh-client=1:7.6p1-4ubuntu0.7 # Obtain remote pathnames with from-to prefix in reverse date order, # i.e., newest first. FILES=( $(remote_from_to_month_prefix) ) if [ -n "$FILES" ] ; then THATMONTH="${FILES[0]:0:6}" # to-month of newest if [ "$THISMONTH" != "$THATMONTH" ] ; then echo "** New monthly snapshot; stashing daily into .$THATMONTH" >&2 { # Generate command stream for sftp echo "mkdir .$THATMONTH" FX= for F in ${FILES[@]} ; do [ "${F:0:6}" != "$THATMONTH" ] && break [ -n "$FX" ] && echo "rename $FX .$THATMONTH/$FX" FX="${F#*:}" [ -z "${FX%duplicity-full*}" ] && break done } | sftp $SFTP echo "** Stashing into .$THATMONTH completed" >&2 fi fi exec duplicity ${DUPL[@]} exit 1 # jic # Duplicity Control Tool # ====================== # # This Duplicity Control Tool should be run as a daily anacron job (or # daily as a cron job) so as to make regular snapshots of a directory # tree using duplicity with the pexpect+scp backend. # # Ideally the backup store should be set up with password-less scp # access, preferrably as a non-root remote user, or at the very least # be accessed by means of a local ssh-agent. # # The store is then managed by this Duplicity Control Tool to keep a # multi-level history, which ends up as a succession of monthly # snapshots with a most recent tail of daily snapshots. Specifically, # at the first snapshot event each month, the store is revised by # stashing away the snapshots of the most recent previous month, to # leave the first one (or the most recent full) as the start point for # a new snapshot, which thus becomes a "monthly" snapshot. # # The chain of daily snapshots from that first (monthly or full) # snapshot remain stashed in the ".%Y%m" directory. # # Note that the Duplicity Control Tool considers "the month of the # last snapshot or full" to be "the previous month" regardless of how # many months back from today that might be, i.e., it will stash # snapshots just as if that month was the prior month. In other words, # if you would happen to have a huge temporal hole in the snapshot # series you might consider running a plain duplicity snapshot first # and then let the subsequent, regular runs of the Duplicity Control # Tool build its multi-level snapshot history henceforth. # # === These are the examples of the various duplicity snapshot files: # duplicity-full.20230128T010719Z.vol1.difftar.gz # duplicity-full-signatures.20230128T010719Z.sigtar.gz # duplicity-new-signatures.20221216T230345Z.to.20221217T230403Z.sigtar.gz # duplicity-inc.20230128T010719Z.to.20230130T082241Z.manifest # duplicity-inc.20230127T061825Z.to.20230130T081002Z.vol1.difftar.gz