csbackgen.py 7.86 KB
Newer Older
1
2
#!/usr/bin/env python
## @file csbackgen.py
3
# @brief Generate checksum files.
4
5
6
7
8
9
10
# 
# -----------------------------------------------------------------------------
# 
# $Id$
# @author Daniel Armbruster
# \date 11/09/2011
# 
11
# Purpose: Generate checksum 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
# 02/01/2012  V0.1  Daniel Armbruster
34
# 05/01/2012  V0.2  added logging flag to enable a file logging mechanism
35
# 10/01/2012  V0.3  introduced --hash flag
36
37
38
# 
# =============================================================================
 
39
40
41
42
import getopt
import os
import sys
import re
43
import pwd
Daniel Armbruster's avatar
Daniel Armbruster committed
44
import logging
45
import csfile
46
import csbacklog
47

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

54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# -----------------------------------------------------------------------------
class Error(Exception):

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

  def display(self):
    sys.stderr.write("csbackgen (ERROR): " + self.msg + "\n")


class Usage(Error):

  def display(self):
    usage_text = "Version: "+__version__+"\nLicense: "+__license__+ \
      "\n"+__subversion__+"\nAuthor: "+__author__+ """
 Usage: csbackgen [-v|--verbose] [-e REGEX [-e REGEX [...]]]
70
                  [-R|--notrecursive] [-d|--debug] [-f|--followlinks]
71
                  [-t|--target DIR] [-l|--logging] [--hash ARG] PATH
72
73
74
75
76
77
78
79
80
81
82
83
    or: csbackgen -h|--help\n"""
    sys.stderr.write("csbackgen: " + 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: csbackgen [-v|--verbose] [-e REGEX [-e REGEX [...]]]
84
                  [-R|--notrecursive] [-d|--debug] [-f|--followlinks] 
85
                  [-t|--target ROOTDIR] [-l|--logging] [--hash ARG] PATH
86
87
    or: csbackgen -h|--help
-------------------------------------------------------------------------------
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
 -v|--verbose         Be verbose.
 -h|--help            Display this help.
 -e REGEX             While generating a checksumfile exclude files matching
                      REGEX(s).
 -R|--notrecursive    Do not generate checksumfiles for 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|--target ROOTDIR  Root target directory for checksumfile. The checksumfile
                      will be put to the appropiated location as the files had
                      in PATH or rather Path's subdirectories. So target must
                      have the same subdirectory structure as PATH.
 -l|--logging         Switch on logging to files. Logfiles will be located in
                      ~/.csback/log/.
 --hash ARG           Set the hash function algorithm. Valid values are:
                      sha224, sha256, sha384, sha512. (default: sha256)
 PATH                 Path to generate checksumfile(s) for including its
                      subdirectories if option '-R' is not set.
107
108
109
110
-------------------------------------------------------------------------------
csbackgen.py will either generate a checksumfile if still no checksumfile is
available or in case there is an existing checksumfile csbackgen.py will append
the not yet registered files to the current checksumfile. In the latter case
111
112
113
114
csbackgen.py is working in its update mode.
Notice that in case PATH contains subdirectories and either option '-R' is set
or the subdirectory is excluded by a matching regular expression every
subdirectory will contain a checksumfile.\n"""
115
116
  sys.stdout.write(help_text)

117
118
119
120
# -----------------------------------------------------------------------------
# global variables
VALIDHASHES = ['sha224', 'sha256', 'sha384', 'sha512']

121
122
# -----------------------------------------------------------------------------
def main(argv=None):
123
  # configure logger
124
125
  logger = csbacklog.CsbackLog('csbackgen')
  
126
  console = logging.StreamHandler()
127
128
  console.setFormatter(logging.Formatter( \
    '%(name)-8s [%(lineno)d]: %(levelname)-8s %(message)s'))
129

130
131
132
133
  if argv is None:
    argv = sys.argv
  try:
    try:
134
      opts, args = getopt.getopt(argv[1:], "vhe:Rdft:l", ["help", "verbose", \
135
136
        "notrecursive", "debug", "followlinks", "target=", "logging", \
        "hash="])
137
138
139
140
141
142
143
    except getopt.GetoptError as err:
      raise Usage(err.msg)
    verbose = False
    debugMode = False
    notRecursive = False
    followLinks = False
    regexes = []
144
    targetSet = False
145
    enableLogging = False
146
    hashfunc = 'sha256'
147
148
149
    for opt, arg in opts:
      if opt in ("-v", "--verbose"):
        verbose = True
150
        console.setLevel(logging.INFO)
151
152
153
154
155
156
157
158
      elif opt in ("-h", "--help"):
        sys.exit(help())
      elif opt in ("-R", "--notrecursive"):
        notRecursive = True
      elif opt in ("-e"):
        regexes.append(arg)
      elif opt in ("-d", "--debug"):
        debugMode = True
159
        console.setLevel(logging.DEBUG)
160
161
      elif opt in ("-f", "--followlinks"):
        followLinks = True
162
163
      elif opt in ("-t", "--target"):
        targetSet = True
164
        targetDirectory = arg
165
166
      elif opt in ("-l", "--logging"):
        logger.configure()
167
168
169
170
171
      elif opt in ("--hash",):
        if arg in VALIDHASHES:
          hashfunc = arg
        else:
          raise Usage("Invalid argument (hash).")
172
173
      else:
        raise Usage("Unhandled option chosen.")
174
175

    if verbose or debugMode:
176
      logger.addHandler(console)
177
178

    # fetch arguments   
179
    if 1 == len(args):
180
      srcpath = str(args[0]).rstrip(os.sep)+os.sep
181
182
    else:
      raise Usage("Invalid argument.")
183
184
    if not os.path.isdir(srcpath):
      raise Usage("PATH not a valid directory.")
Daniel Armbruster's avatar
Daniel Armbruster committed
185

186
187
188
    # major part
    logger.getLogger().debug("Start collecting subdirectories ...")
    dirs = [srcpath]
189
    if not targetSet:
190
      targetDirectory = srcpath
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
      targetdirs = dirs
    else:
      targetdirs = [targetDirectory]
      if not os.path.isdir(targetDirectory):
        raise Usage("Target directory not a valid directory.")
    # recursive
    if not notRecursive:
      dirs.extend(csfile.getSubDirectories(srcpath, regexes, followLinks))
      if targetSet:
        targetdirs.extend([dir.replace(dirs[0],targetDirectory,1) \
          for dir in dirs[1:]])

    pathes = list(zip(targetdirs, dirs))
    logger.getLogger().debug("Directories arranged.")

    logger.getLogger().info("Start updating checksumfile(s) ...")
    for path in pathes:
      logger.getLogger().debug( \
        "Updating checksumfile in '{0}' with files from '{1}' ...".format( \
        path[0], path[1]))
      csfile.CsFile(path[0], path[1], hashfunc=hashfunc).update(regexes)
Daniel Armbruster's avatar
Daniel Armbruster committed
212

213
214
215
216
  except Usage as err:
    err.display()
    return 2
  except csfile.CsFileError as err:
217
    logger.getLogger().error("%s", err.msg)
218
219
220
    err.display()
    return 2
  else:
221
    logger.getLogger().info("Checksumfile(s) updated.")
222
223
224
225
226
227
228
229
    return 0
    

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

# ----- END OF csbackgen.py -----