From 550eaed7e5bd46057279a0c2b10c8899e23cf316 Mon Sep 17 00:00:00 2001 From: Fabian Groffen Date: Mon, 16 Jul 2007 17:34:24 +0000 Subject: [PATCH] Merged from trunk 7269:7283 +------------------------------------------------------------------------+ | 7270 | sync MAILSUBJECT in make.conf with make.globals | | genone | | |--------+---------------------------------------------------------------| | 7271 | s/node/atom/ and other minor updates | | genone | | |--------+---------------------------------------------------------------| | 7272 | Make PackageSet to behave more like a real set, and change | | genone | loading logic a bit to avoid unnecessary reloads for editable | | | sets | |--------+---------------------------------------------------------------| | 7273 | use a more logical approach for editable sets | | genone | | |--------+---------------------------------------------------------------| | 7274 | fix load logic for editable sets once more | | genone | | |--------+---------------------------------------------------------------| | 7275 | ignore already applied glsas when loading the security set | | genone | | |--------+---------------------------------------------------------------| | 7276 | add support for metadata | | genone | | |--------+---------------------------------------------------------------| | 7277 | fix metadata parsing for file-based sets, add support for | | genone | user-defined file sets in test code | |--------+---------------------------------------------------------------| | 7278 | disable FEATURES=gpg as it's unmaintained, useless and likely | | genone | broken for many people | |--------+---------------------------------------------------------------| | 7279 | replace emerge.AtomSet with portage.sets.InternalPackageSet | | genone | | |--------+---------------------------------------------------------------| | 7280 | break recursion cycle | | genone | | |--------+---------------------------------------------------------------| | 7281 | Replace emerge.SystemSet with | | genone | portage.sets.profiles.PackagesSystemSet | |--------+---------------------------------------------------------------| | 7282 | break another recursion cycle, add special subclass for | | genone | "world" to handle locking | |--------+---------------------------------------------------------------| | 7283 | Replace emerge.WorldSet with portage.sets.files.WorldSet | | genone | | +------------------------------------------------------------------------+ svn path=/main/branches/prefix/; revision=7284 --- bin/prepstrip | 4 +- cnf/make.conf | 2 +- pym/emerge/__init__.py | 126 ++++----------------- pym/portage/__init__.py | 3 + pym/portage/elog/mod_mail.py | 15 +++ pym/portage/glsa.py | 4 +- pym/portage/sets/__init__.py | 206 +++++++++++++++++++++++++++-------- pym/portage/sets/dbapi.py | 22 ++-- pym/portage/sets/files.py | 70 ++++++++++-- pym/portage/sets/profiles.py | 3 +- pym/portage/sets/security.py | 51 ++++++--- pym/portage/sets/shell.py | 3 +- 12 files changed, 318 insertions(+), 191 deletions(-) diff --git a/bin/prepstrip b/bin/prepstrip index ca184f064..8fce32b1c 100755 --- a/bin/prepstrip +++ b/bin/prepstrip @@ -45,7 +45,7 @@ save_elf_debug() { hasq splitdebug ${FEATURES} || return 0 local x=$1 - local y="${ED}usr/lib/debug/${x:${#ED}}.debug" + local y="${ED}usr/lib/debug/${x:${#D}}.debug" # dont save debug info twice [[ ${x} == *".debug" ]] && return 0 @@ -116,7 +116,7 @@ do ${STRIP} ${PORTAGE_STRIP_FLAGS} "${x}" fi elif [[ ${f} == *"SB relocatable"* ]] ; then - vecho " ${x:${#D}}" + vecho " ${x:${#ED}}" save_elf_sources "${x}" if ${strip_this} ; then [[ ${x} == *.ko ]] && save_elf_debug "${x}" diff --git a/cnf/make.conf b/cnf/make.conf index d51f09f3b..73a441928 100644 --- a/cnf/make.conf +++ b/cnf/make.conf @@ -382,4 +382,4 @@ PORTAGE_ELOG_CLASSES="warn error log" # variables are expanded: # ${PACKAGE} - see description of PORTAGE_ELOG_COMMAND # ${HOST} - FQDN of the host portage is running on -#PORTAGE_ELOG_MAILSUBJECT="package \${PACKAGE} merged on \${HOST} with notice" +#PORTAGE_ELOG_MAILSUBJECT="[portage] ebuild log for \${PACKAGE} on \${HOST}" diff --git a/pym/emerge/__init__.py b/pym/emerge/__init__.py index b64dbd66e..2017786d6 100644 --- a/pym/emerge/__init__.py +++ b/pym/emerge/__init__.py @@ -53,6 +53,9 @@ from portage.const import EPREFIX from portage.data import secpass from portage.util import normalize_path as normpath from portage.util import writemsg +from portage.sets import InternalPackageSet +from portage.sets.profiles import PackagesSystemSet as SystemSet +from portage.sets.files import WorldSet from itertools import chain, izip from UserDict import DictMixin @@ -565,9 +568,8 @@ def getlist(settings, mode): def clean_world(vardb, cpv): """Remove a package from the world file when unmerged.""" - world_set = WorldSet(vardb.settings) + world_set = WorldSet("world", vardb.settings["ROOT"]) world_set.lock() - world_set.load() worldlist = list(world_set) mykey = portage.cpv_getkey(cpv) newworldlist = [] @@ -587,91 +589,9 @@ def clean_world(vardb, cpv): #this doesn't match the package we're unmerging; keep it. newworldlist.append(x) - world_set.clear() - world_set.update(newworldlist) - world_set.save() + world_set.replace(newworldlist) world_set.unlock() -class AtomSet(object): - def __init__(self, atoms=None): - self._atoms = {} - if atoms: - self.update(atoms) - def clear(self): - self._atoms.clear() - def add(self, atom): - cp = portage.dep_getkey(atom) - cp_list = self._atoms.get(cp) - if cp_list is None: - cp_list = [] - self._atoms[cp] = cp_list - if atom not in cp_list: - cp_list.append(atom) - def update(self, atoms): - for atom in atoms: - self.add(atom) - def __contains__(self, atom): - cp = portage.dep_getkey(atom) - if cp in self._atoms and atom in self._atoms[cp]: - return True - return False - def findAtomForPackage(self, cpv, metadata): - """Return the best match for a given package from the arguments, or - None if there are no matches. This matches virtual arguments against - the PROVIDE metadata. This can raise an InvalidDependString exception - if an error occurs while parsing PROVIDE.""" - cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) - cp = portage.dep_getkey(cpv) - atoms = self._atoms.get(cp) - if atoms: - best_match = portage.best_match_to_list(cpv_slot, atoms) - if best_match: - return best_match - if not metadata["PROVIDE"]: - return None - provides = portage.flatten(portage.dep.use_reduce( - portage.dep.paren_reduce(metadata["PROVIDE"]), - uselist=metadata["USE"].split())) - for provide in provides: - provided_cp = portage.dep_getkey(provide) - atoms = self._atoms.get(provided_cp) - if atoms: - transformed_atoms = [atom.replace(provided_cp, cp) for atom in atoms] - best_match = portage.best_match_to_list(cpv_slot, transformed_atoms) - if best_match: - return atoms[transformed_atoms.index(best_match)] - return None - def __iter__(self): - for atoms in self._atoms.itervalues(): - for atom in atoms: - yield atom - -class SystemSet(AtomSet): - def __init__(self, settings, **kwargs): - AtomSet.__init__(self, **kwargs) - self.update(getlist(settings, "system")) - -class WorldSet(AtomSet): - def __init__(self, settings, **kwargs): - AtomSet.__init__(self, **kwargs) - self.world_file = os.path.join(settings["ROOT"], portage.WORLD_FILE) - self._lock = None - def _ensure_dirs(self): - portage.util.ensure_dirs(os.path.dirname(self.world_file), - gid=portage.portage_gid, mode=02750, mask=02) - def load(self): - self.clear() - self.update(portage.util.grabfile_package(self.world_file)) - def save(self): - self._ensure_dirs() - portage.write_atomic(self.world_file, - "\n".join(sorted(self)) + "\n") - def lock(self): - self._ensure_dirs() - self._lock = portage.locks.lockfile(self.world_file, wantnewlockfile=1) - def unlock(self): - portage.locks.unlockfile(self._lock) - self._lock = None class RootConfig(object): """This is used internally by depgraph to track information about a @@ -681,10 +601,9 @@ class RootConfig(object): self.settings = trees["vartree"].settings self.root = self.settings["ROOT"] self.sets = {} - world_set = WorldSet(self.settings) - world_set.load() + world_set = WorldSet("world", self.root) self.sets["world"] = world_set - system_set = SystemSet(self.settings) + system_set = SystemSet("system", self.settings.profiles) self.sets["system"] = system_set def create_world_atom(pkg_key, metadata, args_set, root_config): @@ -1145,10 +1064,10 @@ class depgraph(object): # contains all sets added to the graph self._sets = {} # contains atoms given as arguments - self._sets["args"] = AtomSet() + self._sets["args"] = InternalPackageSet() # contains all atoms from all sets added to the graph, including # atoms given as arguments - self._set_atoms = AtomSet() + self._set_atoms = InternalPackageSet() # contains all nodes pulled in by self._set_atoms self._set_nodes = set() self.blocker_digraph = digraph() @@ -2589,8 +2508,7 @@ class depgraph(object): if verbosity is None: verbosity = ("--quiet" in self.myopts and 1 or \ "--verbose" in self.myopts and 3 or 2) - favorites_set = AtomSet() - favorites_set.update(favorites) + favorites_set = InternalPackageSet(favorites) changelogs=[] p=[] blockers = [] @@ -3070,10 +2988,10 @@ class depgraph(object): pkg_system = False pkg_world = False try: - pkg_system = system_set.findAtomForPackage(pkg_key, metadata) - pkg_world = world_set.findAtomForPackage(pkg_key, metadata) + pkg_system = system_set.containsCPV(pkg_key) + pkg_world = world_set.containsCPV(pkg_key) if not pkg_world and myroot == self.target_root and \ - favorites_set.findAtomForPackage(pkg_key, metadata): + favorites_set.containsCPV(pkg_key): # Maybe it will be added to world now. if create_world_atom(pkg_key, metadata, favorites_set, root_config): @@ -3279,7 +3197,6 @@ class depgraph(object): root_config = self.roots[self.target_root] world_set = root_config.sets["world"] world_set.lock() - world_set.load() args_set = self._sets["args"] portdb = self.trees[self.target_root]["porttree"].dbapi added_favorites = set() @@ -3304,8 +3221,6 @@ class depgraph(object): writemsg("!!! see '%s'\n\n" % os.path.join( root, portage.VDB_PATH, pkg_key, "PROVIDE"), noiselevel=-1) del e - if added_favorites: - world_set.save() world_set.unlock() def loadResumeCommand(self, resume_data): @@ -3538,7 +3453,7 @@ class MergeTask(object): root_config = RootConfig(self.trees[self.target_root]) system_set = root_config.sets["system"] - args_set = AtomSet(favorites) + args_set = InternalPackageSet(favorites) world_set = root_config.sets["world"] if "--resume" not in self.myopts: mymergelist = mylist @@ -3635,7 +3550,7 @@ class MergeTask(object): #buildsyspkg: Check if we need to _force_ binary package creation issyspkg = ("buildsyspkg" in myfeat) \ and x[0] != "blocks" \ - and system_set.findAtomForPackage(pkg_key, metadata) \ + and system_set.containsCPV(pkg_key) \ and "--buildpkg" not in self.myopts if x[0] in ["ebuild","blocks"]: if x[0] == "blocks" and "--fetchonly" not in self.myopts: @@ -3827,19 +3742,17 @@ class MergeTask(object): self.trees[x[1]]["vartree"].inject(x[2]) myfavkey = portage.cpv_getkey(x[2]) if not fetchonly and not pretend and \ - args_set.findAtomForPackage(pkg_key, metadata): + args_set.containsCPV(pkg_key): world_set.lock() - world_set.load() myfavkey = create_world_atom(pkg_key, metadata, args_set, root_config) if myfavkey: - world_set.add(myfavkey) print ">>> Recording",myfavkey,"in \"world\" favorites file..." emergelog(xterm_titles, " === ("+\ str(mergecount)+" of "+\ str(len(mymergelist))+\ ") Updating world file ("+x[pkgindex]+")") - world_set.save() + world_set.add(myfavkey) world_set.unlock() if "--pretend" not in self.myopts and \ @@ -5398,10 +5311,9 @@ def action_depclean(settings, trees, ldpath_mtimes, vardb = dep_check_trees[myroot]["vartree"].dbapi # Constrain dependency selection to the installed packages. dep_check_trees[myroot]["porttree"] = dep_check_trees[myroot]["vartree"] - system_set = SystemSet(settings) + system_set = SystemSet("system", settings.profiles) syslist = list(system_set) - world_set = WorldSet(settings) - world_set.load() + world_set = WorldSet("world", myroot) worldlist = list(world_set) fakedb = portage.fakedbapi(settings=settings) myvarlist = vardb.cpv_all() diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 34d723b11..be46009d4 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -1483,6 +1483,9 @@ class config(object): self.features.remove("usersandbox") self.features.sort() + if "gpg" in self.features: + writemsg(colorize("WARN", "!!! FEATURES=gpg is unmaintained, incomplete and broken. Disabling it."), noiselevel=-1) + self.features.remove("gpg") self["FEATURES"] = " ".join(self.features) self.backup_changes("FEATURES") diff --git a/pym/portage/elog/mod_mail.py b/pym/portage/elog/mod_mail.py index d0d2b33ac..c9fa5b563 100644 --- a/pym/portage/elog/mod_mail.py +++ b/pym/portage/elog/mod_mail.py @@ -16,6 +16,21 @@ def process(mysettings, key, logentries, fulltext): mysubject = mysubject.replace("${PACKAGE}", key) mysubject = mysubject.replace("${HOST}", socket.getfqdn()) + # look at the phases listed in our logentries to figure out what action was performed + action = "merged" + for phase in logentries.keys(): + # if we found a *rm phase assume that the package was unmerged + if phase in ["postrm", "prerm"]: + action = "unmerged" + # if we think that the package was unmerged, make sure there was no unexpected + # phase recorded to avoid misinformation + if action == "unmerged": + for phase in logentries.keys(): + if phase not in ["postrm", "prerm", "other"]: + action = "unknown" + + mysubject = mysubject.replace("${ACTION}", action) + mymessage = portage.mail.create_message(myfrom, myrecipient, mysubject, fulltext) portage.mail.send_mail(mysettings, mymessage) diff --git a/pym/portage/glsa.py b/pym/portage/glsa.py index ee6343640..d8053a820 100644 --- a/pym/portage/glsa.py +++ b/pym/portage/glsa.py @@ -556,7 +556,7 @@ class Glsa: @rtype: Boolean @returns: True if the GLSA was applied, False if not """ - aList = grabfile(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep))) + aList = grabfile(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep), "glsa")) return (self.nr in aList) def inject(self): @@ -569,7 +569,7 @@ class Glsa: @returns: None """ if not self.isApplied(): - checkfile = open(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep)), "a+") + checkfile = open(os.path.join(os.sep, self.config["ROOT"], CACHE_PATH.lstrip(os.sep), "glsa"), "a+") checkfile.write(self.nr+"\n") checkfile.close() return None diff --git a/pym/portage/sets/__init__.py b/pym/portage/sets/__init__.py index e0be0047c..91ddc3573 100644 --- a/pym/portage/sets/__init__.py +++ b/pym/portage/sets/__init__.py @@ -6,90 +6,187 @@ import os from portage.const import PRIVATE_PATH, USER_CONFIG_PATH from portage.exception import InvalidAtom -from portage.dep import isvalidatom +from portage.dep import isvalidatom, match_from_list, best_match_to_list, dep_getkey, use_reduce, paren_reduce +from portage import flatten -OPERATIONS = ["merge", "unmerge", "edit"] +OPERATIONS = ["merge", "unmerge"] DEFAULT_SETS = ["world", "system", "everything", "security"] \ +["package_"+x for x in ["mask", "unmask", "use", "keywords"]] class PackageSet(object): + # Set this to operations that are supported by your subclass. While + # technically there is no difference between "merge" and "unmerge" regarding + # package sets, the latter doesn't make sense for some sets like "system" + # or "security" and therefore isn't supported by them. _operations = ["merge"] - + _atommap = {} + description = "generic package set" + def __init__(self, name): - self._name = name - self._nodes = [] + self.name = name + self._atoms = set() + self._atommap = {} self._loaded = False + self._loading = False + + def __contains__(self, atom): + return atom in self.getAtoms() + + def __iter__(self): + for x in self.getAtoms(): + yield x def supportsOperation(self, op): if not op in OPERATIONS: raise ValueError(op) return op in self._operations - def getNodes(self): - if not self._loaded: + def getAtoms(self): + if not (self._loaded or self._loading): + self._loading = True self.load() self._loaded = True - return self._nodes - - def _setNodes(self, nodes): - nodes = map(str.strip, nodes) - for n in nodes[:]: - if n == "": - nodes.remove(n) - elif not isvalidatom(n): - raise InvalidAtom(n) - self._nodes = nodes - - def getName(self): - return self._name + self._loading = False + return self._atoms + + def _setAtoms(self, atoms): + atoms = map(str.strip, atoms) + for a in atoms[:]: + if a == "": + atoms.remove(a) + elif not isvalidatom(a): + raise InvalidAtom(a) + self._atoms = atoms + self._updateAtomMap() + + def load(self): + # This method must be overwritten by subclasses + # Editable sets should use the value of self._mtime to determine if they + # need to reload themselves + raise NotImplementedError() + + def containsCPV(self, cpv): + for a in self.getAtoms(): + if match_from_list(a, [cpv]): + return True + return False - def addNode(self, node): - if self.supportsOperation("edit"): - self.load() - self._nodes.append(node) - self.write() + def getMetadata(self, key): + if hasattr(self, key.lower()): + return getattr(self, key.lower()) else: - raise NotImplementedError() + return "" + + def _updateAtomMap(self): + for a in self._atoms: + cp = dep_getkey(a) + self._atommap.setdefault(cp, set()) + self._atommap[cp].add(a) + + # Not sure if this one should really be in PackageSet + def findAtomForPackage(self, cpv, metadata): + """Return the best match for a given package from the arguments, or + None if there are no matches. This matches virtual arguments against + the PROVIDE metadata. This can raise an InvalidDependString exception + if an error occurs while parsing PROVIDE.""" + cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) + cp = dep_getkey(cpv) + atoms = self._atommap.get(cp) + if atoms: + best_match = best_match_to_list(cpv_slot, atoms) + if best_match: + return best_match + if not metadata["PROVIDE"]: + return None + provides = flatten(use_reduce(paren_reduce(metadata["PROVIDE"]), + uselist=metadata["USE"].split())) + for provide in provides: + provided_cp = dep_getkey(provide) + atoms = self._atommap.get(provided_cp) + if atoms: + atoms = list(atoms) + transformed_atoms = [atom.replace(provided_cp, cp) for atom in atoms] + best_match = best_match_to_list(cpv_slot, transformed_atoms) + if best_match: + return atoms[transformed_atoms.index(best_match)] + return None - def removeNode(self, node): - if self.supportsOperation("edit"): - self.load() - self._nodes.remove(node) - self.write() - else: - raise NotImplementedError() +class EditablePackageSet(PackageSet): + def getAtoms(self): + self.load() + return self._atoms + + def update(self, atoms): + self.load() + self._atoms.update(atoms) + self._updateAtomMap() + self.write() + + def add(self, atom): + self.update([atom]) + + def replace(self, atoms): + self._setAtoms(atoms) + self.write() + + def remove(self, atom): + self.load() + self._atoms.discard(atom) + self._updateAtomMap() + self.write() + + def removePackageAtoms(self, cp): + self.load() + for a in self.getAtoms(): + if dep_getkey(a) == cp: + self.remove(a) + self.write() def write(self): + # This method must be overwritten in subclasses that should be editable raise NotImplementedError() + +class InternalPackageSet(EditablePackageSet): + def __init__(self, initial_atoms=None): + super(InternalPackageSet, self).__init__("") + if initial_atoms != None: + self.update(initial_atoms) + + def clear(self): + self._atoms.clear() + self._updateAtomMap() + def load(self): - raise NotImplementedError() + pass -class EmptyPackageSet(PackageSet): - _operations = ["merge", "unmerge"] + def write(self): + pass def make_default_sets(configroot, root, profile_paths, settings=None, vdbapi=None, portdbapi=None): from portage.sets.files import StaticFileSet, ConfigFileSet from portage.sets.profiles import PackagesSystemSet - from portage.sets.security import AffectedSet + from portage.sets.security import NewAffectedSet from portage.sets.dbapi import EverythingSet rValue = set() - rValue.add(StaticFileSet("world", os.path.join(root, PRIVATE_PATH, "world"))) + worldset = StaticFileSet("world", os.path.join(root, PRIVATE_PATH, "world")) + worldset.description = "Set of packages that were directly installed" + rValue.add(worldset) for suffix in ["mask", "unmask", "keywords", "use"]: myname = "package_"+suffix myset = ConfigFileSet(myname, os.path.join(configroot, USER_CONFIG_PATH.lstrip(os.sep), "package."+suffix)) rValue.add(myset) rValue.add(PackagesSystemSet("system", profile_paths)) if settings != None and portdbapi != None: - rValue.add(AffectedSet("security", settings, vdbapi, portdbapi)) + rValue.add(NewAffectedSet("security", settings, vdbapi, portdbapi)) else: - rValue.add(EmptyPackageSet("security")) + rValue.add(InternalPackageSet("security")) if vdbapi != None: rValue.add(EverythingSet("everything", vdbapi)) else: - rValue.add(EmptyPackageSet("everything")) + rValue.add(InternalPackageSet("everything")) return rValue @@ -108,13 +205,34 @@ def make_extra_static_sets(configroot): rValue.add(StaticFileSet(fname, os.path.join(mydir, myname))) return rValue +def make_category_sets(portdbapi, settings, only_visible=True): + from portage.sets.dbapi import CategorySet + rValue = set() + for c in settings.categories: + rValue.add(CategorySet("category_%s" % c, c, portdbapi, only_visible=only_visible)) + return rValue + # adhoc test code if __name__ == "__main__": - import portage + import portage, sys, os + from portage.sets.dbapi import CategorySet + from portage.sets.files import StaticFileSet l = make_default_sets("/", "/", portage.settings.profiles, portage.settings, portage.db["/"]["vartree"].dbapi, portage.db["/"]["porttree"].dbapi) l.update(make_extra_static_sets("/")) + if len(sys.argv) > 1: + for s in sys.argv[1:]: + if s.startswith("category_"): + c = s[9:] + l.add(CategorySet("category_%s" % c, c, portdbapi, only_visible=only_visible)) + elif os.path.exists(s): + l.add(StaticFileSet(os.path.basename(s), s)) + elif s != "*": + print "ERROR: could not create set '%s'" % s + if not "*" in sys.argv: + l = [s for s in l if s.name in sys.argv[1:]] for x in l: - print x.getName()+":" - for n in sorted(x.getNodes()): + print x.name+":" + print "DESCRIPTION = %s" % x.getMetadata("Description") + for n in sorted(x.getAtoms()): print "- "+n print diff --git a/pym/portage/sets/dbapi.py b/pym/portage/sets/dbapi.py index dcc0552ef..1b2520ef6 100644 --- a/pym/portage/sets/dbapi.py +++ b/pym/portage/sets/dbapi.py @@ -8,21 +8,22 @@ from portage.sets import PackageSet class EverythingSet(PackageSet): _operations = ["merge", "unmerge"] - + description = "Package set containing all installed packages" + def __init__(self, name, vdbapi): super(EverythingSet, self).__init__(name) self._db = vdbapi def load(self): - mynodes = [] + myatoms = [] for cp in self._db.cp_all(): if len(self._db.cp_list(cp)) > 1: for cpv in self._db.cp_list(cp): myslot = self._db.aux_get(cpv, ["SLOT"])[0] - mynodes.append(cp+":"+myslot) + myatoms.append(cp+":"+myslot) else: - mynodes.append(cp) - self._setNodes(mynodes) + myatoms.append(cp) + self._setAtoms(myatoms) class CategorySet(PackageSet): _operations = ["merge", "unmerge"] @@ -32,12 +33,17 @@ class CategorySet(PackageSet): self._db = portdbapi self._category = category self._check = only_visible + if only_visible: + s="visible" + else: + s="all" + self.description = "Package set containing %s packages of category %s" % (s, self._category) def load(self): - mynodes = [] + myatoms = [] for cp in self._db.cp_all(): if catsplit(cp)[0] == self._category: if (not self._check) or len(self._db.match(cp)) > 0: - mynodes.append(cp) - self._setNodes(mynodes) + myatoms.append(cp) + self._setAtoms(myatoms) diff --git a/pym/portage/sets/files.py b/pym/portage/sets/files.py index 8e1bca99c..7fa5fb24d 100644 --- a/pym/portage/sets/files.py +++ b/pym/portage/sets/files.py @@ -2,29 +2,75 @@ # Distributed under the terms of the GNU General Public License v2 # $Id$ -from portage.util import grabfile_package, grabdict_package, write_atomic +from portage.util import grabfile, grabfile_package, grabdict_package, write_atomic, ensure_dirs +from portage.const import PRIVATE_PATH +from portage.locks import lockfile, unlockfile +from portage import portage_gid +import os -from portage.sets import PackageSet +from portage.sets import PackageSet, EditablePackageSet -class StaticFileSet(PackageSet): - _operations = ["merge", "unmerge", "edit"] +class StaticFileSet(EditablePackageSet): + _operations = ["merge", "unmerge"] def __init__(self, name, filename): super(StaticFileSet, self).__init__(name) self._filename = filename + self._mtime = None + self.description = "Package set loaded from file %s" % self._filename + metadata = grabfile(self._filename + ".metadata") + key = None + for line in metadata: + line = line.strip() + if len(line) == 0 and key != None: + setattr(self, key, " ".join(value)) + key = None + elif line[-1] == ":" and key == None: + key = line[:-1].lower() + value = [] + elif key != None: + value.append(line) + else: + pass + else: + if key != None: + setattr(self, key, " ".join(value)) def write(self): - write_atomic(self._filename, "\n".join(self._nodes)+"\n") + write_atomic(self._filename, "\n".join(self._atoms)+"\n") def load(self): - self._setNodes(grabfile_package(self._filename, recursive=True)) + try: + mtime = os.stat(self._filename).st_mtime + except (OSError, IOError): + mtime = None + if (not self._loaded or self._mtime != mtime): + self._setAtoms(grabfile_package(self._filename, recursive=True)) + self._mtime = mtime -class ConfigFileSet(StaticFileSet): - _operations = ["merge", "unmerge"] +class ConfigFileSet(PackageSet): + def __init__(self, name, filename): + super(ConfigFileSet, self).__init__(name) + self._filename = filename + self.description = "Package set generated from %s" % self._filename - def write(self): - raise NotImplementedError() - def load(self): - self._setNodes(grabdict_package(self._filename, recursive=True).keys()) + self._setAtoms(grabdict_package(self._filename, recursive=True).keys()) + +class WorldSet(StaticFileSet): + description = "Set of packages that were directly installed by the user" + + def __init__(self, name, root): + super(WorldSet, self).__init__(name, os.path.join(os.sep, root, PRIVATE_PATH, "world")) + self._lock = None + + def _ensure_dirs(self): + ensure_dirs(os.path.dirname(self._filename), gid=portage_gid, mode=02750, mask=02) + + def lock(self): + self._ensure_dirs() + self._lock = lockfile(self._filename, wantnewlockfile=1) + def unlock(self): + unlockfile(self._lock) + self._lock = None diff --git a/pym/portage/sets/profiles.py b/pym/portage/sets/profiles.py index fe7ab201a..55b7942f5 100644 --- a/pym/portage/sets/profiles.py +++ b/pym/portage/sets/profiles.py @@ -13,8 +13,9 @@ class PackagesSystemSet(PackageSet): def __init__(self, name, profile_paths): super(PackagesSystemSet, self).__init__(name) self._profile_paths = profile_paths + self.description = "System packages for profile %s" % self._profile_paths[-1] def load(self): mylist = [grabfile_package(os.path.join(x, "packages")) for x in self._profile_paths] mylist = stack_lists(mylist, incremental=1) - self._setNodes([x[1:] for x in mylist if x[0] == "*"]) + self._setAtoms([x[1:] for x in mylist if x[0] == "*"]) diff --git a/pym/portage/sets/security.py b/pym/portage/sets/security.py index d7db89d95..3bc9cbca4 100644 --- a/pym/portage/sets/security.py +++ b/pym/portage/sets/security.py @@ -3,41 +3,66 @@ # $Id$ import portage.glsa as glsa +from portage.util import grabfile +from portage.const import CACHE_PATH +import os from portage.sets import PackageSet class SecuritySet(PackageSet): _operations = ["merge"] - + _skip_applied = False + + description = "package set that includes all packages possibly affected by a GLSA" + def __init__(self, name, settings, vardbapi, portdbapi): super(SecuritySet, self).__init__(name) self._settings = settings self._vardbapi = vardbapi self._portdbapi = portdbapi + self._checkfile = os.path.join(os.sep, self._settings["ROOT"], CACHE_PATH.lstrip(os.sep), "glsa") + + def getGlsaList(self, skip_applied): + glsaindexlist = glsa.get_glsa_list(self._settings) + if skip_applied: + applied_list = grabfile(self._checkfile) + glsaindexlist = set(glsaindexlist).difference(applied_list) + glsaindexlist = list(glsaindexlist) + glsaindexlist.sort() + return glsaindexlist - def loadCheckfile(self): - self._appliedlist = grabfile(os.path.join(os.sep, self._settings["ROOT"], CACHE_PATH, "glsa")) - def load(self): - glsaindexlist = glsa.get_glsa_list(self._settings) - nodelist = [] + glsaindexlist = self.getGlsaList(self._skip_applied) + atomlist = [] for glsaid in glsaindexlist: myglsa = glsa.Glsa(glsaid, self._settings, self._vardbapi, self._portdbapi) #print glsaid, myglsa.isVulnerable(), myglsa.isApplied(), myglsa.getMergeList() if self.useGlsa(myglsa): - nodelist += myglsa.getMergeList(least_change=False) - self._setNodes(nodelist) - - def useGlsaId(self, glsaid): - return True + atomlist += myglsa.getMergeList(least_change=False) + self._setAtoms(atomlist) def useGlsa(self, myglsa): return True + + def updateAppliedList(self): + glsaindexlist = self.getGlsaList(True) + applied_list = grabfile(self._checkfile) + for glsaid in glsaindexlist: + myglsa = glsa.Glsa(glsaid, self._settings, self._vardbapi, self._portdbapi) + if not myglsa.isVulnerable(): + applied_list.append(glsaid) + write_atomic(self._checkfile, "\n".join(applied_list)) class NewGlsaSet(SecuritySet): - def useGlsa(self, myglsa): - return not myglsa.isApplied() + _skip_applied = True + description = "Package set that includes all packages possibly affected by an unapplied GLSA" class AffectedSet(SecuritySet): + description = "Package set that includes all packages affected by an unapplied GLSA" + def useGlsa(self, myglsa): return myglsa.isVulnerable() + +class NewAffectedSet(AffectedSet): + _skip_applied = True + description = "Package set that includes all packages affected by an unapplied GLSA" diff --git a/pym/portage/sets/shell.py b/pym/portage/sets/shell.py index 8874743c9..2383dba81 100644 --- a/pym/portage/sets/shell.py +++ b/pym/portage/sets/shell.py @@ -12,10 +12,11 @@ class CommandOutputSet(PackageSet): def __init__(self, name, command): super(CommandOutputSet, self).__init__(name) self._command = command + self.description = "Package set generated from output of '%s'" % self._command def load(self): pipe = subprocess.Popen(self._command, stdout=subprocess.PIPE, shell=True) if pipe.wait() == os.EX_OK: text = pipe.stdout.read() - self._setNodes(text.split("\n")) + self._setAtoms(text.split("\n")) -- 2.26.2