csbackchk.py 7.65 KB
Newer Older
1
2
#!/usr/bin/env python
## @file csbackchk.py
3
# @brief Check checksums or rather integrity of corresponding files. 
4
5
6
7
8
# 
# -----------------------------------------------------------------------------
# 
# $Id$
# @author Daniel Armbruster
9
# \date 03/01/2012
10
# 
11
# Purpose: Check checksums or rather integrity of corresponding files.
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#
# ----
# 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 <http://www.gnu.org/licenses/>.
# ----
# 
30
# Copyright (c) 2012 by Daniel Armbruster
31
32
# 
# REVISIONS and CHANGES 
33
# 03/01/2012  V0.1  Daniel Armbruster
34
35
36
# 
# =============================================================================
 
37
38
39
40
41
42
43
import getopt
import os
import sys
import pwd
import logging
import logging.config
import csfile
44

45
46
47
48
49
__version__ = "V0.1"
__subversion__ = "$Id$"
__license__ = "GPLv2"
__author__ = "Daniel Armbruster"
__copyright__ = "Copyright (c) 2012 by Daniel Armbruster"
50

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# -----------------------------------------------------------------------------
class Error(Exception):

  def __init__(self, msg):
    self.msg = msg

  def display(self):
    sys.stderr.write("csbackchk (ERROR): " + self.msg + "\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] [SOURCEPATH] PATH
    or: csbackchk -h|--help\n"""
    sys.stderr.write("csbackchk: " + 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] [SOURCEPATH] PATH
    or: csbackchk -h|--help
-------------------------------------------------------------------------------
 -v|--verbose       Be verbose.
 -h|--help          Display this help.
 -e REGEX           While checking a checksumfile exclude files matching
Daniel Armbruster's avatar
Daniel Armbruster committed
88
                    REGEX(s).
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 -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.
 SOURCEPATH         Optional sourcepath for comparison with files backed up in
                    PATH. PATH and its subdirectories (if option '-R' is not 
                    set) must contain the csback checksumfile(s).
                    Note that the directory structure (if option '-R' is not
                    set) bellow SOURCEPATH must be equal to those in PATH.
                    If SOURCEPATH is not passed a check of files located in
                    PATH with its checksumfiles will be performed.
 PATH               Path to perform check for.
"""
  sys.stdout.write(help_text)
def main(argv=None):
  # configure logger
  logInfo = {'hostname': os.uname()[1], \
Daniel Armbruster's avatar
Daniel Armbruster committed
111
    'user': pwd.getpwuid(os.getuid()).pw_name, \
112
    'pid': os.getpid()}
Daniel Armbruster's avatar
Daniel Armbruster committed
113
114
115
116
  csfile.csfileLogInfo = logInfo
  csfile.csfileLoggerName = 'csbackgen.csfile'
  log = logging.getLogger('csbackgen') 
  logger = logging.LoggerAdapter(log, logInfo)
117
  logging.basicConfig(level=logging.DEBUG, \
Daniel Armbruster's avatar
Daniel Armbruster committed
118
119
    format='%(asctime)-15s %(hostname)-6s %(name)-8s[%(pid)s]: \
(%(user)-4s) %(levelname)-8s %(message)s')
120
121
  console = logging.StreamHandler()
  formatter = logging.Formatter( \
Daniel Armbruster's avatar
Daniel Armbruster committed
122
    '%(name)-8s [%(lineno)d]: %(levelname)-8s %(message)s')
123
124
125
126
127
128
  console.setFormatter(formatter)

  if argv is None:
    argv = sys.argv
  try:
    try:
Daniel Armbruster's avatar
Daniel Armbruster committed
129
130
      opts, args = getopt.getopt(argv[1:], "vhe:Rdft", ["help", "verbose", \
        "notrecursive", "debug", "followlinks", "tolerant"])
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
    except getopt.GetoptError as err:
      raise Usage(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
      else:
        raise Usage("Unhandled option chosen.")

    if verbose or debugMode:
      logger.addHandler(console)

    # fetch arguments
    if 1 == len(args):
      sourcepath = str(args[0]).rstrip(os.sep)+os.sep
      inputpath = sourcepath 
      inputDirectories = [inputpath]
      sourceDirectories = inputDirectories
    elif 2 == len(args):
      sourcepath = str(args[0]).rstrip(os.sep)+os.sep 
      inputpath = str(args[1]).rstrip(os.sep)+os.sep 
      inputDirectories = [inputpath]
      sourceDirectories = [sourcepath]
    else:
      raise Usage("Invalid argument(s).")

    if not notRecursive:
      logger.info("Collecting subdirectories for inputpath: %s",inputpath)
      inputDirectories.extend(csfile.getSubDirectories(inputpath, regexes, \
        followLinks))
      # case: different source directory chosen
      if inputDirectories[0] != sourceDirectories[0]:
        logger.info("Collecting subdirectories for sourcepath: %s",sourcepath)
        sourceDirectories.extend(csfile.getSubDirectories(sourcepath, \
          regexes, followLinks))
    else:
      logger.info("No recursion to subdirectories.")
    # check directory structure of both sourcepath and inputpath
    if inputDirectories[0] != sourceDirectories[0]:
      logger.info("Checking directory structure.")
      inTmp = [dir.rstrip(inputDirectories[0]) for dir in inputDirectories[1:]]
      srcTmp = [dir.rstrip(sourceDirectories[0]) \
        for dir in sourceDirectories[1:]]
      if inTmp != srcTmp:
        logger.error("Source directory does not contain the same directory \
          structure like the input directory.")
    directories = list(zip(inputDirectories, sourceDirectories))  
    for dirpair in directories:
      csfile.CsFile(dirpair[0]).check(regexes, dirpair[1], beTolerant)
      
  except Usage as err:
    err.display()
    return 2
  except csfile.CsFileError as err:
    err.display()
    return 2
  else:
    logger.info("Checks performed.")
    return 0
    

# -----------------------------------------------------------------------------
if __name__ == "__main__":
  sys.exit(main())

# ----- END OF csbackchk.py -----