Restructure system affection detection.
authorfuzzyray <fuzzyray@gentoo.org>
Wed, 20 May 2009 21:49:39 +0000 (21:49 -0000)
committerfuzzyray <fuzzyray@gentoo.org>
Wed, 20 May 2009 21:49:39 +0000 (21:49 -0000)
Store "vulnerable" and "upgrade" packages in a table, and use that
data to determine which packages cannot be upgraded, and which
packages actually cause upgrades

svn path=/trunk/gentoolkit/; revision=648

bin/glsa-check
pym/gentoolkit/glsa/__init__.py

index f1e8912ce2f28cd24cdd280acb7f0955a20247e8..d5ac4e1ff3e6f2d7c8f71d2ff712fa963763e1e7 100644 (file)
@@ -265,52 +265,47 @@ if mode in ["dump", "fix", "inject", "pretend"]:
                if mode == "dump":
                        myglsa.dump()
                elif mode == "fix":
-                       sys.stdout.write("fixing "+myid+"\n")
-                       mergelist = myglsa.getMergeList(least_change=least_change)
-                       if mergelist == None:
+                       sys.stdout.write("Fixing GLSA "+myid+"\n")
+                       if not myglsa.isVulnerable():
                                sys.stdout.write(">>> no vulnerable packages installed\n")
-                       elif mergelist == []:
-                               sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n")
-                               sys.exit(2)
-                       for pkg in mergelist:
-                               sys.stdout.write(">>> merging "+pkg+"\n")
-                               # using emerge for the actual merging as it contains the dependency
-                               # code and we want to be consistent in behaviour. Also this functionality
-                               # will be integrated in emerge later, so it shouldn't hurt much.
-                               emergecmd = "emerge --oneshot " + glsaconfig["EMERGE_OPTS"] + " =" + pkg
-                               if verbose:
-                                       sys.stderr.write(emergecmd+"\n")
-                               exitcode = os.system(emergecmd)
-                               # system() returns the exitcode in the high byte of a 16bit integer
-                               if exitcode >= 1<<8:
-                                       exitcode >>= 8
-                               if exitcode:
-                                       sys.exit(exitcode)
+                       else:
+                               mergelist = myglsa.getMergeList(least_change=least_change)
+                               if mergelist == []:
+                                       sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n")
+                                       sys.exit(2)
+                               for pkg in mergelist:
+                                       sys.stdout.write(">>> merging "+pkg+"\n")
+                                       # using emerge for the actual merging as it contains the dependency
+                                       # code and we want to be consistent in behaviour. Also this functionality
+                                       # will be integrated in emerge later, so it shouldn't hurt much.
+                                       emergecmd = "emerge --oneshot " + glsaconfig["EMERGE_OPTS"] + " =" + pkg
+                                       if verbose:
+                                               sys.stderr.write(emergecmd+"\n")
+                                       exitcode = os.system(emergecmd)
+                                       # system() returns the exitcode in the high byte of a 16bit integer
+                                       if exitcode >= 1<<8:
+                                               exitcode >>= 8
+                                       if exitcode:
+                                               sys.exit(exitcode)
                        if len(mergelist):
                                sys.stdout.write("\n")
                        myglsa.inject()
                elif mode == "pretend":
                        sys.stdout.write("Checking GLSA "+myid+"\n")
-                       mergelist = myglsa.getMergeList(least_change=least_change)
-                       if mergelist == None:
+                       if not myglsa.isVulnerable():
                                sys.stdout.write(">>> no vulnerable packages installed\n")
-                       elif mergelist == []:
-                               sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n")
-                               sys.exit(2)
-                       if mergelist:
-                               sys.stdout.write("The following updates will be performed for this GLSA:\n")
-                               for pkg in mergelist:
-                                       oldver = None
-                                       for x in vardb.match(portage.dep_getkey(pkg)):
-                                               if vardb.aux_get(x, ["SLOT"]) == portdb.aux_get(pkg, ["SLOT"]):
-                                                       oldver = x
-                                       if oldver == None:
-                                               raise ValueError("could not find old version for package %s" % pkg)
-                                       oldver = oldver[len(portage.dep_getkey(oldver))+1:]
-                                       sys.stdout.write("     " + pkg + " (" + oldver + ")\n")
                        else:
