#!/usr/bin/env python ## @file csbackchk.py # @brief Check checksums or rather integrity of corresponding files. # # ----------------------------------------------------------------------------- # # $Id$ # @author Daniel Armbruster # \date 03/01/2012 # # Purpose: Check checksums or rather integrity of corresponding files. # # ---- # This file is part of csback. # # csback 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. # # csback 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 csback. If not, see . # ---- # # Copyright (c) 2012 by Daniel Armbruster # # REVISIONS and CHANGES # 03/01/2012 V0.1 Daniel Armbruster # 10/01/2012 V0.2 adjustments because every subdirectory now contains its own # checksumfile. # # ============================================================================= import getopt import os import sys import pwd import logging import csfile import csbacklog __version__ = "V0.2" __subversion__ = "$Id$" __license__ = "GPLv2" __author__ = "Daniel Armbruster" __copyright__ = "Copyright (c) 2012 by Daniel Armbruster" # ----------------------------------------------------------------------------- class Error(Exception): def __init__(self, line, msg): self.line = str(line) self.msg = msg def display(self): sys.stderr.write("csbackchk (ERROR): " + self.msg + "\n") sys.stderr.write("triggered in line: " + self.line + "\n") class Usage(Error): def display(self): usage_text = "Version: "+__version__+"\nLicense: "+__license__+ \ "\n"+__subversion__+"\nAuthor: "+__author__+ """ Usage: csbackchk [-v|--verbose] [-e REGEX [-e REGEX [...]]] [-R|--notrecursive] [-d|--debug] [-f|--followlinks] [-t|--tolerant] [-l|--logging] [SOURCEPATH] PATH or: csbackchk -h|--help\n""" sys.stderr.write("csbackchk [line "+self.line+"]: "+self.msg+"\n") sys.stderr.write(usage_text) # ----------------------------------------------------------------------------- def help(): """ Printing helptext to stdout. """ help_text = "Version: "+__version__+"\nLicense: "+__license__+"\n"+ \ __subversion__+"\nAuthor: "+__author__+""" Usage: csbackchk [-v|--verbose] [-e REGEX [-e REGEX [...]]] [-R|--notrecursive] [-d|--debug] [-f|--followlinks] [-t|--tolerant] [-l|--logging] [SOURCEPATH] PATH or: csbackchk -h|--help ------------------------------------------------------------------------------- -v|--verbose Be verbose. -h|--help Display this help. -e REGEX While checking a checksumfile(s) exclude files and directories matching REGEX(s). -R|--notrecursive Do not search in subdirectories of PATH. -d|--debug Debug mode. Be really verbose. -f|--followlinks Follow symbolic links. Only available if option -R is not set. Note that this option can lead to infinite recursion. -t|--tolerant Be tolerant. While checking don't report anything if a file listed in the checksumfile is missing. This flag is useful if a test of a directory is executed but this directory is realized e.g. as a ring buffer. -l|--logging Switch on logging to files. Logfiles will be located in ~/.csback/log/. SOURCEPATH Optional sourcepath for comparison with files backed up in PATH. PATH and its subdirectories (if option '-R' had not been selected) must contain the csback checksumfile(s). Note that the directory structure (if option '-R' is not set) bellow SOURCEPATH should be equal to those in PATH because otherwise the files won't be found. If SOURCEPATH is not passed a check of files located in PATH with its checksumfiles will be performed. PATH Path to perform check with its checksumfile(s). If option '-R' had not been set the check will be performed additionally for PATHs' subdirectories. """ sys.stdout.write(help_text) # ----------------------------------------------------------------------------- def main(argv=None): # configure logger logger = csbacklog.CsbackLog('csbackchk') console = logging.StreamHandler() console.setFormatter(logging.Formatter( \ '%(name)-8s [%(lineno)d]: %(levelname)-8s %(message)s')) # fetch arguments if argv is None: argv = sys.argv try: try: opts, args = getopt.getopt(argv[1:], "vhe:Rdftl", ["help", "verbose", \ "notrecursive", "debug", "followlinks", "tolerant", "logging"]) except getopt.GetoptError as err: raise Usage(135,err.msg) verbose = False debugMode = False notRecursive = False followLinks = False beTolerant = False regexes = [] # collect arguments for opt, arg in opts: if opt in ("-v", "--verbose"): verbose = True console.setLevel(logging.INFO) elif opt in ("-h", "--help"): sys.exit(help()) return 0 elif opt in ("-R", "--notrecursive"): notRecursive = True elif opt in ("-e"): regexes.append(arg) elif opt in ("-d", "--debug"): debugMode = True console.setLevel(logging.DEBUG) elif opt in ("-f", "--followlinks"): followLinks = True elif opt in ("-t", "--tolerant"): beTolerant = True elif opt in ("-l", "--logging"): logger.configure() else: raise Usage(164,"Unhandled option chosen.") if verbose or debugMode: logger.addHandler(console) if 1 == len(args) or 2 == len(args) and \ args[0].rstrip(os.sep)+os.sep == args[1].rstrip(os.sep)+os.sep: sourcepath = str(args[0]).rstrip(os.sep)+os.sep sourceDirs = [sourcepath] inputpath = sourcepath inputDirs = sourceDirs elif 2 == len(args): sourcepath = str(args[0]).rstrip(os.sep)+os.sep sourceDirs = [sourcepath] inputpath = str(args[1]).rstrip(os.sep)+os.sep inputDirs = [inputpath] else: raise Usage(180,"Invalid argument(s).") # major part logger.getLogger().debug("Collecting subdirectories ...") # recursive if not notRecursive: sourceDirs.extend( \ csfile.getSubDirectories(sourcepath, regexes, followLinks)) if sourceDirs[0] != inputDirs[0]: inputDirs.extend( \ csfile.getSubDirectories(inputpath, regexes, followLinks)) if len(sourceDirs) != len(inputDirs): raise Error(194, \ "Directory structure of inputpath and sourcepath different.") if not csfile.hasCsFile(inputpath): raise Error(198,"PATH does not contain a checksumfile.") paths = list(zip(inputDirs, sourceDirs)) logger.getLogger().info("Start checking checksums ...") for path in paths: checksumfile = csfile.CsFile(path[0], path[1]) checksumfile.read() checksumfile.check(regexes, beTolerant) except Usage as err: err.display() return 2 except Error as err: logger.getLogger().error("message: %s [line %s]", err.msg, err.line) err.display() return 2 except csfile.CsFileError as err: logger.getLogger().error("message: %s [line %s]", err.msg, err.line) err.display() return 2 else: logger.getLogger().info("Checks performed.") return 0 # ----------------------------------------------------------------------------- if __name__ == "__main__": sys.exit(main()) # ----- END OF csbackchk.py -----