chiark / gitweb /
New check-file option to test whether a volume is available
authorRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 10 Nov 2012 16:04:52 +0000 (16:04 +0000)
committerRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 10 Nov 2012 16:04:52 +0000 (16:04 +0000)
doc/CHANGES.html
doc/rsbackup.1
src/Conf.cc
src/Conf.h
src/MakeBackup.cc
src/Volume.cc
tests/Makefile.am
tests/backup
tests/check-file [new file with mode: 0755]
tests/setup.sh

index 3b91388..032c5cc 100644 (file)
       <li>The new <code>rsync-timeout</code> option allows a time
       limit to be imposed on a backup.</li>
 
+      <li>The new <code>check-file</code> option allows backups of a
+      volume to be suppressed when it is not available (for instance,
+      becuase it is only sometimes mounted).</li>
+
       <li><code>--verbose</code> (and therefore
       <code>--dry-run</code>) is now more verbose.</li>
 
index 2c74800..4347825 100644 (file)
@@ -342,6 +342,13 @@ See the rsync man page for full details.
 .B traverse
 Traverse mount points.
 This suppresses the rsync \fB\-\-one\-file\-system\fR option.
+.TP
+.B check-file \fIPATH\fR
+Checks that \fIPATH\fR exists before backing up the volume.
+\fIPATH\fR may be either an absolute path or a relative path (to the
+root of the volume).
+It need not be inside the volume though the usual use would be to
+check for a file which is always present there.
 .PP
 In addition, the following directives can be used within a volume
 stanza, and apply to just that volume:
index ce8f439..a4a24e1 100644 (file)
@@ -177,8 +177,14 @@ void Conf::readOneFile(const std::string &path) {
         if(bits.size() != 2)
           throw SyntaxError("wrong number of arguments to 'devices'");
         if(host == NULL)
-          throw SyntaxError("'host' command without 'volume'");
+          throw SyntaxError("'devices' command without 'volume'");
         context->devicePattern = bits[1];
+      } else if(bits[0] == "check-file") {
+        if(bits.size() != 2)
+          throw SyntaxError("wrong number of arguments to 'check-file'");
+        if(volume == NULL)
+          throw SyntaxError("'check-file' command without 'volume'");
+        volume->checkFile = bits[1];
       } else {
         throw SyntaxError("unknown command '" + bits[0] + "'");
       }
index 4e5c1a6..fd5c7e1 100644 (file)
@@ -444,6 +444,9 @@ public:
   /** @brief Traverse mount points if true */
   bool traverse;
 
+  /** @brief File to check before backing up */
+  std::string checkFile;
+
   /** @brief Return true if volume is selected */
   bool selected() const { return isSelected; }
 
@@ -458,6 +461,11 @@ public:
    */
   static bool valid(const std::string &n);
 
+  /** @brief Test if volume available
+   * @return true if volume is available
+   */
+  bool available() const;
+
   /** @brief Known backups of this volume */
   backups_type backups;
 
index bef9712..2ac3591 100644 (file)
@@ -374,8 +374,9 @@ static void backupVolume(Volume *volume, Device *device) {
 enum BackupRequirement {
   AlreadyBackedUp,
   NotThisDevice,
+  NotAvailable,
   BackupRequired
-}l;
+};
 
 // See whether VOLUME needs a backup on DEVICE
 static BackupRequirement needsBackup(Volume *volume, Device *device) {
@@ -401,6 +402,8 @@ static BackupRequirement needsBackup(Volume *volume, Device *device) {
        && backup->deviceName == device->name)
       return AlreadyBackedUp;           // Already backed up
   }
+  if(!volume->available())
+    return NotAvailable;
   return BackupRequired;
 }
 
@@ -430,6 +433,12 @@ static void backupVolume(Volume *volume) {
                        volume->name.c_str(),
                        device->name.c_str());
       break;
+    case NotAvailable:
+      if(command.verbose)
+        IO::out.writef("INFO: %s:%s is not available\n",
+                       host->name.c_str(),
+                       volume->name.c_str());
+      break;
     case NotThisDevice:
       if(command.verbose)
         IO::out.writef("INFO: %s:%s excluded from %s by device pattern\n",
index 90833e5..c77c45f 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright © 2011 Richard Kettlewell.
+// Copyright © 2011, 2012 Richard Kettlewell.
 //
 // This program is free software: you can redistribute it and/or modify
 // it under the terms of the GNU General Public License as published by
@@ -14,6 +14,8 @@
 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include <config.h>
 #include "Conf.h"
+#include "Subprocess.h"
+#include <cstdio>
 
 void Volume::select(bool sense) {
   isSelected = sense;
@@ -97,3 +99,24 @@ const Backup *Volume::mostRecentFailedBackup(const Device *device) const {
   }
   return result;
 }
+
+bool Volume::available() const {
+  if(!checkFile.size())
+    return true;
+  std::vector<std::string> cmd;
+  cmd.push_back("ssh");
+  if(parent->parent->sshTimeout > 0) {
+    char buffer[64];
+    snprintf(buffer, sizeof buffer, "%d", parent->parent->sshTimeout);
+    cmd.push_back(std::string("-oConnectTimeout=") + buffer);
+  }
+  cmd.push_back(parent->userAndHost());
+  cmd.push_back("test");
+  cmd.push_back("-e");
+  cmd.push_back(checkFile[0] == '/' ? checkFile : path + "/" + checkFile);
+  Subprocess sp(cmd);
+  sp.nullChildFD(1);
+  sp.nullChildFD(2);
+  int rc = sp.runAndWait(false);
+  return rc == 0;
+}
index d8f1290..39a48a9 100644 (file)
@@ -12,6 +12,6 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
-TESTS=backup prune retire-device retire-volume store
+TESTS=backup prune retire-device retire-volume store check-file
 EXTRA_DIST=${TESTS} setup.sh hook
 TESTS_ENVIRONMENT=bash
index d2f1af6..55a8b78 100755 (executable)
@@ -1,5 +1,5 @@
 #! /bin/bash
-# Copyright © 2011 Richard Kettlewell.
+# Copyright © 2011, 2012 Richard Kettlewell.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
diff --git a/tests/check-file b/tests/check-file
new file mode 100755 (executable)
index 0000000..41ae75c
--- /dev/null
@@ -0,0 +1,30 @@
+#! /bin/bash
+# Copyright © 2011, 2012 Richard Kettlewell.
+#
+# This program 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 <http://www.gnu.org/licenses/>.
+set -e
+. ${srcdir:-.}/setup.sh
+
+setup
+
+echo "| Backup is skipped if check-file missing"
+rm -f volume1/file1
+RSBACKUP_TODAY=1980-01-03 s ${RSBACKUP} --backup --verbose
+absent store1/host1/volume1
+compare volume2 store1/host1/volume2/1980-01-03
+absent store1/host1/volume3
+compare volume3 store2/host1/volume3/1980-01-03
+s ${RSBACKUP} -T -
+
+cleanup
index 0afe289..6debeac 100644 (file)
@@ -43,6 +43,7 @@ setup() {
   echo "    min-backups 1" >> config
   echo "    pre-backup-hook ${srcdir:-.}/hook" >> config
   echo "    post-backup-hook ${srcdir:-.}/hook" >> config
+  echo "    check-file file1" >> config
   echo "  volume volume2 $PWD/volume2" >> config
   echo "    min-backups 2" >> config
   echo "  volume volume3 $PWD/volume3" >> config