-                               sys.stdout.write("Nothing to do for this GLSA\n")
-                       sys.stdout.write("\n")
+                               mergedict = {}
+                               for (vuln, update) in myglsa.getAffectionTable(least_change=least_change):
+                                       mergedict.setdefault(update, []).append(vuln)
+                               
+                               sys.stdout.write(">>> The following updates will be performed for this GLSA:\n")
+                               for pkg in mergedict:
+                                       if pkg != "":
+                                               sys.stdout.write("     " + pkg + " (vulnerable: " + ", ".join(mergedict[pkg]) + ")\n")
+                               if "" in mergedict:
+                                       sys.stdout.write("\n>>> For the following packages, no upgrade path exists:\n")
+                                       sys.stdout.write("     " + ", ".join(mergedict[""]))
                elif mode == "inject":
                        sys.stdout.write("injecting " + myid + "\n")
                        myglsa.inject()
index 121f066f3f801877ee5c5566ae758d4294a272b0..2ee294f7e40bc48f789e9aeb0cd5a4d0c8798636 100644 (file)
@@ -398,29 +398,37 @@ def getMinUpgrade(vulnerableList, unaffectedList, minimize=True):
        v_installed = reduce(operator.add, [match(v, "vartree") for v in vulnerableList], [])
        u_installed = reduce(operator.add, [match(u, "vartree") for u in unaffectedList], [])
        
-       install_unaffected = True
-       for i in v_installed:
-               if i not in u_installed:
-                       install_unaffected = False
+       # remove all unaffected atoms from vulnerable list
+       v_installed = list(set(v_installed).difference(set(u_installed)))
 
-       if install_unaffected:
+       if not v_installed:
                return None
 
+       # this tuple holds all vulnerable atoms, and the related upgrade atom
+       vuln_update = []
+       avail_updates = set()
        for u in unaffectedList:
-               mylist = match(u, "porttree", match_type="match-all")
-               for c in mylist:
+               # TODO: This had match_type="match-all" before. I don't think it should
+               # since we disregarded masked items later anyway (match(=rValue, "porttree"))
+               avail_updates.update(match(u, "porttree"))
+       # if an atom is already installed, we should not consider it for upgrades
+       avail_updates.difference_update(u_installed)
+
+       for vuln in v_installed:
+               update = ""
+               for c in avail_updates:
                        c_pv = portage.catpkgsplit(c)
-                       i_pv = portage.catpkgsplit(portage.best(v_installed))
+                       i_pv = portage.catpkgsplit(vuln)
                        if portage.pkgcmp(c_pv[1:], i_pv[1:]) > 0 \
-                                       and (rValue == "" \
-                                               or not match("="+rValue, "porttree") \
-                                               or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(rValue)[1:]) > 0)) \
-                                                       and match("="+c, "porttree")) \
-                                       and portage.db["/"]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db["/"]["vartree"].dbapi.aux_get(portage.best(v_installed), ["SLOT"]):
-                               rValue = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
+                                       and (update == "" \
+                                               or (minimize ^ (portage.pkgcmp(c_pv[1:], portage.catpkgsplit(rValue)[1:]) > 0))) \
+                                       and portage.db["/"]["porttree"].dbapi.aux_get(c, ["SLOT"]) == portage.db["/"]["vartree"].dbapi.aux_get(vuln, ["SLOT"]):
+                               update = c_pv[0]+"/"+c_pv[1]+"-"+c_pv[2]
                                if c_pv[3] != "r0":             # we don't like -r0 for display
-                                       rValue += "-"+c_pv[3]
-       return rValue
+                                       update += "-"+c_pv[3]
+               vuln_update.append([vuln, update])
+
+       return vuln_update
 
 def format_date(datestr):
        """
@@ -658,8 +666,7 @@ class Glsa:
                                if path["arch"] == "*" or self.config["ARCH"] in path["arch"].split():
                                        for v in path["vul_atoms"]:
                                                rValue = rValue \
-                                                       or (len(match(v, "vartree")) > 0 \
-                                                               and None != getMinUpgrade(path["vul_atoms"], path["unaff_atoms"]))
+                                                       or (None != getMinUpgrade([v,], path["unaff_atoms"]))
                return rValue
        
        def isApplied(self):
@@ -700,10 +707,18 @@ class Glsa:
                @rtype:         List of Strings
                @return:        list of package-versions that have to be merged
                """
-               rValue = []
+               return list(set(update for (vuln, update) in self.getAffectionTable(least_change) if update))
+
+       def getAffectionTable(self, least_change=True):
+               """
+               Will initialize the self.systemAffection list of
+               atoms installed on the system that are affected
+               by this GLSA, and the atoms that are minimal upgrades.
+               """
+               systemAffection = []
                for pkg in self.packages.keys():
                        for path in self.packages[pkg]:
                                update = getMinUpgrade(path["vul_atoms"], path["unaff_atoms"], minimize=least_change)
                                if update:
-                                       rValue.append(update)
-               return rValue
+                                       systemAffection.extend(update)
+               return systemAffection