chiark / gitweb /
Maintain an index of backup artifacts.
authorMark Wooding <mwooding@good.com>
Fri, 25 Jan 2013 18:33:03 +0000 (18:33 +0000)
committerMark Wooding <mwooding@good.com>
Fri, 25 Jan 2013 18:33:03 +0000 (18:33 +0000)
There's a new program `update-bkp-index' to create and refresh the
database from a backup volume, and some new pieces of `rsync-backup' to
update the index incrementally as artifacts are committed and expired.

Makefile.am
rsync-backup.8
rsync-backup.in
update-bkp-index.8 [new file with mode: 0644]
update-bkp-index.in [new file with mode: 0644]

index f116932d9aa4e9055bfb0bdee4163519bc6e623d..4fa8227936a75667b60b38ba7a3dffad8c1641f0 100644 (file)
@@ -47,6 +47,7 @@ SUBSTVARS = \
        sysconfdir="$(sysconfdir)" \
        mntbkpdir="$(mntbkpdir)" \
        fshashdir="$(fshashdir)" \
+       pkglocalstatedir="$(localstatedir)/lib/bkp" \
        logdir="$(logdir)"
 
 V_SUBST = $(V_SUBST_$V)
@@ -73,6 +74,16 @@ rsync-backup: rsync-backup.in Makefile
                chmod +x rsync-backup.new && \
                mv rsync-backup.new rsync-backup
 
+sbin_SCRIPTS           += update-bkp-index
+dist_man_MANS          += update-bkp-index.8
+CLEANFILES             += update-bkp-index
+EXTRA_DIST             += update-bkp-index.in
+update-bkp-index: update-bkp-index.in Makefile
+       $(SUBST) >update-bkp-index.new \
+                       $(srcdir)/update-bkp-index.in $(SUBSTVARS) && \
+               chmod +x update-bkp-index.new && \
+               mv update-bkp-index.new update-bkp-index
+
 bin_SCRIPTS            += fshash
 dist_man_MANS          += fshash.1
 CLEANFILES             += fshash
index 271a9d6c3cea1495ccbc63b0074ade96171b7ede..aa7821fb974908d4642b9553538ddd55500442ea 100644 (file)
@@ -265,11 +265,30 @@ module.
 The default is
 .BR sha256 .
 .TP
+.B INDEXDB
+The name of a SQLite database initialized by
+.BR update-bkp-index (8)
+in which an index is maintained of which dumps are on which backup
+volumes.  If the file doesn't exist, then no index is maintained.  The
+default is
+.IB localstatedir /lib/bkp/index.db
+where
+.I localstatedir
+is the state directory configured at build time.
+.TP
 .B MAXLOG
 The number of log files to be kept for each filesystem.  Old logfiles
 are deleted to keep the total number below this bound.  The default
 value is 14.
 .TP
+.B METADIR
+The metadata directory for the currently mounted backup volume.
+The default is
+.IB mntbkpdir /meta
+where
+.I mntbkpdir
+is the backup mount directory configured at build time.
+.TP
 .B RSYNCOPTS
 Command-line options to pass to
 .BR rsync (1)
@@ -315,6 +334,11 @@ where
 .I mntbkpdir
 is the backup mount directory configured at build time.
 .TP
+.B VOLUME
+The name of the current volume.  If this is left unset, the volume name
+is read from the file
+.IB METADIR /volume
+once at the start of the backup run.
 .SS Hook functions
 The configuration file may define shell functions to perform custom
 actions at various points in the backup process.
@@ -451,6 +475,7 @@ format), together with associated files named
 .BR lvm (8),
 .BR rfreezefs (8),
 .BR rsync (1),
-.BR ssh (1).
+.BR ssh (1),
+.BR update-bkp-index (8).
 .SH AUTHOR
 Mark Wooding, <mdw@distorted.org.uk>
index 7519b125e2ee8455cf67afe793cbbd6adcebf81b..ac9cfdc36e32972a323f2bd034418e387422cc72 100644 (file)
@@ -148,6 +148,33 @@ hostpath () {
   fi
 }
 
+###--------------------------------------------------------------------------
+### Database operations.
+
+INDEXDB=@pkglocalstatedir@/index.db
+
+insert_index () {
+  host=$1 fs=$2 date=$3 vol=$4
+
+  if [ -f "$INDEXDB" ]; then
+    sqlite3 "$INDEXDB" <<EOF
+INSERT INTO idx (host, fs, date, vol)
+       VALUES ('$host', '$fs', '$date', '$vol');
+EOF
+  fi
+}
+
+delete_index () {
+  host=$1 fs=$2 date=$3
+
+  if [ -f "$INDEXDB" ]; then
+    sqlite3 "$INDEXDB" <<EOF
+DELETE FROM idx WHERE
+       host = '$host' AND fs = '$fs' AND $date = '$date';
+EOF
+  fi
+}
+
 ###--------------------------------------------------------------------------
 ### Snapshot handling.
 
@@ -481,8 +508,10 @@ EOF
 ### Actually taking backups of filesystems.
 
 STOREDIR=@mntbkpdir@/store
+METADIR=@mntbkpdir@/meta
 MAXLOG=14
 HASH=sha256
+unset VOLUME
 
 bkprc=0
 
@@ -528,6 +557,7 @@ expire_backups () {
        echo "delete $date"
        $verbose -n "   expire $date..."
        rm -rf $date $date.*
+       delete_index $host $fs $date
        $verbose " done"
        ;;
     esac
@@ -657,6 +687,7 @@ do_backup () {
       backup_precommit_hook $host $fs $date
       mv new $date
       mv new.fshash $date.fshash
+      insert_index $host $fs $date $VOLUME
       backup_commit_hook $host $fs $date
       mkdir hack
       ln -s $date hack/last
@@ -697,6 +728,12 @@ backup () {
     exit 15
   fi
 
+  ## Read the volume name if we don't have one already.  Again, this allows
+  ## the configuration file to provide a volume name.
+  case "${VOLUME+t}${VOLUME-nil}" in
+    nil) VOLUME=$(cat $METADIR/volume) ;;
+  esac
+
   ## Back up each requested file system in turn.
   for fs in "$@"; do
 
diff --git a/update-bkp-index.8 b/update-bkp-index.8
new file mode 100644 (file)
index 0000000..91f82c8
--- /dev/null
@@ -0,0 +1,61 @@
+.ie t .ds o \(bu
+.el .ds o o
+.de hP
+.IP
+\h'-\w'\fB\\$1\ \fP'u'\fB\\$1\ \fP\c
+..
+.TH update-bkp-index 8 "25 January 2013" rsync-backup
+.SH NAME
+update-bkp-index \- create or update the rsync-backup index database
+.SH SYNOPSIS
+The
+.BR rsync-backup (8)
+program can maintain an index database which keeps track of which dumps
+are on which backup volumes.
+.PP
+The
+.B update-bkp-index
+program updates this database from the currently mounted backup volume,
+creating or upgrading it if necessary.
+.PP
+The program ignores any command-line arguments it's given, but it makes
+use of a number of environment variables.
+.TP
+.B INDEXDB
+The name of a SQLite database initialized by
+.BR update-bkp-index (8)
+in which an index is maintained of which dumps are on which backup
+volumes.  If the file doesn't exist, then no index is maintained.  The
+default is
+.IB localstatedir /lib/bkp/index.db
+where
+.I localstatedir
+is the state directory configured at build time.
+.TP
+.B METADIR
+The metadata directory for the currently mounted backup volume.
+The default is
+.IB mntbkpdir /meta
+where
+.I mntbkpdir
+is the backup mount directory configured at build time.
+.TP
+.B STOREDIR
+Where the actual backup trees should be stored.  See the section on
+.B Archive structure
+below.
+The default is
+.IB mntbkpdir /store
+where
+.I mntbkpdir
+is the backup mount directory configured at build time.
+.TP
+.B VOLUME
+The name of the current volume.  The default is to read this from the
+file
+.IB METADIR /volume
+once at the start of the backup run.
+.SH SEE ALSO
+.BR rsync-backup (8).
+.SH AUTHOR
+Mark Wooding, <mdw@distorted.org.uk>
diff --git a/update-bkp-index.in b/update-bkp-index.in
new file mode 100644 (file)
index 0000000..23b64bd
--- /dev/null
@@ -0,0 +1,100 @@
+#! @BASH@
+###
+### Backup script
+###
+### (c) 2012 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the `rsync-backup' program.
+###
+### rsync-backup 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.
+###
+### rsync-backup 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 rsync-backup; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+
+mkdir -p @pkglocalstatedir@
+INDEXDB=@pkglocalstatedir@/index.db
+: ${STOREDIR=@mntbkpdir@/store}
+: ${METADIR=@mntbkpdir@/meta}
+
+if [ ! -f $STOREDIR/.rsync-backup-store ]; then
+  echo >&2 "$quis: no backup volume mounted"
+  exit 15
+fi
+: ${VOLUME=$(cat $METADIR/volume)}
+
+## If the database exists then we're OK.  (This will turn into a version
+## check and upgrade if the schema changes.)
+if [ ! -f "$INDEXDB" ]; then
+
+  ## Create the database.
+  rm -f "$INDEXDB.new"
+  sqlite3 "$INDEXDB.new" <<EOF
+CREATE TABLE meta (
+       version INTEGER NOT NULL);
+INSERT INTO meta (version) VALUES (0);
+
+CREATE TABLE idx (
+       host TEXT NOT NULL,
+       fs TEXT NOT NULL,
+       date TEXT NOT NULL,
+       vol TEXT NOT NULL,
+       PRIMARY KEY (host, fs, date));
+CREATE INDEX idx_byvol ON idx (vol);
+EOF
+
+  ## Done.
+  mv "$INDEXDB.new" "$INDEXDB"
+fi
+
+{
+  ## Do everything in a single transaction.  SQLite is pretty good at this,
+  ## and also it'll avoid updating the database until it sees a `COMMIT'
+  ## command, so if we fail halfway through we're still OK.  So it's safe to
+  ## start by removing all of the current records referring to this volume.
+  cat <<EOF
+BEGIN;
+DELETE FROM idx WHERE vol = '$VOLUME';
+EOF
+
+  ## Now work through the various filesystems.  This is a slightly cheesy way
+  ## of finding them.
+  for i in $STOREDIR/*/*/last; do
+
+    ## Parse out the host and filesystem names.
+    i=${i%/*}
+    fs=${i##*/} i=${i%/*}
+    host=${i##*/} i=${i%/*}
+
+    ## And work through the date list.
+    for j in $STOREDIR/$host/$fs/*; do
+      if [ -L "$j" ] || [ ! -d "$j" ]; then continue; fi
+      j=${j%/}
+      date=${j##*/}
+      cat <<EOF
+INSERT INTO idx (host, fs, date, vol)
+       VALUES ('$host', '$fs', '$date', '$VOLUME');
+EOF
+    done
+  done
+
+  ## Done.
+  cat <<EOF
+COMMIT;
+EOF
+} | sqlite3 "$INDEXDB"
+
+###----- That's all, folks --------------------------------------------------