"Fossies" - the Fresh Open Source Software Archive

Member "dconf-0.5.1/dconf" (30 Jul 2006, 14698 Bytes) of package /linux/privat/old/dconf-0.5.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.


    1 #!/usr/bin/env python
    2 
    3 import os, sys, glob, re, shutil, getopt, popen2, time, fnmatch
    4 import ConfigParser, urlparse, pwd, grp, stat, syslog
    5 import difflib, smtplib, gzip, md5, sha
    6 
    7 VERSION = '0.5.1'
    8 
    9 enable = ('yes', 'on', 'true', '1')
   10 disable = ('no', 'off', 'false', '0')
   11 
   12 excludelist = 'CVS CVS.adm RCS RCSLOG SCCS TAGS cvslog.* tags .make.state .nse_depinfo *~ #* .#* ,* _$* *$ *.old *.bak *.BAK *.orig *.rej .del-* *.a *.olb *.o *.lo *.la *.obj *.so *.exe *.Z *.elc *.ln core core.[0-9]* .svn *.rpmorig *.rpmnew *.rpmsave .DS_Store'
   13 
   14 class Options:
   15     def __init__(self, args):
   16         self.configfile = '/etc/dconf.conf'
   17         self.dist = None
   18         self.output = None
   19         self.quiet = False
   20         self.verbose = 1
   21 
   22         try:
   23             opts, args = getopt.getopt (args, 'c:ho:qv',
   24                 ['config=', 'help', 'output=', 'quiet', 'verbose', 'version'])
   25         except getopt.error, exc:
   26             print 'dconf: %s, try dconf -h for a list of all the options' % str(exc)
   27             sys.exit(1)
   28 
   29         for opt, arg in opts:
   30             if opt in ['-c', '--config']:
   31                 self.configfile = os.path.abspath(arg)
   32             elif opt in ['-h', '--help']:
   33                 self.usage()
   34                 self.help()
   35                 sys.exit(0)
   36             elif opt in ('-o', '--output'):
   37                 self.output=arg
   38             elif opt in ['-q', '--quiet']:
   39                 self.quiet = True
   40             elif opt in ['-v', '--verbose']:
   41                 self.verbose = self.verbose + 1
   42             elif opt in ['--version']:
   43                 self.version()
   44                 sys.exit(0)
   45 
   46         if self.quiet:
   47             self.verbose = 0
   48 
   49         if self.verbose >= 3:
   50             print 'Verbosity set to level %d' % (self.verbose - 1)
   51             print 'Using configfile %s' % self.configfile
   52 
   53     def version(self):
   54         print 'dconf %s' % VERSION
   55         print 'Written by Dag Wieers <dag@wieers.com>'
   56         print
   57         print 'platform %s/%s' % (os.name, sys.platform)
   58         print 'python %s' % sys.version
   59 
   60     def usage(self):
   61         print 'usage: dconf [-q] [-v] [-c config] [-o output]'
   62 
   63     def help(self):
   64         print '''Create a system's hardware and software configuration snapshot
   65 
   66 Dconf options:
   67   -c, --config=file    specify alternative configfile
   68   -o, --output=file    write output to given file
   69   -q, --quiet          minimal output
   70   -v, --verbose        increase verbosity
   71   -vv, -vvv            increase verbosity more
   72 '''
   73 
   74 class Config:
   75     def __init__(self):
   76         self.sections = {}
   77 
   78         self.includefile(op.configfile)
   79         self.include = self.getoption('main', 'include', None)
   80 
   81     def includefile(self, configfile):
   82         self.cfg = ConfigParser.ConfigParser()
   83 
   84         (s,b,p,q,f,o) = urlparse.urlparse(configfile)
   85         if s in ('http', 'ftp', 'file'):
   86             configfh = urllib.urlopen(configfile)
   87             try:
   88                 self.cfg.readfp(configfh)
   89             except ConfigParser.MissingSectionHeaderError, e:
   90                 die(6, 'Error accessing URL: %s' % configfile)
   91         else:
   92             if os.access(configfile, os.R_OK):
   93                 try:
   94                     self.cfg.read(configfile)
   95                 except:
   96                     die(7, 'Syntax error reading file: %s' % configfile)
   97             else:
   98                 die(6, 'Error accessing file: %s' % configfile)
   99 
  100         self.compression = self.getoption('main', 'compression', 'gzip')
  101         self.cron = self.getoption('main', 'cron', None)
  102         self.logdir = self.getoption('main', 'logdir', '/var/log/dconf')
  103         self.mailto = self.getoption('main', 'mailto', None)
  104         self.smtpserver = self.getoption('main', 'smtp-server', 'localhost')
  105         self.rpm = not self.getoption('main', 'rpm', 'no') in disable
  106         self.exclude = self.getoption('main', 'exclude', excludelist).split()
  107 
  108         if not op.output:
  109             op.output = self.getoption('main', 'output', None)
  110 
  111         self.quiet = not self.getoption('main', 'quiet', 'no') in disable
  112         if op.verbose == 1 and self.quiet:
  113             op.verbose = 0
  114 
  115         sections = self.cfg.sections()
  116         sections.sort()
  117         for section in sections:
  118             if section in ['main']:
  119                 continue
  120             else:
  121                 self.sections[section] = {}
  122                 for option in self.cfg.options(section):
  123                     if option in ('cmds', 'dirs', 'files'):
  124                         self.sections[section][option] = self.getoption(section, option, '').split('\n')
  125 
  126     def getoption(self, section, option, var):
  127         "Get an option from a section from configfile"
  128         try:
  129             var = self.cfg.get(section, option)
  130 #           info(3, 'Setting option %s in section [%s] to: %s' % (option, section, var))
  131         except ConfigParser.NoSectionError, e:
  132 #           info(4, 'Failed to find section [%s] in %s' % (section, op.configfile))
  133             pass
  134         except ConfigParser.NoOptionError, e:
  135 #           info(4, 'Setting option %s in section [%s] to: %s (default)' % (option, section, var))
  136             pass
  137         return var
  138 
  139 def dzopen(filename, arg='r'):
  140     "Opens a file using compression based on file's extension"
  141     if fnmatch.fnmatch(filename, '*.gz'):
  142         return gzip.open(filename, arg)
  143     elif fnmatch.fnmatch(filename, '*.bz2'):
  144         return BZ2File.open(filename, arg)
  145     else:
  146         return open(filename, arg)
  147 
  148 def md5sum(filename):
  149     "Return md5 from file"
  150     md5o = md5.new(); f = dzopen(filename); md5o.update(f.read()); f.close()
  151     return md5o
  152 
  153 ### FIXME: Also check timestamps/owner/perms
  154 def md5check(h, filename):
  155     "Check if md5sum is the same as in rpmdb"
  156     if os.path.exists(filename):
  157         for file in h.fiFromHeader():
  158             if file[0] == filename:
  159                 if md5sum(filename).hexdigest() == file[12]:
  160                     return True
  161     return False
  162 
  163 def sha1sum(filename):
  164     "Return sha1 from file"
  165     sha1o = sha.new(); f = dzopen(filename); sha1o.update(f.read()); f.close()
  166     return sha1o
  167 
  168 ### BOGUS function, rpmdb only has md5 ?
  169 def sha1check(h, filename):
  170     "Check if sha1sum is the same as in rpmdb"
  171     if os.path.exists(filename):
  172         for file in h.fiFromHeader():
  173             if file[0] == filename:
  174                 if sha1sum(filename).hexdigest() == file[12]:
  175                     return True
  176     return False
  177 
  178 def fromrpmdb(filename):
  179     "Check if file is inside rpmdb"
  180     if not ts: return None
  181     mi = ts.dbMatch('basenames', filename)
  182     for h in mi:
  183         return h
  184     else:
  185         info(5, 'File %s not in rpmdb, including' % filename)
  186         return None
  187 
  188 def info(level, str):
  189     "Output info message"
  190     if level <= op.verbose:
  191         print str
  192 
  193 def die(ret, str):
  194     "Print error and exit with errorcode"
  195     info(0, str)
  196     sys.exit(ret)
  197 
  198 def cleanup():
  199     "Clean up logfile when interrupted."
  200     logfile = os.path.join(cf.logdir, 'dconf-' + hostname + '-' + timestamp + '.log' + extension)
  201     if os.path.isfile(logfile):
  202         remove(logfile)
  203 
  204 def symlink(src, dst):
  205     "Create a symbolic link, force if dst exists"
  206     if not os.path.islink(dst) and os.path.isdir(dst):
  207         dst = os.path.join(dst, os.path.basename(src))
  208 ### Not using filecmp increases speed with 15%
  209 #   if os.path.isfile(dst) and filecmp.cmp(src, dst) == 0:
  210     if os.path.isfile(dst):
  211         os.unlink(dst)
  212     if os.path.islink(dst):
  213         os.unlink(dst)
  214     mkdir(os.path.dirname(dst))
  215     if not os.path.exists(dst):
  216         os.symlink(src, dst)
  217 
  218 def remove(*files):
  219     "Remove files or directories"
  220     for file in files:
  221         if os.path.islink(file):
  222             os.unlink(file)
  223         elif os.path.isdir(file):
  224             try:
  225                 os.rmdir(file)
  226             except:
  227                 os.path.walk(file, removedir, ())
  228         elif os.path.exists(file):
  229             os.unlink(file)
  230 
  231 def removedir(void, dir, files):
  232     for file in files:
  233         remove(os.path.join(dir, file))
  234 
  235 def mkdir(path):
  236     "Create a directory, and parents if needed"
  237     if not os.path.exists(path):
  238         os.makedirs(path)
  239 
  240 def getowner(uid, gid):
  241     try:
  242         owner = pwd.getpwuid(uid)[0]
  243     except:
  244         owner = uid
  245     try:
  246         group = grp.getgrgid(gid)[0]
  247     except:
  248         group = gid
  249     return owner, group
  250 
  251 def sectiontitle(section):
  252     info(3, 'Processing section [%s]' % section)
  253     stitle = '== ' + section.upper() + ' =='
  254     return stitle + '=' * (80 - len(stitle)) + '\n'
  255 
  256 def which(cmd):
  257     "Find executables in PATH environment"
  258     for path in os.environ.get('PATH','$PATH').split(':'):
  259         if os.path.isfile(os.path.join(path, cmd)):
  260             info(4, 'Found command %s in path %s' % (cmd, path))
  261             return os.path.join(path, cmd)
  262     return None
  263 
  264 def diff(fromfile, tofile):
  265     "Create a unified diff from 2 files"
  266     msg = ''
  267     fromfd = dzopen(fromfile)
  268     tofd = dzopen(tofile)
  269     for line in difflib.unified_diff(fromfd.readlines(), tofd.readlines(), fromfile, tofile, os.stat(fromfile).st_mtime, os.stat(tofile).st_mtime):
  270         msg = msg + line
  271     tofd.close()
  272     fromfd.close()
  273     return msg
  274 
  275 def fnmatches(file):
  276     for entry in cf.exclude:
  277         if fnmatch.fnmatch(file, entry):
  278 #           info(6, 'File %s matches against glob %s' % (file, entry))
  279             return True
  280     return False
  281 
  282 def mail(subject, msg):
  283     info(2, 'Sending mail to: %s' % cf.mailto)
  284     try:
  285         smtp = smtplib.SMTP(cf.smtpserver)
  286 #       server.set_debuglevel(1)
  287         msg = 'Subject: [dconf] %s\n\nCurrent time:\n%s\nSystem information:\n%s\nUptime:\n%s\nCurrently logged on users:\n%s\n%s\n\n%s' % (subject, os.popen('date').read(), os.popen('uname -a').read(), os.popen('uptime').read(), os.popen('who').read(), subject, msg)
  288         for email in cf.mailto.split():
  289             smtp.sendmail('dconf@%s' % os.uname()[1], email, 'To: %s\n%s' % (email, msg))
  290         smtp.quit()
  291     except:
  292         info(1, 'Sending mail via %s failed.' % cf.smtpserver)
  293 
  294 def main():
  295     global extension, hostname, timestamp, ts
  296 
  297 #   if cf.rpm or which('rpm'):
  298     try:
  299         import rpm
  300         ts = rpm.TransactionSet()
  301 #       ts.setVSFlags(rpm.RPMVSF_NORSA | rpm.RPMVSF_NODSA)
  302         ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES | rpm.RPMVSF_NOHDRCHK | rpm._RPMVSF_NODIGESTS | rpm.RPMVSF_NEEDPAYLOAD)
  303     except:
  304 #   else:
  305         info(2, 'Disabling RPM capability since the rpm-python bindings could not be loaded.')
  306         cf.rpm = False
  307         ts = None
  308 
  309     if cf.compression == 'gzip':
  310         extension = '.log.gz'
  311     elif cf.compression == 'bzip2':
  312         extension = '.log.bz2'
  313     else:
  314         extension = '.log'
  315 
  316     hostname = os.uname()[1].split('.')[0]
  317 #   hostname = os.uname()[1]
  318 #   hostname = socket.gethostbyaddr(socket.gethostname())[0]
  319     timestamp = time.strftime('%Y%m%d-%H%M%S', time.localtime()) 
  320 
  321     os.umask(077)
  322 
  323 #   os.setenv('LC_ALL', 'C')
  324     os.environ['LC_ALL'] = 'C'
  325 
  326     syslog.openlog('dconf[%d]' % os.getpid())
  327     syslog.syslog('Dconf %s started.' % VERSION)
  328 
  329     ### Add to cron
  330     if cf.cron and not op.output:
  331         if cf.cron in ('hourly', 'daily', 'weekly', 'monthly'):
  332             cronfile = os.path.join('/etc', 'cron.%s' % cf.cron, 'dconf')
  333             if os.path.isdir(os.path.dirname(cronfile)):
  334                 if (os.path.realpath(cronfile) != which('dconf')):
  335                     info(2, 'Adding dconf to cron (%s).' % cf.cron)
  336                     symlink(which('dconf'), cronfile)
  337             else:
  338                 info(2, 'Path %s does not exist, ignoring.' % os.path.dirname(cronfile))
  339         else:
  340             info(2, 'Option cron should be set to hourly, daily, weekly or monthly.')
  341 
  342         ### Remove from cron
  343         for item in ('hourly', 'daily', 'weekly', 'monthly'):
  344             if item != cf.cron:
  345                 cronfile = os.path.join('/etc', 'cron.%s' % item, 'dconf')
  346                 if os.path.isfile(cronfile):
  347                     info(2, 'Removing dconf from cron (%s).' % item)
  348                     remove(cronfile)
  349 
  350     if op.output == '-':
  351         logfile = '- (stdout)'
  352         log = sys.stdout
  353         op.quiet = True
  354         op.verbose = 0
  355     elif op.output:
  356         logfile = op.output
  357         log = dzopen(logfile, 'w')
  358     else:
  359         logfile = os.path.join(cf.logdir, 'dconf-' + hostname + '-' + timestamp + extension)
  360         latestlog = os.path.join(cf.logdir, 'dconf-' + hostname + '-latest' + extension)
  361         previouslog = os.path.join(cf.logdir, 'dconf-' + hostname + '-previous' + extension)
  362         oldestlog = os.path.join(cf.logdir, 'dconf-' + hostname + '-oldest' + extension)
  363         mkdir(cf.logdir)
  364         os.chmod(cf.logdir, 0700)
  365         log = dzopen(logfile, 'w')
  366 
  367     info(2, 'Building file: %s' % logfile)
  368     sections = cf.sections.keys()
  369     sections.sort()
  370     for section in sections:
  371         stitle = False
  372 
  373         if cf.sections[section].has_key('cmds'):
  374             for line in cf.sections[section]['cmds']:
  375                 cmdline = line.split()
  376                 if not cmdline: continue
  377 
  378                 cmd = which(cmdline[0].strip())
  379                 extra = ' '.join(cmdline[1:])
  380                 if not cmd:
  381                     info(5, 'Cmd %s not found in PATH, excluding.' % cmdline[0].strip())
  382                     continue
  383 
  384                 if not os.access(cmd, os.X_OK):
  385                     info(1, 'Cmd %s cannot be executed, excluding.' % cmd)
  386                     continue
  387 
  388                 if not stitle:
  389                     stitle = True
  390                     log.write(sectiontitle(section))
  391 
  392                 info(4, 'Processing cmd %s' % cmd)
  393                 (mode, t, t, t, uid, gid, size, t, t, t) = os.stat(cmd)
  394                 owner, group = getowner(uid, gid)
  395                 title = '--[ Cmd: %s %s ]--(%04o, %s, %s, %s)--' % (cmd, extra, stat.S_IMODE(mode), owner, group, size)
  396                 log.write(title + '-' * (80 - len(title)) + '\n')
  397                 (o, i) = popen2.popen4('%s %s' % (cmd, extra))
  398                 log.write(o.read() + '\n')
  399 
  400         if cf.sections[section].has_key('files'):
  401             for line in cf.sections[section]['files']:
  402                 flist = line.split('|')
  403                 if not flist: continue
  404                 extra = '|'.join(flist[1:])
  405                 if extra: extra = extra + ' '
  406                 for file in glob.glob(flist[0].strip()):
  407                     if not file or not os.path.isfile(file): continue
  408 
  409                     if fnmatches(file):
  410                         info(5, 'File %s is in the exclude list, excluding.' % file)
  411                         continue
  412 
  413                     (mode, t, t, t, uid, gid, size, t, t, t) = os.stat(file)
  414                     if not size: continue
  415 
  416                     if ts:
  417                         h = fromrpmdb(file)
  418                         if h and md5check(h, file):
  419                             info(5, 'File %s has not been changed since installation, excluding.' % file)
  420                             continue
  421 
  422                     if not os.access(file, os.R_OK):
  423                         info(1, 'File %s cannot be read, excluding.' % file)
  424                         continue
  425 
  426                     if not stitle:
  427                         stitle = True
  428                         log.write(sectiontitle(section))
  429 
  430                     info(4, 'Processing file %s' % file)
  431                     owner, group = getowner(uid, gid)
  432                     title = '--[ File: %s %s]--(%04o, %s, %s, %s)--' % (file, extra, stat.S_IMODE(mode), owner, group, size)
  433                     log.write(title + '-' * (80 - len(title)) + '\n')
  434                     (o, i) = popen2.popen4('cat %s %s' % (file, extra))
  435                     log.write(o.read() + '\n')
  436     log.close()
  437 
  438     syslog.syslog('Dconf %s ended succesfully.' % VERSION)
  439     syslog.closelog()
  440 
  441     if op.output:
  442         return
  443 
  444     if os.path.isfile(latestlog):
  445         if sha1sum(latestlog).digest() != sha1sum(logfile).digest():
  446             info(2, 'New logfile is different than last logfile, keeping.')
  447             symlink(os.path.basename(os.path.realpath(latestlog)), previouslog)
  448             symlink(os.path.basename(logfile), latestlog)
  449             if cf.mailto:
  450                 mail('changes to %s' % os.uname()[1], diff(os.path.realpath(previouslog), os.path.realpath(latestlog)))
  451         else:
  452             info(2, 'New logfile is identical to last logfile, removing.')
  453             remove(logfile)
  454     else:
  455         info(2, 'Marking this as a first time run, symlinking.')
  456         symlink(os.path.basename(logfile), oldestlog)
  457         symlink(os.path.basename(logfile), previouslog)
  458         symlink(os.path.basename(logfile), latestlog)
  459         if cf.mailto:
  460             mail('initial config for %s' % os.uname()[1], dzopen(logfile, 'r').read())
  461 
  462 ### Workaround for python <= 2.2.1
  463 try:
  464      True, False
  465 except NameError:
  466      True = 1
  467      False = 0
  468 
  469 ### Main entrance
  470 if __name__ == '__main__':
  471     op=Options(sys.argv[1:])
  472     cf=Config()
  473     for cfgfile in cf.include.split():
  474         if os.path.isfile(cfgfile):
  475             info(2, 'Processing configfile %s.' % cfgfile)
  476             cf.includefile(cfgfile)
  477         else:
  478             info(3, 'Configfile %s does not exist, ignoring.')
  479 
  480     try:
  481         main()
  482     except KeyboardInterrupt, e:
  483         cleanup()
  484         die(6, 'Exiting on user request')
  485     except:
  486         cleanup()
  487         raise
  488 
  489 # vim:ts=4:sw=4