updating version number
[sitarba.git] / shbackup
index e97dbf5182790944bfd2eb1c26987d6e8503ae2e..e57e2508500fe22a966357413cb6d398c73a9f20 100755 (executable)
--- a/shbackup
+++ b/shbackup
@@ -1,7 +1,7 @@
 #!/usr/bin/python3
 """A simple backup solution."""
 
-__version__ = "0.1"
+__version__ = "2.0"
 __author__ = "Stefan Huber"
 
 import datetime
@@ -171,8 +171,9 @@ class Config:
     checksumfn = "checksum"
 
     def __init__(self):
-        self.directory = None
-        self.format = self.formats[0]
+        self.backupdir = None
+        self.format = self.formats[1]
+        self.tarbin = "/bin/tar"
         self.excludes = []
         self.sets = []
         self.checksum = None
@@ -181,8 +182,9 @@ class Config:
 
 
     def __repr__(self):
-        return "[directory: " + self.directory + \
+        return "[backupdir: " + self.backupdir + \
                                  ", format: " + self.format + \
+                                 ", tarbin: " + self.tarbin + \
                                  ", excludes: " + repr(self.excludes) + \
                   ", epochs: " + repr(self.epochs) + \
                                  ", sets: " + repr(self.sets) + "]"
@@ -197,23 +199,21 @@ class Config:
         return realepochs
 
 
-    def _read_destination(self, config, sec):
+    def _read_global(self, config, sec):
         for opt in config.options(sec):
-            if opt=="directory":
-                self.directory = config.get(sec, opt)
-                if not os.path.isdir(self.directory):
-                    raise Config.ReadError("Directory '{0}' does not exist.".format(self.directory))
+            if opt=="backupdir":
+                self.backupdir = config.get(sec, opt)
+                if not os.path.isdir(self.backupdir):
+                    raise Config.ReadError("Backupdir '{0}' does not exist.".format(self.backupdir))
             elif opt=="format":
                 self.format = config.get(sec, opt)
                 if not self.format in Config.formats:
                     raise Config.ReadError("Invalid 'format' given.")
-            else:
-                raise Config.ReadError("Unknown option '{0}'.".format(opt))
-
-
-    def _read_global(self, config, sec):
-        for opt in config.options(sec):
-            if opt.startswith("exclude"):
+            elif opt=="tarbin":
+                self.tarbin = config.get(sec, opt)
+                if not os.path.isfile(self.tarbin):
+                    raise Config.ReadError("Tar binary '{0}' does not exist.".format(self.tarbin))
+            elif opt.startswith("exclude"):
                 self.excludes += [ config.get(sec, opt) ]
             else:
                 raise Config.ReadError("Unknown option '{0}'.".format(opt))
@@ -241,7 +241,7 @@ class Config:
                 if not e.mode in Modes:
                     raise Config.ReadError("Invalid mode '{0}'.".format(e.mode))
 
-            elif opt=="timedelta":
+            elif opt=="timespan":
                 if name in Epoch.units:
                     raise Config.ReadError("The time delta of a standard epoch " + \
                             "is not supposed to be redefined. ")
@@ -251,7 +251,7 @@ class Config:
                     e.unit = unit
                     e.mult = mult
                 except ValueError as e:
-                    raise Config.ReadError("Invalid timedelta '{0}': {1}".format(td, str(e)))
+                    raise Config.ReadError("Invalid timespan '{0}': {1}".format(td, str(e)))
 
             elif opt.startswith("exclude"):
                 e.excludes += [config.get(sec, opt)]
@@ -290,16 +290,13 @@ class Config:
         config = configparser.RawConfigParser()
         config.read(filename)
 
-        for reqsec in ["destination"]:
+        for reqsec in ["global"]:
             if not config.has_section(reqsec):
                 raise Config.ReadError("Mandatory section '" + reqsec + "' is missing.")
 
         for sec in config.sections():
 
-            if sec=="destination":
-                self._read_destination(config, sec)
-
-            elif sec=="global":
+            if sec=="global":
                 self._read_global(config, sec)
 
             elif sec.startswith("epoch "):
