# Copyright 2008 The Tor Project, Inc. See LICENSE for licensing information. import thandy.packagesys.PackageSystem as PS import os try: import rpm except ImportError: rpm = None import md5 import thandy.formats __all__ = [ 'RPMPackageSystem' ] _CALLBACK_CODES = {} for name in dir(rpm): if name.startswith("RPMCALLBACK_"): _CALLBACK_CODES[getattr(rpm, name)] = name[12:] del name class RPMPackageTransaction: def _start(self): PS.PackageTransaction.__init__(self) self._tset = rpm.TransactionSet() def _commit(self): self._tset.run(self._callback, "") def _callback(self, what, amount, total, mydata, _): if what == rpm.RPMCALLBACK_INST_OPEN_FILE: hdr, path = mydata logging.info("Installing RPM for %s [%s]", hdr['name'], path) elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE: hdr, path = mydata logging.info("Done installing RPM for %s", path) elif what == rpm.RPMCALLBACK_INST_PROGRESS: hdr, path = mydata logging.info("%s: %.5s%% done", name, float(amount)/total*100) else: hdr, path = mydata logging.info("RPM event %s on %s [%s/%s]", _CALLBACK_CODES.get(what,str(what)), hdr['name'], amount, total) def addRPMInstall(ts, path): fd = os.open(path, os.O_RDONLY) try: hdr = ts.hdrFromFdno(fd) finally: os.close(fd) ts.addInstall(hdr, (hdr, path), "u") def addRPMErase(ts, name): ts.addErase(name) def getInstalledRPMVersions(name, ts=None): if ts is None: ts = rpm.TransactionSet() #XXXX need to close? versions = set() for match in ts.dbMatch(rpm.RPMTAG_NAME, name): versions.add(match['version']) return versions def fileMD5(fname): d = md5.new() try: f = open(fname, 'r') try: while 1: s = f.read(4096) if not s: break d.update(s) finally: f.close() except OSError, e: logging.warn("Couldn't get digest of %s: %s", fname, e) return None return d.hexdigest() def checkRPMInstall(name, version, ts=None): if ts is None: ts = rpm.TransactionSet() #XXXX need to close? found = False all_ok = True for h in ts.dbMatch(rpm.RPMTAG_NAME, name): if h['version'] != version: continue found = True for fname, flags, md5sum in zip(h['filenames'], h['fileflags'], h['filemd5s']): haveMD5 = fileMD5(fname) if not haveMD5: if (flags & rpm.RPMFILE_MISSINGOK): logging.info("%s is missing or unreadable from %s %s; " "that's ok.", fname, name, h['version']) else: logging.warn("%s is missing or unreadable from %s %s.", fname, name, h['version']) all_ok = False elif haveMD5 == md5sum: logging.info("%s is unchanged from %s %s", fname, name, h['version']) else: # file changed. If it's not configuration, that's a problem. if not (flags & rpm.RPMFILE_CONFIG): logging.warn("%s changed from installed version of %s %s", fname, name, h['version']) all_ok = False return found and all_ok class RPMChecker(PS.Checker): def __init__(self, rpmname, rpmversion): PS.Checker.__init__(self) self._name = rpmname self._version = rpmversion def __repr__(self): return "RPMChecker(%r, %r)"%(self._name, self._version) def getInstalledVersions(self): return getInstalledRPMVersions(self._name, self._transaction) def isInstalled(self): vers = getInstalledRPMVersions(self._name, self._transaction) return self._version in vers # def checkInstall(self): # return checkRPMInstall(self._name, self._version) class RPMInstaller(PS.Installer): def __init__(self, rpmname, relPath): PS.Installer.__init__(self, relPath) self._name = rpmname def __repr__(self): return "RPMInstaller(%r, %r)"%(self._name, self._relPath) def install(self, transaction): addRPMInstall(transaction._trans, self.getFilename()) def remove(self, transaction): addRPMErase(transaction._trans, self._name)