#!/usr/bin/python ''' # DESCRIPTION: # Equo Library used by Python frontends Copyright (C) 2007 Fabio Erculiani 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 ''' from sys import path, getfilesystemencoding import os import shutil from entropyConstants import * from clientConstants import * from outputTools import * import remoteTools from entropyTools import compareMd5, bytesIntoHuman, askquestion, getRandomNumber, dep_getkey, entropyCompareVersions, filterDuplicatedEntries, extractDuplicatedEntries, uncompressTarBz2, extractXpak, applicationLockCheck, countdown, isRoot, spliturl, remove_tag, dep_striptag, md5sum, allocateMaskedFile, istextfile, isnumber, extractEdb, getNewerVersion, getNewerVersionTag, unpackXpak, lifobuffer from databaseTools import openRepositoryDatabase, openClientDatabase, openGenericDatabase, fetchRepositoryIfNotAvailable, listAllAvailableBranches import triggerTools import confTools import dumpTools import gc # Logging initialization import logTools equoLog = logTools.LogFile(level = etpConst['equologlevel'],filename = etpConst['equologfile'], header = "[Equo]") ### Caching functions def loadCaches(): if os.getuid() != 0: # don't load cache as user return if not etpUi['quiet']: print_info(darkred(" @@ ")+blue("Loading On-Disk Cache...")) # atomMatch try: mycache = dumpTools.loadobj(etpCache['atomMatch']) if isinstance(mycache, dict): atomMatchCache.clear() atomMatchCache.update(mycache) del mycache except: atomMatchCache.clear() dumpTools.dumpobj(etpCache['atomMatch'],{}) # removal dependencies try: mycache3 = dumpTools.loadobj(etpCache['generateDependsTree']) if isinstance(mycache3, dict): generateDependsTreeCache.clear() generateDependsTreeCache.update(mycache3) del mycache3 except: generateDependsTreeCache.clear() dumpTools.dumpobj(etpCache['generateDependsTree'],{}) def saveCaches(): if os.getuid() != 0: # don't save cache as user return dumpTools.dumpobj(etpCache['atomMatch'],atomMatchCache) if os.path.isfile(etpConst['dumpstoragedir']+"/"+etpCache['atomMatch']+".dmp"): if os.stat(etpConst['dumpstoragedir']+"/"+etpCache['atomMatch']+".dmp")[6] > etpCacheSizes['atomMatch']: # clean cache dumpTools.dumpobj(etpCache['atomMatch'],{}) dumpTools.dumpobj(etpCache['generateDependsTree'],generateDependsTreeCache) if os.path.isfile(etpConst['dumpstoragedir']+"/"+etpCache['generateDependsTree']+".dmp"): if os.stat(etpConst['dumpstoragedir']+"/"+etpCache['generateDependsTree']+".dmp")[6] > etpCacheSizes['generateDependsTree']: # clean cache dumpTools.dumpobj(etpCache['generateDependsTree'],{}) for dbinfo in dbCacheStore: dumpTools.dumpobj(dbinfo,dbCacheStore[dbinfo]) # check size if os.path.isfile(etpConst['dumpstoragedir']+"/"+dbinfo+".dmp"): if dbinfo.startswith(etpCache['dbMatch']): if os.stat(etpConst['dumpstoragedir']+"/"+dbinfo+".dmp")[6] > etpCacheSizes['dbMatch']: # clean cache dumpTools.dumpobj(dbinfo,{}) elif dbinfo.startswith(etpCache['dbInfo']): if os.stat(etpConst['dumpstoragedir']+"/"+dbinfo+".dmp")[6] > etpCacheSizes['dbInfo']: # clean cache dumpTools.dumpobj(dbinfo,{}) elif dbinfo.startswith(etpCache['dbSearch']): if os.stat(etpConst['dumpstoragedir']+"/"+dbinfo+".dmp")[6] > etpCacheSizes['dbSearch']: # clean cache dumpTools.dumpobj(dbinfo,{}) ######################################################## #### ## Dependency handling functions # ''' @description: matches the package that user chose, using dbconnection.atomMatch searching in all available repositories. @input atom: user choosen package name @output: the matched selection, list: [package id,repository name] | if nothing found, returns: ( -1,1 ) @ exit errors: -1 => repository cannot be fetched online ''' def atomMatch(atom, caseSentitive = True, matchSlot = None, matchBranches = (), xcache = True): if xcache: cached = atomMatchCache.get(atom) if cached: if (cached['matchSlot'] == matchSlot) and (cached['matchBranches'] == matchBranches) and (cached['etpRepositories'] == etpRepositories): return cached['result'] repoResults = {} exitErrors = {} for repo in etpRepositories: # sync database if not available rc = fetchRepositoryIfNotAvailable(repo) if (rc != 0): exitErrors[repo] = -1 continue # open database dbconn = openRepositoryDatabase(repo, xcache = xcache) # search query = dbconn.atomMatch(atom, caseSensitive = caseSentitive, matchSlot = matchSlot, matchBranches = matchBranches) #print "repo:",repo,"atom:",atom,"result:",query if query[1] == 0: # package found, add to our dictionary repoResults[repo] = query[0] dbconn.closeDB() del dbconn # handle repoResults packageInformation = {} # nothing found if not repoResults: atomMatchCache[atom] = {} atomMatchCache[atom]['result'] = -1,1 atomMatchCache[atom]['matchSlot'] = matchSlot atomMatchCache[atom]['matchBranches'] = matchBranches atomMatchCache[atom]['etpRepositories'] = etpRepositories.copy() return -1,1 elif len(repoResults) == 1: # one result found for repo in repoResults: atomMatchCache[atom] = {} atomMatchCache[atom]['result'] = repoResults[repo],repo atomMatchCache[atom]['matchSlot'] = matchSlot atomMatchCache[atom]['matchBranches'] = matchBranches atomMatchCache[atom]['etpRepositories'] = etpRepositories.copy() return repoResults[repo],repo elif len(repoResults) > 1: # we have to decide which version should be taken # .tbz2 repos have always the precedence, so if we find them, we should second what user wants, installing his tbz2 tbz2repos = [x for x in repoResults if x.endswith(".tbz2")] if tbz2repos: del tbz2repos newrepos = repoResults.copy() for x in newrepos: if not x.endswith(".tbz2"): del repoResults[x] # get package information for all the entries for repo in repoResults: # open database dbconn = openRepositoryDatabase(repo) # search packageInformation[repo] = {} packageInformation[repo]['version'] = dbconn.retrieveVersion(repoResults[repo]) packageInformation[repo]['versiontag'] = dbconn.retrieveVersionTag(repoResults[repo]) packageInformation[repo]['revision'] = dbconn.retrieveRevision(repoResults[repo]) dbconn.closeDB() del dbconn versions = [] repoNames = [] # compare versions for repo in packageInformation: repoNames.append(repo) versions.append(packageInformation[repo]['version']) # found duplicates, this mean that we have to look at the revision and then, at the version tag # if all this shait fails, get the uppest repository # if no duplicates, we're done #print versions filteredVersions = filterDuplicatedEntries(versions) if (len(versions) > len(filteredVersions)): # there are duplicated results, fetch them # get the newerVersion #print versions newerVersion = getNewerVersion(versions) newerVersion = newerVersion[0] # is newerVersion, the duplicated one? duplicatedEntries = extractDuplicatedEntries(versions) needFiltering = False if newerVersion in duplicatedEntries: needFiltering = True if (needFiltering): # we have to decide which one is good #print "need filtering" # we have newerVersion conflictingEntries = {} for repo in packageInformation: if packageInformation[repo]['version'] == newerVersion: conflictingEntries[repo] = {} #conflictingEntries[repo]['version'] = packageInformation[repo]['version'] conflictingEntries[repo]['versiontag'] = packageInformation[repo]['versiontag'] conflictingEntries[repo]['revision'] = packageInformation[repo]['revision'] # at this point compare tags tags = [] for repo in conflictingEntries: tags.append(conflictingEntries[repo]['versiontag']) newerTag = getNewerVersionTag(tags) newerTag = newerTag[0] # is the chosen tag duplicated? duplicatedTags = extractDuplicatedEntries(tags) needFiltering = False if newerTag in duplicatedTags: needFiltering = True if (needFiltering): #print "also tags match" # yes, it is. we need to compare revisions conflictingTags = {} for repo in conflictingEntries: if conflictingEntries[repo]['versiontag'] == newerTag: conflictingTags[repo] = {} #conflictingTags[repo]['version'] = conflictingEntries[repo]['version'] #conflictingTags[repo]['versiontag'] = conflictingEntries[repo]['versiontag'] conflictingTags[repo]['revision'] = conflictingEntries[repo]['revision'] #print tags #print conflictingTags revisions = [] for repo in conflictingTags: revisions.append(str(conflictingTags[repo]['revision'])) newerRevision = max(revisions) duplicatedRevisions = extractDuplicatedEntries(revisions) needFiltering = False if newerRevision in duplicatedRevisions: needFiltering = True if (needFiltering): # ok, we must get the repository with the biggest priority #print "d'oh" # I'm pissed off, now I get the repository name and quit myrepoorder = list(etpRepositoriesOrder) myrepoorder.sort() for repository in myrepoorder: for repo in conflictingTags: if repository[1] == repo: # found it, WE ARE DOOONE! atomMatchCache[atom] = {} atomMatchCache[atom]['result'] = repoResults[repo],repo atomMatchCache[atom]['matchSlot'] = matchSlot atomMatchCache[atom]['matchBranches'] = matchBranches atomMatchCache[atom]['etpRepositories'] = etpRepositories.copy() return repoResults[repo],repo else: # we are done!!! reponame = '' #print conflictingTags for x in conflictingTags: if str(conflictingTags[x]['revision']) == str(newerRevision): reponame = x break atomMatchCache[atom] = {} atomMatchCache[atom]['result'] = repoResults[reponame],reponame atomMatchCache[atom]['matchSlot'] = matchSlot atomMatchCache[atom]['matchBranches'] = matchBranches atomMatchCache[atom]['etpRepositories'] = etpRepositories.copy() return repoResults[reponame],reponame else: # we're finally done reponame = '' for x in conflictingEntries: if conflictingEntries[x]['versiontag'] == newerTag: reponame = x break atomMatchCache[atom] = {} atomMatchCache[atom]['result'] = repoResults[reponame],reponame atomMatchCache[atom]['matchSlot'] = matchSlot atomMatchCache[atom]['matchBranches'] = matchBranches atomMatchCache[atom]['etpRepositories'] = etpRepositories.copy() return repoResults[reponame],reponame else: # we are fine, the newerVersion is not one of the duplicated ones reponame = '' for x in packageInformation: if packageInformation[x]['version'] == newerVersion: reponame = x break atomMatchCache[atom] = {} atomMatchCache[atom]['result'] = repoResults[reponame],reponame atomMatchCache[atom]['matchSlot'] = matchSlot atomMatchCache[atom]['matchBranches'] = matchBranches atomMatchCache[atom]['etpRepositories'] = etpRepositories.copy() return repoResults[reponame],reponame #print versions else: # yeah, we're done, just return the info #print versions newerVersion = getNewerVersion(versions) # get the repository name newerVersion = newerVersion[0] reponame = '' for x in packageInformation: if packageInformation[x]['version'] == newerVersion: reponame = x break #print reponame atomMatchCache[atom] = {} atomMatchCache[atom]['result'] = repoResults[reponame],reponame atomMatchCache[atom]['matchSlot'] = matchSlot atomMatchCache[atom]['matchBranches'] = matchBranches atomMatchCache[atom]['etpRepositories'] = etpRepositories.copy() return repoResults[reponame],reponame ''' @description: generates the dependencies of a [id,repository name] combo. @input packageInfo: tuple composed by int(id) and str(repository name), if this one is int(0), the client database will be opened. @output: ordered dependency list ''' # FIXME: move this to database? def getDependencies(packageInfo): ''' caching ''' cached = getDependenciesCache.get(tuple(packageInfo)) if cached: return cached['result'] idpackage = packageInfo[0] reponame = packageInfo[1] if reponame == 0: dbconn = openClientDatabase() else: dbconn = openRepositoryDatabase(reponame) # retrieve dependencies depend = dbconn.retrieveDependencies(idpackage) # and conflicts conflicts = dbconn.retrieveConflicts(idpackage) for x in conflicts: depend.add("!"+x) dbconn.closeDB() del dbconn ''' caching ''' getDependenciesCache[tuple(packageInfo)] = {} getDependenciesCache[tuple(packageInfo)]['result'] = depend return depend ''' @description: filter the already installed dependencies @input dependencies: list of dependencies to check @output: filtered list, aka the needed ones and the ones satisfied ''' def filterSatisfiedDependencies(dependencies, deepdeps = False): unsatisfiedDeps = set() satisfiedDeps = set() # now create a list with the unsatisfied ones # query the installed packages database #print etpConst['etpdatabaseclientfilepath'] clientDbconn = openClientDatabase() for dependency in dependencies: depsatisfied = set() depunsatisfied = set() ''' caching ''' cached = filterSatisfiedDependenciesCache.get(dependency) if cached: if (cached['deepdeps'] == deepdeps): unsatisfiedDeps.update(cached['depunsatisfied']) satisfiedDeps.update(cached['depsatisfied']) continue ### conflict if dependency[0] == "!": testdep = dependency[1:] xmatch = clientDbconn.atomMatch(testdep) if xmatch[0] != -1: unsatisfiedDeps.add(dependency) else: satisfiedDeps.add(dependency) continue repoMatch = atomMatch(dependency) if repoMatch[0] != -1: dbconn = openRepositoryDatabase(repoMatch[1]) repo_pkgver = dbconn.retrieveVersion(repoMatch[0]) repo_pkgtag = dbconn.retrieveVersionTag(repoMatch[0]) repo_pkgrev = dbconn.retrieveRevision(repoMatch[0]) dbconn.closeDB() del dbconn else: # dependency does not exist in our database unsatisfiedDeps.add(dependency) continue clientMatch = clientDbconn.atomMatch(dependency) if clientMatch[0] != -1: installedVer = clientDbconn.retrieveVersion(clientMatch[0]) installedTag = clientDbconn.retrieveVersionTag(clientMatch[0]) installedRev = clientDbconn.retrieveRevision(clientMatch[0]) if installedRev == 9999: # any revision is fine repo_pkgrev = 9999 if (deepdeps): vcmp = entropyCompareVersions((repo_pkgver,repo_pkgtag,repo_pkgrev),(installedVer,installedTag,installedRev)) if vcmp != 0: filterSatisfiedDependenciesCmpResults[dependency] = vcmp depunsatisfied.add(dependency) else: depsatisfied.add(dependency) else: depsatisfied.add(dependency) else: # not installed filterSatisfiedDependenciesCmpResults[dependency] = 0 depunsatisfied.add(dependency) unsatisfiedDeps.update(depunsatisfied) satisfiedDeps.update(depsatisfied) ''' caching ''' filterSatisfiedDependenciesCache[dependency] = {} filterSatisfiedDependenciesCache[dependency]['depunsatisfied'] = depunsatisfied filterSatisfiedDependenciesCache[dependency]['depsatisfied'] = depsatisfied filterSatisfiedDependenciesCache[dependency]['deepdeps'] = deepdeps clientDbconn.closeDB() del clientDbconn return unsatisfiedDeps, satisfiedDeps ''' @description: generates a dependency tree using unsatisfied dependencies @input package: atomInfo (idpackage,reponame) @output: dependency tree dictionary, plus status code ''' def generateDependencyTree(atomInfo, emptydeps = False, deepdeps = False, usefilter = False): if (not usefilter): matchFilter.clear() ''' caching ''' cached = generateDependencyTreeCache.get(tuple(atomInfo)) if cached: if (cached['emptydeps'] == emptydeps) and \ (cached['deepdeps'] == deepdeps) and \ (cached['usefilter'] == usefilter): return cached['result'] #print atomInfo mydbconn = openRepositoryDatabase(atomInfo[1]) myatom = mydbconn.retrieveAtom(atomInfo[0]) mydbconn.closeDB() del mydbconn # caches treecache = set() matchcache = set() keyslotcache = set() # special events dependenciesNotFound = set() conflicts = set() mydep = (1,myatom) mybuffer = lifobuffer() deptree = set() if not ((atomInfo in matchFilter) and (usefilter)): mybuffer.push((1,myatom)) #mytree.append((1,myatom)) deptree.add((1,atomInfo)) clientDbconn = openClientDatabase() while mydep != None: # already analyzed in this call if mydep[1] in treecache: mydep = mybuffer.pop() continue # conflicts if mydep[1][0] == "!": xmatch = clientDbconn.atomMatch(mydep[1][1:]) if xmatch[0] != -1: conflicts.add(xmatch[0]) mydep = mybuffer.pop() continue # atom found? match = atomMatch(mydep[1]) if match[0] == -1: dependenciesNotFound.add(mydep[1]) mydep = mybuffer.pop() continue # check if atom has been already pulled in matchdb = openRepositoryDatabase(match[1]) matchatom = matchdb.retrieveAtom(match[0]) matchslot = matchdb.retrieveSlot(match[0]) # used later matchdb.closeDB() del matchdb if matchatom in treecache: mydep = mybuffer.pop() continue else: treecache.add(matchatom) treecache.add(mydep[1]) # check if key + slot has been already pulled in key = dep_getkey(matchatom) if (matchslot,key) in keyslotcache: mydep = mybuffer.pop() continue else: keyslotcache.add((matchslot,key)) # already analyzed by the calling function if (match in matchFilter) and (usefilter): mydep = mybuffer.pop() continue if usefilter: matchFilter.add(match) # result already analyzed? if match in matchcache: mydep = mybuffer.pop() continue treedepth = mydep[0]+1 # all checks passed, well done matchcache.add(match) deptree.add((mydep[0],match)) # add match myundeps = getDependencies(match) # in this way filterSatisfiedDependenciesCmpResults is alway consistent mytestdeps, xxx = filterSatisfiedDependencies(myundeps, deepdeps = deepdeps) if (not emptydeps): myundeps = mytestdeps for x in myundeps: mybuffer.push((treedepth,x)) # handle possible library breakage action = filterSatisfiedDependenciesCmpResults.get(mydep[1]) if action and ((action < 0) or (action > 0)): # do not use != 0 since action can be "None" i = clientDbconn.atomMatch(dep_getkey(mydep[1]), matchSlot = matchslot) if i[0] != -1: oldneeded = clientDbconn.retrieveNeeded(i[0]) if oldneeded: # if there are needed ndbconn = openRepositoryDatabase(match[1]) needed = ndbconn.retrieveNeeded(match[0]) ndbconn.closeDB() del ndbconn oldneeded.difference_update(needed) if oldneeded: # reverse lookup to find belonging package for need in oldneeded: myidpackages = clientDbconn.searchNeeded(need) for myidpackage in myidpackages: myname = clientDbconn.retrieveName(myidpackage) mycategory = clientDbconn.retrieveCategory(myidpackage) myslot = clientDbconn.retrieveSlot(myidpackage) mykey = mycategory+"/"+myname mymatch = atomMatch(mykey, matchSlot = myslot) # search in our repo if mymatch[0] != -1: mydbconn = openRepositoryDatabase(mymatch[1]) mynewatom = mydbconn.retrieveAtom(mymatch[0]) mydbconn.closeDB() del mydbconn if (mymatch not in matchcache) and (mynewatom not in treecache) and (mymatch not in matchFilter): mybuffer.push((treedepth,mynewatom)) else: # we bastardly ignore the missing library for now continue mydep = mybuffer.pop() newdeptree = {} for x in deptree: key = x[0] item = x[1] try: newdeptree[key].add(item) except: newdeptree[key] = set() newdeptree[key].add(item) del deptree clientDbconn.closeDB() del clientDbconn if (dependenciesNotFound): # Houston, we've got a problem flatview = list(dependenciesNotFound) return flatview,-2 # conflicts newdeptree[0] = conflicts ''' caching ''' generateDependencyTreeCache[tuple(atomInfo)] = {} generateDependencyTreeCache[tuple(atomInfo)]['result'] = newdeptree,0 generateDependencyTreeCache[tuple(atomInfo)]['emptydeps'] = emptydeps generateDependencyTreeCache[tuple(atomInfo)]['deepdeps'] = deepdeps generateDependencyTreeCache[tuple(atomInfo)]['usefilter'] = usefilter treecache.clear() matchcache.clear() return newdeptree,0 # note: newtree[0] contains possible conflicts ''' @description: generates a list cotaining the needed dependencies of a list requested atoms @input package: list of atoms that would be installed in list form, whose each element is composed by [idpackage,repository name] @output: list containing, for each element: [idpackage,repository name] @ if dependencies couldn't be satisfied, the output will be -1 @note: this is the function that should be used for 3rd party applications after using atomMatch() ''' def getRequiredPackages(foundAtoms, emptydeps = False, deepdeps = False, spinning = False): deptree = {} deptree[0] = set() if spinning: atomlen = len(foundAtoms); count = 0 matchFilter.clear() # clear generateDependencyTree global filter for atomInfo in foundAtoms: if spinning: count += 1; print_info(":: "+str(round((float(count)/atomlen)*100,1))+"% ::", back = True) #print depcount newtree, result = generateDependencyTree(atomInfo, emptydeps, deepdeps, usefilter = True) if (result != 0): return newtree, result elif (newtree): parent_keys = deptree.keys() # add conflicts max_parent_key = parent_keys[-1] deptree[0].update(newtree[0]) # reverse dict levelcount = 0 reversetree = {} for key in newtree.keys()[::-1]: if key == 0: continue levelcount += 1 reversetree[levelcount] = newtree[key] del newtree for mylevel in reversetree.keys(): deptree[max_parent_key+mylevel] = reversetree[mylevel].copy() del reversetree matchFilter.clear() return deptree,0 ''' @description: generates a depends tree using provided idpackages (from client database) !!! you can see it as the function that generates the removal tree @input package: idpackages list @output: depends tree dictionary, plus status code ''' def generateDependsTree(idpackages, deep = False): ''' caching ''' cached = generateDependsTreeCache.get(tuple(idpackages)) if cached: if (cached['deep'] == deep): return cached['result'] dependscache = {} clientDbconn = openClientDatabase() dependsOk = False treeview = set(idpackages) treelevel = idpackages[:] tree = {} treedepth = 0 # I start from level 1 because level 0 is idpackages itself tree[treedepth] = set(idpackages) monotree = set(idpackages) # monodimensional tree # check if dependstable is sane before beginning rx = clientDbconn.retrieveDepends(idpackages[0]) if rx == -2: # generation needed clientDbconn.regenerateDependsTable(output = False) rx = clientDbconn.retrieveDepends(idpackages[0]) while (not dependsOk): treedepth += 1 tree[treedepth] = set() for idpackage in treelevel: passed = dependscache.get(idpackage,None) systempkg = clientDbconn.isSystemPackage(idpackage) if passed or systempkg: try: while 1: treeview.remove(idpackage) except: pass continue # obtain its depends depends = clientDbconn.retrieveDepends(idpackage) # filter already satisfied ones depends = [x for x in depends if x not in list(monotree)] if (depends): # something depends on idpackage for x in depends: if x not in tree[treedepth]: tree[treedepth].add(x) monotree.add(x) treeview.add(x) elif deep: # if deep, grab its dependencies and check mydeps = set(clientDbconn.retrieveDependencies(idpackage)) _mydeps = set() for x in mydeps: match = clientDbconn.atomMatch(x) if match and match[1] == 0: _mydeps.add(match[0]) mydeps = _mydeps # now filter them mydeps = [x for x in mydeps if x not in list(monotree)] for x in mydeps: #print clientDbconn.retrieveAtom(x) mydepends = clientDbconn.retrieveDepends(x) mydepends = [y for y in mydepends if y not in list(monotree)] if (not mydepends): tree[treedepth].add(x) monotree.add(x) treeview.add(x) dependscache[idpackage] = True try: while 1: treeview.remove(idpackage) except: pass treelevel = list(treeview)[:] if (not treelevel): if not tree[treedepth]: del tree[treedepth] # probably the last one is empty then dependsOk = True newtree = tree.copy() # tree list if (tree): # now filter newtree treelength = len(newtree) for count in range(treelength)[::-1]: x = 0 while x < count: # remove dups in this list for z in newtree[count]: try: while 1: newtree[x].remove(z) #print "removing "+str(z) except: pass x += 1 del tree clientDbconn.closeDB() del clientDbconn ''' caching ''' generateDependsTreeCache[tuple(idpackages)] = {} generateDependsTreeCache[tuple(idpackages)]['result'] = newtree,0 generateDependsTreeCache[tuple(idpackages)]['deep'] = deep return newtree,0 # treeview is used to show deps while tree is used to run the dependency code. ######################################################## #### ## Files handling # ''' @description: check if Equo has to download the given package @input package: filename to check inside the packages directory -> file, checksum of the package -> checksum @output: -1 = should be downloaded, -2 = digest broken (not mandatory), remove & download, 0 = all fine, we don't need to download it ''' def checkNeededDownload(filepath,checksum = None): # is the file available if os.path.isfile(etpConst['entropyworkdir']+"/"+filepath): if checksum is None: return 0 else: # check digest md5res = compareMd5(etpConst['entropyworkdir']+"/"+filepath,checksum) if (md5res): return 0 else: return -2 else: return -1 def addFailingMirror(mirrorname,increment = 1): item = etpRemoteFailures.get(mirrorname) if item == None: etpRemoteFailures[mirrorname] = increment else: etpRemoteFailures[mirrorname] += increment # add a failure return etpRemoteFailures[mirrorname] def getFailingMirrorStatus(mirrorname): item = etpRemoteFailures.get(mirrorname) if item == None: return 0 else: return item ''' @description: download a package into etpConst['packagesbindir'] passing all the available mirrors @input package: repository -> name of the repository, filename -> name of the file to download, digest -> md5 hash of the file @output: 0 = all fine, -3 = error on all the available mirrors ''' def fetchFileOnMirrors(repository, filename, digest = False): uris = etpRepositories[repository]['packages'][::-1] remaining = set(uris[:]) mirrorcount = 0 for uri in uris: if not remaining: # tried all the mirrors, quitting for error return -3 mirrorcount += 1 mirrorCountText = "( mirror #"+str(mirrorcount)+" ) " url = uri+"/"+filename # check if uri is sane if getFailingMirrorStatus(uri) >= 30: # ohohoh! etpRemoteFailures[uri] = 30 # set to 30 for convenience print_warning(red(" ## ")+mirrorCountText+blue(" Mirror: ")+red(spliturl(url)[1])+" - maximum failure threshold reached.") if getFailingMirrorStatus(uri) == 30: addFailingMirror(uri,45) # put to 75 then decrement by 4 so we won't reach 30 anytime soon ahahaha else: # now decrement each time this point is reached, if will be back < 30, then equo will try to use it again if getFailingMirrorStatus(uri) > 31: addFailingMirror(uri,-4) else: # put to 0 - reenable mirror, welcome back uri! etpRemoteFailures[uri] = 0 remaining.remove(uri) continue # now fetch the new one print_info(red(" ## ")+mirrorCountText+blue("Downloading from: ")+red(spliturl(url)[1])) rc = fetchFile(url, digest) if rc == 0: print_info(red(" ## ")+mirrorCountText+blue("Successfully downloaded from: ")+red(spliturl(url)[1])+blue(" at "+str(bytesIntoHuman(etpFileTransfer['datatransfer']))+"/sec")) return 0 else: # something bad happened if rc == -1: print_warning(red(" ## ")+mirrorCountText+blue("Error downloading from: ")+red(spliturl(url)[1])+" - file not available on this mirror.") elif rc == -2: addFailingMirror(uri,1) print_warning(red(" ## ")+mirrorCountText+blue("Error downloading from: ")+red(spliturl(url)[1])+" - wrong checksum.") elif rc == -3: addFailingMirror(uri,2) print_warning(red(" ## ")+mirrorCountText+blue("Error downloading from: ")+red(spliturl(url)[1])+" - not found.") elif rc == -4: print_warning(red(" ## ")+mirrorCountText+blue("Discarded download.")) return -1 else: addFailingMirror(uri, 5) print_warning(red(" ## ")+mirrorCountText+blue("Error downloading from: ")+red(spliturl(url)[1])+" - unknown reason.") remaining.remove(uri) ''' @description: download a package into etpConst['packagesbindir'] and check for digest if digest is not False @input package: url -> HTTP/FTP url, digest -> md5 hash of the file @output: -1 = download error (cannot find the file), -2 = digest error, 0 = all fine ''' def fetchFile(url, digest = None): # remove old filename = os.path.basename(url) filepath = etpConst['packagesbindir']+"/"+etpConst['branch']+"/"+filename if os.path.exists(filepath): os.remove(filepath) # check if file has the right checksum, otherwise skip # XXX: problem is that mirrors don't support this (some because don't support php itself) #print remoteTools.getRemotePackageChecksum(spliturl(url)[1],filename,etpConst['branch']) # now fetch the new one try: fetchChecksum = remoteTools.downloadData(url,filepath) except KeyboardInterrupt: return -4 except: return -1 if fetchChecksum == "-3": return -3 if (digest): #print digest+" <--> "+fetchChecksum if (fetchChecksum != digest): # not properly downloaded return -2 else: return 0 return 0 def matchChecksum(infoDict): dlcount = 0 match = False while dlcount <= 5: print_info(red(" ## ")+blue("Checking package checksum..."), back = True) dlcheck = checkNeededDownload(infoDict['download'], checksum = infoDict['checksum']) if dlcheck == 0: print_info(red(" ## ")+blue("Package checksum matches.")) match = True break # file downloaded successfully else: dlcount += 1 print_info(red(" ## ")+blue("Package checksum does not match. Redownloading... attempt #"+str(dlcount)), back = True) fetch = fetchFileOnMirrors(infoDict['repository'],infoDict['download'],infoDict['checksum']) if fetch != 0: print_info(red(" ## ")+blue("Cannot properly fetch package! Quitting.")) return 1 if (not match): print_info(red(" ## ")+blue("Cannot properly fetch package or checksum does not match. Try running again '")+bold("equo update")+blue("'")) return 1 return 0 def removePackage(infoDict): atom = infoDict['removeatom'] content = infoDict['removecontent'] removeidpackage = infoDict['removeidpackage'] clientDbconn = openClientDatabase() equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Removing package: "+str(atom)) # clear on-disk cache generateDependsTreeCache.clear() dumpTools.dumpobj(etpCache['generateDependsTree'],generateDependsTreeCache) # remove from database if removeidpackage != -1: if not etpUi['quiet']: print_info(red(" ## ")+blue("Removing from database: ")+red(infoDict['removeatom'])) removePackageFromDatabase(removeidpackage) # Handle gentoo database if (etpConst['gentoo-compat']): gentooAtom = dep_striptag(remove_tag(atom)) # FIXME: remove dep_striptag asap equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Removing package from Gentoo database: "+str(gentooAtom)) removePackageFromGentooDatabase(gentooAtom) # load CONFIG_PROTECT and its mask - client database at this point has been surely opened, so our dicts are already filled protect = etpConst['dbconfigprotect'] mask = etpConst['dbconfigprotectmask'] # remove files from system directories = set() for item in content: # collision check if etpConst['collisionprotect'] > 0: if clientDbconn.isFileAvailable(item) and os.path.isfile(etpConst['systemroot']+item): # in this way we filter out directories if not etpUi['quiet']: print_warning(darkred(" ## ")+red("Collision found during remove of ")+etpConst['systemroot']+item+red(" - cannot overwrite")) equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Collision found during remove of "+etpConst['systemroot']+item+" - cannot overwrite") continue protected = False if (not infoDict['removeconfig']) and (not infoDict['diffremoval']): try: # -- CONFIGURATION FILE PROTECTION -- if os.access(etpConst['systemroot']+item,os.R_OK): for x in protect: if etpConst['systemroot']+item.startswith(x): protected = True break if (protected): for x in mask: if etpConst['systemroot']+item.startswith(x): protected = False break if (protected) and os.path.isfile(etpConst['systemroot']+item): protected = istextfile(etpConst['systemroot']+item) else: protected = False # it's not a file # -- CONFIGURATION FILE PROTECTION -- except: pass # some filenames are buggy encoded if (protected): equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_VERBOSE,"[remove] Protecting config file: "+etpConst['systemroot']+item) if not etpUi['quiet']: print_warning(darkred(" ## ")+red("[remove] Protecting config file: ")+etpConst['systemroot']+item) else: try: os.lstat(etpConst['systemroot']+item) except OSError: continue # skip file, does not exist except UnicodeEncodeError: print_warning(darkred(" ## ")+red("QA: ")+brown("this package contains a badly encoded file")) continue # file has a really bad encoding if os.path.isdir(etpConst['systemroot']+item) and os.path.islink(etpConst['systemroot']+item): # S_ISDIR returns False for directory symlinks, so using os.path.isdir # valid directory symlink #print "symlink dir",file directories.add((etpConst['systemroot']+item,"link")) elif os.path.isdir(etpConst['systemroot']+item): # plain directory #print "plain dir",file directories.add((etpConst['systemroot']+item,"dir")) else: # files, symlinks or not # just a file or symlink or broken directory symlink (remove now) try: #print "plain file",file os.remove(etpConst['systemroot']+item) # add its parent directory dirfile = os.path.dirname(etpConst['systemroot']+item) if os.path.isdir(dirfile) and os.path.islink(dirfile): #print "symlink dir2",dirfile directories.add((dirfile,"link")) elif os.path.isdir(dirfile): #print "plain dir2",dirfile directories.add((dirfile,"dir")) except OSError: pass # now handle directories directories = list(directories) directories.reverse() while 1: taint = False for directory in directories: mydir = etpConst['systemroot']+directory[0] if directory[1] == "link": try: mylist = os.listdir(mydir) if not mylist: try: os.remove(mydir) taint = True except OSError: pass except OSError: pass elif directory[1] == "dir": try: mylist = os.listdir(mydir) if not mylist: try: os.rmdir(mydir) taint = True except OSError: pass except OSError: pass if not taint: break del directories del content clientDbconn.closeDB() del clientDbconn return 0 ''' @description: unpack the given file on the system, update database and also update gentoo db if requested @input package: package file (without path) @output: 0 = all fine, >0 = error! ''' def installPackage(infoDict): clientDbconn = openClientDatabase() package = infoDict['download'] # clear on-disk cache generateDependsTreeCache.clear() dumpTools.dumpobj(etpCache['generateDependsTree'],{}) equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Installing package: "+str(infoDict['atom'])) # unpack and install if infoDict['repository'].endswith(".tbz2"): pkgpath = etpRepositories[infoDict['repository']]['pkgpath'] else: pkgpath = etpConst['entropyworkdir']+"/"+package unpackDir = etpConst['entropyunpackdir']+"/"+package if os.path.isdir(unpackDir): shutil.rmtree(unpackDir) imageDir = unpackDir+"/image" os.makedirs(imageDir) rc = uncompressTarBz2(pkgpath, imageDir, catchEmpty = True) if (rc != 0): return rc if not os.path.isdir(imageDir): return 2 # load CONFIG_PROTECT and its mask protect = etpRepositories[infoDict['repository']]['configprotect'] mask = etpRepositories[infoDict['repository']]['configprotectmask'] # copy files over rc = moveImageToSystem(imageDir, protect, mask) if rc != 0: return rc # inject into database print_info(red(" ## ")+blue("Updating database with: ")+red(infoDict['atom'])) newidpackage = installPackageIntoDatabase(infoDict['idpackage'], infoDict['repository']) # remove old files and gentoo stuff if (infoDict['removeidpackage'] != -1): # doing a diff removal equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Remove old package: "+str(infoDict['removeatom'])) infoDict['removeidpackage'] = -1 # disabling database removal if (etpConst['gentoo-compat']): equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Removing Gentoo database entry for "+str(infoDict['removeatom'])) print_info(red(" ## ")+blue("Cleaning old package files...")+" ## w/Gentoo compatibility") else: print_info(red(" ## ")+blue("Cleaning old package files...")) removePackage(infoDict) clientDbconn.closeDB() del clientDbconn rc = 0 if (etpConst['gentoo-compat']): equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Installing new Gentoo database entry: "+str(infoDict['atom'])) rc = installPackageIntoGentooDatabase(infoDict, pkgpath, newidpackage = newidpackage) # remove unpack dir shutil.rmtree(unpackDir,True) try: os.rmdir(unpackDir) except OSError: pass return rc def moveImageToSystem(imageDir, protect, mask): clientDbconn = openClientDatabase() # setup imageDir properly imageDir = imageDir.encode(getfilesystemencoding()) # merge data into system for currentdir,subdirs,files in os.walk(imageDir): # create subdirs for subdir in subdirs: imagepathDir = currentdir + "/" + subdir rootdir = etpConst['systemroot']+imagepathDir[len(imageDir):] #print rootdir # handle broken symlinks if os.path.islink(rootdir) and not os.path.exists(rootdir):# broken symlink os.remove(rootdir) # if our directory is a file on the live system elif os.path.isfile(rootdir): # really weird...! equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"WARNING!!! "+rootdir+" is a file when it should be a directory !! Removing in 10 seconds...") print_warning(red(" *** ")+bold(rootdir)+red(" is a file when it should be a directory !! Removing in 10 seconds...")) import time time.sleep(10) os.remove(rootdir) # if our directory is a symlink instead, then copy the symlink if os.path.islink(imagepathDir) and not os.path.isdir(rootdir): # for security we skip live items that are dirs tolink = os.readlink(imagepathDir) if os.path.islink(rootdir): os.remove(rootdir) os.symlink(tolink,rootdir) elif (not os.path.isdir(rootdir)) and (not os.access(rootdir,os.R_OK)): #print "creating dir "+rootdir os.makedirs(rootdir) if not os.path.islink(rootdir): # symlink don't need permissions, also until os.walk ends they might be broken user = os.stat(imagepathDir)[4] group = os.stat(imagepathDir)[5] os.chown(rootdir,user,group) shutil.copystat(imagepathDir,rootdir) for item in files: fromfile = currentdir+"/"+item tofile = etpConst['systemroot']+fromfile[len(imageDir):] #print tofile if etpConst['collisionprotect'] > 1: todbfile = fromfile[len(imageDir):] if clientDbconn.isFileAvailable(todbfile): print_warning(darkred(" ## ")+red("Collision found during install for ")+tofile+" - cannot overwrite") equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"WARNING!!! Collision found during install for "+tofile+" - cannot overwrite") equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"[collision] Protecting config file: "+tofile) print_warning(darkred(" ## ")+red("[collision] Protecting config file: ")+tofile) continue # -- CONFIGURATION FILE PROTECTION -- protected = False try: for x in protect: if tofile.startswith(x): protected = True break if (protected): # check if perhaps, file is masked, so unprotected for x in mask: if tofile.startswith(x): protected = False break if not os.path.lexists(tofile): protected = False # file doesn't exist # check if it's a text file if (protected) and os.path.isfile(tofile): protected = istextfile(tofile) else: protected = False # it's not a file # request new tofile then if (protected): if tofile not in etpConst['configprotectskip']: tofile, prot_status = allocateMaskedFile(tofile, fromfile) if not prot_status: protected = False else: oldtofile = tofile if oldtofile.find("._cfg") != -1: oldtofile = os.path.dirname(oldtofile)+"/"+os.path.basename(oldtofile)[10:] equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Protecting config file: "+oldtofile) print_warning(darkred(" ## ")+red("Protecting config file: ")+oldtofile) else: equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Skipping config file installation, as stated in equo.conf: "+tofile) print_warning(darkred(" ## ")+red("Skipping file installation: ")+tofile) continue # -- CONFIGURATION FILE PROTECTION -- except: pass # some files are buggy encoded try: # this also handles symlinks shutil.move(fromfile,tofile) except IOError,(errno,strerror): if errno == 2: # better to pass away, sometimes gentoo packages are fucked up and contain broken things pass else: rc = os.system("mv "+fromfile+" "+tofile) if (rc != 0): return 4 try: user = os.stat(fromfile)[4] group = os.stat(fromfile)[5] os.chown(tofile,user,group) shutil.copystat(fromfile,tofile) except: pass # sometimes, gentoo packages are fucked up and contain broken symlinks if (protected): # add to disk cache confTools.addtocache(tofile) clientDbconn.closeDB() del clientDbconn return 0 ''' @description: remove package entry from Gentoo database @input gentoo package atom (cat/name+ver): @output: 0 = all fine, <0 = error! ''' def removePackageFromGentooDatabase(atom): # handle gentoo-compat _portage_avail = False try: from portageTools import getPortageAppDbPath as _portage_getPortageAppDbPath, getInstalledAtoms as _portage_getInstalledAtoms _portage_avail = True except: return -1 # no Portage support if (_portage_avail): portDbDir = _portage_getPortageAppDbPath() removePath = portDbDir+atom #print removePath try: shutil.rmtree(removePath,True) except: pass key = dep_getkey(atom) othersInstalled = _portage_getInstalledAtoms(key) #FIXME: really slow if othersInstalled == None: # safest way (error free) is to use sed without loading the file # escape / skippedKey = '' for x in key: if x == "/": x = "\/" skippedKey += x os.system("sed -i '/"+skippedKey+"/d' "+etpConst['systemroot']+"/var/lib/portage/world") return 0 ''' @description: inject the database information into the Gentoo database @input package: dictionary containing information collected by installPackages (important are atom, slot, category, name, version) @output: 0 = all fine, >0 = error! ''' def installPackageIntoGentooDatabase(infoDict, packageFile, newidpackage = -1): # handle gentoo-compat _portage_avail = False try: import portageTools _portage_avail = True except: return -1 # no Portage support if (_portage_avail): portDbDir = portageTools.getPortageAppDbPath() # extract xpak from unpackDir+etpConst['packagecontentdir']+"/"+package key = infoDict['category']+"/"+infoDict['name'] #print portageTools.getInstalledAtom(key) atomsfound = set() dbdirs = os.listdir(portDbDir) if infoDict['category'] in dbdirs: catdirs = os.listdir(portDbDir+"/"+infoDict['category']) dirsfound = set([infoDict['category']+"/"+x for x in catdirs if key == dep_getkey(infoDict['category']+"/"+x)]) atomsfound.update(dirsfound) ### REMOVE # parse slot and match and remove if atomsfound: pkgToRemove = '' for atom in atomsfound: atomslot = portageTools.getPackageSlot(atom) # get slot from gentoo db if atomslot == infoDict['slot']: #print "match slot, remove -> "+str(atomslot) pkgToRemove = atom break if (pkgToRemove): removePath = portDbDir+pkgToRemove shutil.rmtree(removePath,True) try: os.rmdir(removePath) except OSError: pass #print "removing -> "+removePath del atomsfound ### INSTALL NEW extractTmp = etpConst['entropyunpackdir']+"/"+os.path.basename(packageFile) xpakPath = extractTmp+"/xpak" if os.path.isfile(etpConst['entropyunpackdir']+"/"+os.path.basename(packageFile)): os.remove(etpConst['entropyunpackdir']+"/"+os.path.basename(packageFile)) if os.path.isdir(xpakPath): shutil.rmtree(xpakPath) else: os.makedirs(xpakPath) smartpackage = False if infoDict['repository'].endswith(".tbz2"): smartpackage = etpRepositories[infoDict['repository']]['smartpackage'] if (smartpackage): # we need to get the .xpak from database xdbconn = openRepositoryDatabase(infoDict['repository']) xpakdata = xdbconn.retrieveXpakMetadata(infoDict['idpackage']) if xpakdata: # save into a file f = open(xpakPath+".xpak","wb") f.write(xpakdata) f.flush() f.close() xpakstatus = unpackXpak(xpakPath+".xpak",xpakPath) else: xpakstatus = None xdbconn.closeDB() del xdbconn else: xpakstatus = extractXpak(packageFile,xpakPath) if xpakstatus != None: if not os.path.isdir(portDbDir+infoDict['category']): os.makedirs(portDbDir+infoDict['category']) destination = portDbDir+infoDict['category']+"/"+infoDict['name']+"-"+infoDict['version'] if os.path.isdir(destination): shutil.rmtree(destination) shutil.move(xpakPath,destination) # clean temp directory shutil.rmtree(extractTmp,True) try: os.rmdir(extractTmp) except OSError: pass # test if /var/cache/edb/counter is fine if os.path.isfile(etpConst['edbcounter']): try: f = open(etpConst['edbcounter'],"r") counter = int(f.readline().strip()) f.close() except: # need file recreation, parse gentoo tree counter = portageTools.refillCounter() else: counter = portageTools.refillCounter() # write new counter to file if os.path.isdir(destination): counter += 1 f = open(destination+"/"+dbCOUNTER,"w") f.write(str(counter)+"\n") f.flush() f.close() f = open(etpConst['edbcounter'],"w") f.write(str(counter)) f.flush() f.close() # update counter inside clientDatabase clientDbconn = openClientDatabase() clientDbconn.setCounter(newidpackage,counter) clientDbconn.closeDB() del clientDbconn else: print "DEBUG: WARNING!! "+destination+" DOES NOT EXIST, CANNOT UPDATE COUNTER!!" return 0 ''' @description: injects package info into the installed packages database @input int(idpackage): idpackage matched into repository @input str(repository): name of the repository where idpackage is @output: 0 = all fine, >0 = error! ''' def installPackageIntoDatabase(idpackage, repository): # fetch info dbconn = openRepositoryDatabase(repository) data = dbconn.getPackageData(idpackage) dbconn.closeDB() del dbconn #print data['dependencies'] # open client db clientDbconn = openClientDatabase() idpk, rev, x, status = clientDbconn.handlePackage(etpData = data, forcedRevision = data['revision']) del x del data if (not status): print "DEBUG!!! THIS SHOULD NOT NEVER HAPPEN. Package "+str(idpk)+" has not been inserted, status: "+str(status) idpk = -1 # it hasn't been insterted ? why?? else: # all fine # add idpk to the installedtable clientDbconn.removePackageFromInstalledTable(idpk) clientDbconn.addPackageToInstalledTable(idpk,repository) # update dependstable try: depends = clientDbconn.listIdpackageDependencies(idpk) #print depends for depend in depends: atom = depend[1] iddep = depend[0] match = clientDbconn.atomMatch(atom) if (match[0] != -1): clientDbconn.removeDependencyFromDependsTable(iddep) clientDbconn.addDependRelationToDependsTable(iddep,match[0]) del depends except: clientDbconn.regenerateDependsTable() clientDbconn.closeDB() del clientDbconn return idpk ''' @description: remove the package from the installed packages database.. This function is a wrapper around databaseTools.removePackage that will let us to add our custom things @input int(idpackage): idpackage matched into repository @output: 0 = all fine, >0 = error! ''' def removePackageFromDatabase(idpackage): clientDbconn = openClientDatabase() clientDbconn.removePackage(idpackage) clientDbconn.closeDB() del clientDbconn return 0 ''' @description: execute the requested step (it is only used by the CLI client) @input: step -> name of the step to execute infoDict -> dictionary containing all the needed information collected by installPackages() -> actionQueue[pkgatom] loopString -> used to print to xterm title bar something like "10/900 - equo" @output: -1,"description" for error ; 0,True for no errors ''' def stepExecutor(step, infoDict, loopString = None): clientDbconn = openClientDatabase() output = 0 if loopString == None: loopString = '' if step == "fetch": print_info(red(" ## ")+blue("Fetching archive: ")+red(os.path.basename(infoDict['download']))) xtermTitle(loopString+' Fetching archive: '+os.path.basename(infoDict['download'])) output = fetchFileOnMirrors(infoDict['repository'],infoDict['download'],infoDict['checksum']) if output < 0: print_error(red("Package cannot be fetched. Try to run: '")+bold("equo update")+red("' and this command again. Error "+str(output))) elif step == "checksum": output = matchChecksum(infoDict) elif step == "install": compatstring = '' if (etpConst['gentoo-compat']): compatstring = " ## w/Gentoo compatibility" print_info(red(" ## ")+blue("Installing package: ")+red(os.path.basename(infoDict['atom']))+compatstring) xtermTitle(loopString+' Installing package: '+os.path.basename(infoDict['atom'])+compatstring) output = installPackage(infoDict) if output != 0: if output == 512: errormsg = red("You are running out of disk space. I bet, you're probably Michele. Error 512") else: errormsg = red("An error occured while trying to install the package. Check if your hard disk is healthy. Error "+str(output)) print_error(errormsg) elif step == "remove": gcompat = "" if (etpConst['gentoo-compat']): gcompat = " ## w/Gentoo compatibility" print_info(red(" ## ")+blue("Removing installed package: ")+red(infoDict['removeatom'])+gcompat) xtermTitle(loopString+' Removing installed package: '+os.path.basename(infoDict['removeatom'])+gcompat) output = removePackage(infoDict) if output != 0: errormsg = red("An error occured while trying to remove the package. Check if you have enough disk space on your hard disk. Error "+str(output)) print_error(errormsg) elif step == "showmessages": # get messages if infoDict['messages']: equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"Message from "+infoDict['atom']+" :") print_warning(brown(' ## ')+darkgreen("Gentoo ebuild messages:")) for msg in infoDict['messages']: equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,msg) print_warning(brown(' ## ')+msg) if infoDict['messages']: equoLog.log(ETP_LOGPRI_INFO,ETP_LOGLEVEL_NORMAL,"End message.") elif step == "postinstall": # analyze atom pkgdata = etpInstallTriggers.get(infoDict['atom']) if pkgdata: triggers = triggerTools.postinstall(pkgdata) for trigger in triggers: # code reuse, we'll fetch triggers list on the GUI client and run each trigger by itself if trigger not in etpUi['postinstall_triggers_disable']: eval("triggerTools."+trigger)(pkgdata) del triggers del pkgdata elif step == "preinstall": # analyze atom pkgdata = etpInstallTriggers.get(infoDict['atom']) if pkgdata: triggers = triggerTools.preinstall(pkgdata) if (infoDict.get("diffremoval") != None): # diffremoval is true only when the remove action is triggered by installPackages() if infoDict['diffremoval']: remdata = etpRemovalTriggers.get(infoDict['removeatom']) if remdata: itriggers = triggerTools.preremove(remdata) # remove duplicated triggers triggers.difference_update(itriggers) del itriggers del remdata for trigger in triggers: # code reuse, we'll fetch triggers list on the GUI client and run each trigger by itself if trigger not in etpUi['preinstall_triggers_disable']: eval("triggerTools."+trigger)(pkgdata) del triggers del pkgdata elif step == "preremove": # analyze atom remdata = etpRemovalTriggers.get(infoDict['removeatom']) if remdata: triggers = triggerTools.preremove(remdata) for trigger in triggers: # code reuse, we'll fetch triggers list on the GUI client and run each trigger by itself if trigger not in etpUi['preremove_triggers_disable']: eval("triggerTools."+trigger)(remdata) del triggers del remdata elif step == "postremove": # analyze atom remdata = etpRemovalTriggers.get(infoDict['removeatom']) if remdata: triggers = triggerTools.postremove(remdata) if infoDict['diffremoval'] and (infoDict.get("atom") != None): # diffremoval is true only when the remove action is triggered by installPackages() pkgdata = etpInstallTriggers.get(infoDict['atom']) # remove duplicated triggers if pkgdata: itriggers = triggerTools.postinstall(pkgdata) triggers.difference_update(itriggers) del itriggers del pkgdata for trigger in triggers: # code reuse, we'll fetch triggers list on the GUI client and run each trigger by itself if trigger not in etpUi['postremove_triggers_disable']: eval("triggerTools."+trigger)(remdata) del triggers del remdata clientDbconn.closeDB() del clientDbconn # clear garbage gc.collect() return output