@@ -311,8 +308,8 @@ class Config:
             else:
                 raise Config.ReadError("Unknown section '" + sec + "'.")
 
-        if self.directory == None:
-            raise Config.ReadError("No destination directory set.")
+        if self.backupdir == None:
+            raise Config.ReadError("No backup directory set.")
 
 
         # Compute checksum of config file
@@ -325,7 +322,7 @@ class Config:
             f.close()
 
         try:
-            f = open(os.path.join(self.directory, self.checksumfn), 'r')
+            f = open(os.path.join(self.backupdir, self.checksumfn), 'r')
             self.lastchecksum = f.read().strip()
             f.close()
         except IOError:
@@ -341,10 +338,10 @@ class BackupManager:
 
 
     def listAllDirs(self):
-        """List all dirs in destination directory"""
+        """List all dirs in backupdir"""
 
         # Get all entries
-        basedir = self.conf.directory
+        basedir = self.conf.backupdir
         dirs = os.listdir(basedir)
         # Filter directories
         return [ d for d in dirs if os.path.isdir(os.path.join(basedir, d)) ]
@@ -393,9 +390,7 @@ class BackupManager:
         logfile = logging.getLogger('backuplog')
         logfile.info("Running file set: " + fileset.name)
 
-        tarpath = "/bin/tar"
         fsfn = os.path.join(targetdir, fileset.name) + "." + self.conf.format
-
         taropts = []
 
         # Add the since date, if given
@@ -414,7 +409,7 @@ class BackupManager:
         taropts += ["-C", "/"] + [ "./" + d.lstrip("/") for d in fileset.dirs]
 
         # Launch the tar process
-        tarargs = [tarpath] + ["-cpvaf", fsfn] + taropts
+        tarargs = [self.conf.tarbin] + ["-cpvaf", fsfn] + taropts
         logfile.debug("tar call: " + " ".join(tarargs))
         tarp = subprocess.Popen( tarargs, bufsize=-1, \
                 stdout=subprocess.PIPE, stderr=subprocess.PIPE )
@@ -444,7 +439,8 @@ class BackupManager:
         if rett != 0:
             for l in errmsg.decode().split("\n"):
                 logfile.error(l)
-            logfile.error(tarpath + " returned with exit status " + str(rett) + ".")
+            logfile.error(self.conf.tarbin + " returned with exit status " + \
+                   str(rett) + ".")
 
 
     def backup(self, epoch=None, mode=None):
@@ -492,8 +488,8 @@ class BackupManager:
         if yesno == "n":
             return
 
-        # Create new target directory
-        basedir = self.conf.directory
+        # Create new backup directory
+        basedir = self.conf.backupdir
         dirname = Backup.getDirName(now, epoch, mode)
         tmpdirname = dirname + ("-%x" % (random.random()*2e16) )
         targetdir = os.path.join(basedir, tmpdirname)
@@ -567,7 +563,7 @@ class BackupManager:
             logging.info("No stale/outdated entries to remove.")
             return
 
-        basedir = self.conf.directory
+        basedir = self.conf.backupdir
         yesno = self.ask_user_yesno("Remove entries marked by '*'? [y, N] ")
         if yesno == "y":
             for d in removeDirs:
@@ -600,11 +596,11 @@ def printUsage():
     print("")
     print("Options:")
     print("  -h, --help                 print this usage text")
-    print("  -c, --conf <configfile>    use given configuration file")
+    print("  -c, --conf FILE            use given configuration file")
     print("                             default: /etc/shbackup.conf")
-    print("  -e, --epoch <epoch>        force to create backup for given epoch:")
-    print("                             year, month, week, day, hour, sporadic")
-    print("  -m, --mode <mode>          override mode: full, diff, or incr")
+    print("  -e, --epoch EPOCH          force to create backup for given epoch, which")
+    print("                             can be 'sporadic' or one of the configured epochs")
+    print("  -m, --mode MODE            override mode: full, diff, or incr")
     print("  -v, --verbose              be more verbose and interact with user")
     print("  --verbosity LEVEL          set verbosity to LEVEL, which can be")
     print("                             error, warning, info, debug")