#!/usr/bin/python # # archive-conf -- save off a config file in the dispatch-conf rcs archive # # Wayne Davison with code snagged from Jeremy Wohl's # dispatch-conf script and the portage chkcontents script. # import os, sys, string, shutil import portage FIND_EXTANT_CONTENTS = "find %s -name CONTENTS" RCS_BRANCH = '1.1.1' RCS_LOCK = 'rcs -ko -M -l' RCS_PUT = 'ci -t-"Archived config file." -m"archive-conf update."' RCS_GET = 'co' MANDATORY_OPTS = [ 'archive-dir' ] try: import fchksum def perform_checksum(filename): return fchksum.fmd5t(filename) except ImportError: import md5 def md5_to_hex(md5sum): hexform = "" for ix in xrange(len(md5sum)): hexform = hexform + "%02x" % ord(md5sum[ix]) return string.lower(hexform) def perform_checksum(filename): f = open(filename, 'rb') blocksize=32768 data = f.read(blocksize) size = 0L sum = md5.new() while data: sum.update(data) size = size + len(data) data = f.read(blocksize) return (md5_to_hex(sum.digest()),size) class save_conf: options = {} def doit(self): args = sys.argv[1:] content_files = [] md5_match_hash = {} self.read_my_config() todo_cnt = 0 for conf in args: if not os.path.isabs(conf): conf = os.path.abspath(conf) args[todo_cnt] = conf md5_match_hash[conf] = 0 todo_cnt += 1 # Find all the CONTENT files in /var/db/pkg. content_files += os.popen(FIND_EXTANT_CONTENTS % ('/var/db/pkg')).readlines() # Search for the saved md5 checksum of all the specified config files # and see if the current file is unmodified or not. try: for file in content_files: file = file.rstrip() try: contents = open(file, "r") except IOError, e: print "Unable to open %s: %s" % (file, e) sys.exit(1) lines = contents.readlines() for line in lines: items = string.split(line) if items[0] == 'obj': for conf in args: if items[1] == conf: stored = string.lower(items[2]) real = string.lower(perform_checksum(conf)[0]) if stored == real: md5_match_hash[conf] = 1 todo_cnt -= 1 if todo_cnt == 0: raise "Break" except "Break": pass for conf in args: s.do_rcs(conf, md5_match_hash[conf]) def do_rcs(self, curconf, md5_matches): """Archive existing config in rcs (on trunk). Then, if the file matches the MD5 checksum for the last package install, also check it in on the branch.""" archive = os.path.join(self.options['archive-dir'], curconf.lstrip('/')) try: os.makedirs(os.path.dirname(archive)) except: pass try: shutil.copy2(curconf, archive) except(IOError, os.error), why: print >> sys.stderr, 'archive-conf: Error copying %s to %s: %s; fatal' % \ (curconf, archive, str(why)) if os.path.exists(archive + ',v'): os.system(RCS_LOCK + ' ' + archive) os.system(RCS_PUT + ' ' + archive) if md5_matches: # There is probably a better way of checking for the branch... os.system(RCS_GET + ' -r' + RCS_BRANCH + ' ' + archive) has_branch = os.path.exists(archive) try: shutil.copy2(curconf, archive) except(IOError, os.error), why: print >> sys.stderr, 'archive-conf: Error copying %s to %s: %s; fatal' % \ (curconf, archive, str(why)) if has_branch: # Commit the config file onto the branch, just in case. os.system(RCS_LOCK + RCS_BRANCH + ' ' + archive) os.system(RCS_PUT + ' -r' + RCS_BRANCH + ' ' + archive) else: # Forcefully commit the config file onto the branch. os.system(RCS_PUT + ' -f -r' + RCS_BRANCH + ' ' + archive) def read_my_config(self): try: opts = portage.getconfig('/etc/dispatch-conf.conf') except: opts = None if not opts: print >> sys.stderr, 'archive-conf: Error reading /etc/dispatch-conf.conf; fatal' sys.exit(1) for key in MANDATORY_OPTS: if not opts.has_key(key): print >> sys.stderr, 'archive-conf: Missing option "%s" in /etc/dispatch-conf.conf; fatal' % (key,) if not (os.path.exists(opts['archive-dir']) and os.path.isdir(opts['archive-dir'])): print >> sys.stderr, 'archive-conf: Config archive dir [%s] must exist; fatal' % (opts['archive-dir'],) sys.exit(1) self.options = opts # run if len(sys.argv) > 1: s = save_conf() s.doit() else: print 'Usage: archive-conf /CONFIG/FILE [/CONFIG/FILE...]'