From ed22ffa9a4386df3b48c330bc11f9f8ffbe5bf47 Mon Sep 17 00:00:00 2001 From: Ralph Ronnquist Date: Fri, 3 Mar 2023 22:57:53 +1100 Subject: [PATCH 1/1] initial setup --- Makefile | 19 ++++++++ debian/changelog | 5 ++ debian/control | 17 +++++++ debian/copyright | 43 +++++++++++++++++ debian/rules | 25 ++++++++++ debian/source/format | 1 + duplicity-daily | 32 +++++++++++++ duplicity-daily.conf | 19 ++++++++ dupltool | 109 +++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 270 insertions(+) create mode 100644 Makefile create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100755 duplicity-daily create mode 100644 duplicity-daily.conf create mode 100755 dupltool diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7437a42 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +BINDIR = $(DESTDIR)/usr/bin +ETCDIR = $(DESTDIR)/etc +CRONDAILY = $(DESTDIR)/etc/cron.daily + +compile: + : # do nothing + +$(BINDIR)/% $(ETCDIR)/% $(CRONDAILY)/%: % + mkdir -p $$(dirname $@) + cp $< $@ + +INSTALLTARGETS = $(BINDIR)/dupltool +INSTALLTARGETS += $(ETCDIR)/duplicity-daily.conf +INSTALLTARGETS += $(CRONDAILY)/duplicity-daily + +install: $(INSTALLTARGETS) + +deb: + PREFIX= INCLUDE_PREFIX=/usr dpkg-buildpackage -us -uc --build=full diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..75e5887 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +buck (0.0.1) unstable; urgency=medium + + * Initial release + + -- Ralph Ronnquist Mon, 20 Feb 2023 14:15:35 +1100 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..d7dd6d7 --- /dev/null +++ b/debian/control @@ -0,0 +1,17 @@ +Source: buck +Section: unknown +Priority: optional +Maintainer: Ralph Ronnquist +Build-Depends: debhelper-compat (= 13) +Standards-Version: 4.5.1 +#Homepage: +#Vcs-Browser: https://salsa.debian.org/debian/buck +#Vcs-Git: https://salsa.debian.org/debian/buck.git +Rules-Requires-Root: no + +Package: buck +Architecture: all +Depends: ${misc:Depends}, bash, duplicity, bsdutils +Description: Backup control script for duplicity with multi-level history. + This script should be set up to run daily by root so as to make a daily + backup snapshot run to the ssh host named "backup". diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..fef46d5 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,43 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: buck +Upstream-Contact: +Source: + +Files: * +Copyright: + +License: + + + . + + +# If you want to use GPL v2 or later for the /debian/* files use +# the following clauses, or change it to suit. Delete these two lines +Files: debian/* +Copyright: 2023 Ralph Ronnquist +License: GPL-2+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. +# Please avoid picking licenses with terms that are more restrictive than the +# packaged work, as it may make Debian's contributions unacceptable upstream. +# +# If you need, there are some extra license texts available in two places: +# /usr/share/debhelper/dh_make/licenses/ +# /usr/share/common-licenses/ diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..59ea751 --- /dev/null +++ b/debian/rules @@ -0,0 +1,25 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +%: + dh $@ + + +# dh_make generated override targets +# This is example for Cmake (See https://bugs.debian.org/641051 ) +#override_dh_auto_configure: +# dh_auto_configure -- \ +# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/duplicity-daily b/duplicity-daily new file mode 100755 index 0000000..ce659ce --- /dev/null +++ b/duplicity-daily @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Backup directories daily. Configuration at /etc/duplicity-daily + +# Requires dupltool +[ -x /usr/bin/dupltool ] || exit 0 + +CONFIG=/etc/duplicity-daily.conf +grep -q ^ENABLE=yes $CONFIG || exit 0 + +# Detail backup options +OPTIONS=( $(grep ^OPTIONS= $CONFIG | sed 's/^OPTIONS=//') ) + +# The default backup target +TARGET="$(grep ^TARGET= -m1 $CONFIG | sed 's/^TARGET=//')" + +# Backup lines start with full path, optionally with =target +# No spaces in pathname or target, e.g. +# /home +# /opt/other/x=user@host/backup/x + +SRCS=( $(grep ^/ $CONFIG) ) +for SRC in "${SRCS[@]}" ; do + if [ "${SRC#*=}" = "$SRC" ] ; then + DST=$TARGET$SRC + else + DST="${SRC##*=}" + SRC="${SRC%%=*}" + fi + echo "** Backing up $SRC to $DST" + dupltool ${OPTIONS[*]} $SRC pexpect+scp://$DST +done |& logger -t backup diff --git a/duplicity-daily.conf b/duplicity-daily.conf new file mode 100644 index 0000000..cebf061 --- /dev/null +++ b/duplicity-daily.conf @@ -0,0 +1,19 @@ +# Backup is enabled with ENABLE=yes (exactly) +ENABLE=no + +# TARGET declares the default target which gets the source appended +# pexpect+scp://$TARGET$SRC +#TARGET=stuga@backup.lan.hemma + +#OPTIONS=--no-encryption + +# Each line starting with "/" declares a backup directory $SRC to be +# backup up with duplicity to "pexpect+scp://$TARGET$SRC" +# +# A line may also have target overide, as $SRC=$DST in which case the +# $SRC directory is backup up to pexpect+scp://$DST +# +#/root +#/etc +#/home +#/opt/uml/ceres=stuga@backup.lan.hemma/ceres diff --git a/dupltool b/dupltool new file mode 100755 index 0000000..c72b2c7 --- /dev/null +++ b/dupltool @@ -0,0 +1,109 @@ +#!/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" + +# 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]:6: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 -- 2.39.2