#!/usr/bin/python -t # -*- mode: Python; indent-tabs-mode: nil; -*- # # This program 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 2 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import errno, os, sys import commands, shutil, tempfile import xml.parsers.expat import Utils def _fingerprint(filename): """return MD5 fingerprint of a file""" import md5 m = md5.new() if os.path.isfile(filename): f = open(filename,'r') data = f.read() f.close() m.update(data) return m.hexdigest() class PushCompsException(Exception): pass # Small extra check of well-formed comps.xml to catch typos which # broke comps support in pirut+yum badly. def startelhandler(name, attrs): if name=='packagereq': for (a,v) in attrs.items(): if a=='type' and v not in ['mandatory', 'default', 'optional', 'conditional']: raise PushCompsException def _check(filename): p = xml.parsers.expat.ParserCreate() p.StartElementHandler = startelhandler isopen = False try: f = open(filename,'r') isopen = True p.ParseFile(f) f.close() except IOError: if isopen: f.close() return False except PushCompsException: f.close() return False return True class CompsUpdater: def __init__(self,cfg): self.tmpdir = None self.cfg = cfg def _cleanup(self): if self.tmpdir: shutil.rmtree(self.tmpdir) self.tmpdir = None def _backup(self): # Back up some security relevant files of the working-copy. # throws: OSError, IOError self.tmpdir = tempfile.mkdtemp('','.tmp',os.getcwd()) for sourcefile in self.cfg.comps_dangerousfiles: targetfile = os.path.join(self.tmpdir,sourcefile.replace('/','___')) if os.path.exists(sourcefile): shutil.copy2(sourcefile,targetfile) def _restoreBackup(self): # Restore backups. # throws: IOError for f in os.listdir(self.tmpdir): sourcefile = os.path.join(self.tmpdir,f) targetfile = f.replace('___','/') Utils.install_copy(sourcefile,targetfile,True) def Update(self): self.workdir = os.path.join(self.cfg.rundir,'comps') if not os.path.exists(self.workdir): print 'WARNING: %s missing! Manual setup needed.' % self.workdir return self.olddir= os.getcwd() try: os.chdir(self.workdir) self._backup() if self._update(self.cfg.comps_up_pass1): self._restoreBackup() if self._update(self.cfg.comps_up_pass2): for dist in self.cfg.alldists: if dist in self.cfg.frozendists: continue self._install(dist) # TODO: Error-handling and reporting. We don't want these to # be fatal. except IOError: pass except OSError: pass except KeyboardInterrupt: pass os.chdir(self.olddir) self._cleanup() def _update(self,cmd): # Update working-copy. if cmd: (rc, rv) = commands.getstatusoutput(cmd) print rv if rc: print 'WARNING: comps update failed: %s' % cmd return False return True def _install(self,dist): # If a new comps file for this dist exists, make sure it's # well-formed. Else delete it and return. # throws: IOError, OSError compsnew = self.cfg.comps_map.get(dist,None) if not compsnew or not os.path.exists(compsnew): return compsvalid = 'comps.xml.valid' command = 'xmllint %s --output %s' % (compsnew,compsvalid) (rc, rv) = commands.getstatusoutput(command) if rc: print 'WARNING: malformed %s' % compsnew if os.path.exists(compsnew): os.remove(compsnew) return if not _check(compsvalid): print 'WARNING: %s comps fails check' % dist return # Install new comps file only if fingerprint differs. # First repo gets a new copy, further repos a hardlink. oldfingerprint = None newfingerprint = _fingerprint(compsvalid) linkloc = None for arch in self.cfg.archdict[dist]: # list of repo archs repodir = Utils.rpm_repodir(self.cfg,dist,arch) compstarget = os.path.join(repodir,'comps.xml') if not oldfingerprint: oldfingerprint = _fingerprint(compstarget) if os.path.exists(compstarget) and (oldfingerprint == newfingerprint): # This copy of comps.xml is the same, don't overwrite it, # but use it as link source. linkloc = compstarget continue print ' Updating %s' % compstarget if os.path.exists(compstarget): os.remove(compstarget) if linkloc: Utils.install_link_or_copy(linkloc,compstarget) else: Utils.install_copy(compsvalid,compstarget) linkloc = compstarget os.remove(compsvalid) def main(cfg): c = CompsUpdater(cfg) c.Update() if __name__ == '__main__': if len(sys.argv) < 2: print 'Usage: %s \n' % os.path.basename(sys.argv[0]) sys.exit(errno.EINVAL) cfg = Utils.load_config_module(sys.argv[1]) Utils.signer_gid_check(cfg.signersgid) os.umask(cfg.signersumask) main(cfg) sys.exit(0)