Better error handling
[sitarba.git] / shbackup
index ca00a1305bf04a3d6178cbd7ea1c24c244573a8b..cbd5396cc897636c93a272c4df36caae195c4818 100755 (executable)
--- a/shbackup
+++ b/shbackup
@@ -52,9 +52,11 @@ class Backup:
 class Config:
     """Encapsules the configuration for the backup program."""
 
-    class ReadException(Exception):
+    class ReadError(RuntimeError):
         """An exception raised when reading configurations."""
-        pass
+        def __init__(self, value):
+            self.value = value
+            self.message = value
 
     class FileSet:
         """A fileset has a name and a list of directories."""
@@ -65,7 +67,7 @@ class Config:
         def __str__(self):
             return "[name: " + self.name + ", dirs: " + str(self.dirs) + "]"
 
-    formats = ["tar.gz", "tar.bz2", "tar.xz" ]
+    formats = ["tar", "tar.gz", "tar.bz2", "tar.xz" ]
 
     # Filename where checksum of config is saved
     checksumfn = "checksum"
@@ -92,20 +94,22 @@ class Config:
         """Read configuration from file"""
 
         if not os.path.isfile(filename):
-            raise Config.ReadException("No file '" + filename + "'.")
+            raise Config.ReadError("Cannot read config file '" + filename + "'.")
 
         config = configparser.RawConfigParser()
         config.read(filename)
 
         for reqsec in ["destination"]:
             if not config.has_section(reqsec):
-                raise Config.ReadException("Section '" + reqsec + "' is missing.")
+                raise Config.ReadError("Section '" + reqsec + "' is missing.")
 
         self.directory = config.get("destination", "directory")
+        if not os.path.isdir(self.directory):
+            raise Config.ReadError("Directory '{0}' does not exist.".format(self.directory))
 
         self.format = config.get("destination", "format")
         if not self.format in Config.formats:
-            raise Config.ReadException("Invalid 'format' given.")
+            raise Config.ReadError("Invalid 'format' given.")
 
 
         if config.has_section("history"):
@@ -113,24 +117,27 @@ class Config:
                 if opt.startswith("keep"):
                     epoch = opt[4:]
                     if not epoch in RealEpoch.keys():
-                        raise Config.ReadException("Invalid option 'keep" + epoch + "'.")
-                    self.epochkeeps[epoch] = int(config.getint("history", opt))
+                        raise Config.ReadError("Invalid option 'keep" + epoch + "'.")
+                    try:
+                        self.epochkeeps[epoch] = int(config.getint("history", opt))
+                    except ValueError:
+                        raise Config.ReadError("Invalid integer given for '" + opt + "'.")
                 elif opt.startswith("mode"):
                     epoch = opt[4:]
                     if not epoch in RealEpoch.keys():
-                        raise Config.ReadException("Invalid option 'mode" + epoch + "'.")
+                        raise Config.ReadError("Invalid option 'mode" + epoch + "'.")
                     self.epochmodes[epoch] = config.get("history", opt)
                     if not self.epochmodes[epoch] in Mode:
-                        raise Config.ReadException("Invalid mode given.")
+                        raise Config.ReadError("Invalid mode given.")
                 else:
-                    raise Config.ReadException("Invalid option '" + opt + "'.")
+                    raise Config.ReadError("Invalid option '" + opt + "'.")
 
         if config.has_section("input"):
             for opt in config.options("input"):
                 if opt.startswith("exclude"):
                     self.exclpatterns += [ config.get("input", opt) ]
                 else:
-                    raise Config.ReadException("Invalid option '" + opt + "'.")
+                    raise Config.ReadError("Invalid option '" + opt + "'.")
 
         for sec in config.sections():
             if sec in ["destination", "history", "input"]:
@@ -141,12 +148,12 @@ class Config:
 
                 for opt in config.options(sec):
                     if not opt.startswith("dir"):
-                        raise Config.ReadException("Unknown option '" + opt + "'.")
+                        raise Config.ReadError("Unknown option '" + opt + "'.")
                     else:
                         dirs += [config.get(sec, opt)]
                 self.sets += [Config.FileSet(name, dirs)]
             else:
-                raise Config.ReadException("Unknown section '" + sec + "'.")
+                raise Config.ReadError("Unknown section '" + sec + "'.")
 
         # Compute checksum of config file
         m = hashlib.sha1()
@@ -248,28 +255,12 @@ class BackupManager:
             taropts += ["--exclude", pat]
 
         tarargs = [tarpath] + taropts + ["-f", fsfn] + fileset.dirs
-        print("tarargs: ", tarargs)
-        tarp = subprocess.Popen( tarargs, \
-                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-
-        while tarp.poll():
-            l = tarp.stdout.readline()
-            if len(l) > 0:
-                print(l.decode(), end="")
-            l = tarp.stderr.readline()
-            if len(l) > 0:
-                print(l.decode(), end="")
-
-        for l in tarp.stdout.readlines():
-            print(l.decode(), end="")
-
-        for l in tarp.stderr.readlines():
-            print(l.decode(), end="")
+        #print("tarargs: ", tarargs)
+        tarp = subprocess.Popen( tarargs )
 
         rett = tarp.wait()
         if rett != 0:
             print(tarpath + " returned with exit status " + str(rett) + ":")
-            print( tarp.stderr.read().decode() )
 
 
     def backup(self, epoch=None, mode=None):
@@ -380,12 +371,12 @@ def printUsage():
     print("  prune                      prune outdated/old backups")
     print("")
     print("Options:")
-    print("  -C <configfile>            use given configuration file")
+    print("  -h, --help                 print this usage text")
+    print("  -c, --conf <configfile>    use given configuration file")
     print("                             default: /etc/shbackup.conf")
-    print("  -m, --mode <mode>          override mode: full, diff, or incr")
     print("  -e, --epoch <epoch>        force to create backup for given epoch:")
     print("                             year, month, week, day, hour, sporadic")
-    print("  -h, --help                 print this usage text")
+    print("  -m, --mode <mode>          override mode: full, diff, or incr")
 
 
 if __name__ == "__main__":
@@ -404,7 +395,7 @@ if __name__ == "__main__":
             printUsage()
             exit(0)
 
-        elif opt in ["-C", "--config"]:
+        elif opt in ["-c", "--conf"]:
             i += 1
             conffn = sys.argv[i]
 
@@ -444,11 +435,8 @@ if __name__ == "__main__":
         if cmd == "prune":
             man.prune()
 
-    except Config.ReadException as e:
-        print("Error reading config file: ", end="")
-        for a in e.args:
-            print(a, end=" ")
-        print()
+    except (Config.ReadError, configparser.DuplicateOptionError) as e:
+        print("Error reading config file: " + e.message)