Merged from trunk 8470:8481
authorFabian Groffen <grobian@gentoo.org>
Thu, 15 Nov 2007 19:04:14 +0000 (19:04 -0000)
committerFabian Groffen <grobian@gentoo.org>
Thu, 15 Nov 2007 19:04:14 +0000 (19:04 -0000)
   | 8471    | make.globals should not be relative to config_root because   |
   | zmedico | it only contains constants.                                  |

   | 8472    | Revert accidental hunk from bug 198491. Haven't tested this  |
   | zmedico | yet.                                                         |

   | 8473    | Bug #198491 - Disable termios.OPOST post-processing of       |
   | zmedico | output on the slave pty file descriptor since otherwise      |
   |         | weird things like \n -> \r\n transformations may occur.      |
   |         | Thanks to Ulrich Mueller <ulm@gentoo.org> for this patch.    |

   | 8474    | Remove the workaround for \n -> \r\n transformations in      |
   | zmedico | SpawnTestCase.testLogfile() since the problem is solved by   |
   |         | the patch from bug #198491.                                  |

   | 8475    | Create classes to encapsulate eache of the depgraph argument |
   | zmedico | types: atoms, packages and sets.                             |

   | 8476    | Fix incorrect type in depgraph._missing_args.                |
   | zmedico |                                                              |

   | 8477    | revert getMergeList to return CPVs again instead of atoms    |
   | genone  |                                                              |

   | 8478    | Move glsa-check from gentoolkit into portage so the          |
   | genone  | gentoolkit version can be removed after 2.2 is released      |

   | 8479    | move checkfile parsing into its own function                 |
   | genone  |                                                              |

   | 8480    | if multiple glsas result in the same cat/pkg:slot being      |
   | genone  | affected only use the highest resulting atom                 |

   | 8481    | fix typo and missing classmethod declaration                 |
   | genone  |                                                              |

svn path=/main/branches/prefix/; revision=8504

bin/emaint
bin/glsa-check [new file with mode: 0644]
pym/_emerge/__init__.py
pym/portage/__init__.py
pym/portage/glsa.py
pym/portage/sets/security.py
pym/portage/sets/shell.py
pym/portage/tests/ebuild/test_spawn.py

index ef03c247720965ff08f00a5f3ea825dff4d08fab..2bffd0f420eb097dcaffdcc87816452d98f26609 100755 (executable)
@@ -2,9 +2,8 @@
 
 import sys, os, time, signal
 from optparse import OptionParser, OptionValueError
-if not hasattr(__builtins__, "set"):
-       from sets import Set as set
 import re
+
 try:
        import portage
 except ImportError:
diff --git a/bin/glsa-check b/bin/glsa-check
new file mode 100644 (file)
index 0000000..8d95552
--- /dev/null
@@ -0,0 +1,336 @@
+#!/usr/bin/python
+
+# $Header: $
+# This program is licensed under the GPL, version 2
+
+import os
+import sys
+
+try:
+       import portage
+except ImportError:
+       from os import path as osp
+       sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
+       import portage
+
+from portage.output import *
+
+from getopt import getopt, GetoptError
+
+__program__ = "glsa-check"
+__author__ = "Marius Mauch <genone@gentoo.org>"
+__version__ = "1.0"
+
+optionmap = [
+["-l", "--list", "list all unapplied GLSA"],
+["-d", "--dump", "--print", "show all information about the given GLSA"],
+["-t", "--test", "test if this system is affected by the given GLSA"],
+["-p", "--pretend", "show the necessary commands to apply this GLSA"],
+["-f", "--fix", "try to auto-apply this GLSA (experimental)"],
+["-i", "--inject", "inject the given GLSA into the checkfile"],
+["-n", "--nocolor", "disable colors (option)"],
+["-e", "--emergelike", "do not use a least-change algorithm (option)"],
+["-h", "--help", "show this help message"],
+["-V", "--version", "some information about this tool"],
+["-v", "--verbose", "print more information (option)"],
+["-c", "--cve", "show CAN ids in listing mode (option)"],
+["-m", "--mail", "send a mail with the given GLSAs to the administrator"]
+]
+
+# option parsing
+args = []
+params = []
+try:
+       args, params = getopt(sys.argv[1:], "".join([o[0][1] for o in optionmap]), \
+               [x[2:] for x in reduce(lambda x,y: x+y, [z[1:-1] for z in optionmap])])
+#              ["dump", "print", "list", "pretend", "fix", "inject", "help", "verbose", "version", "test", "nocolor", "cve", "mail"])
+       args = [a for a,b in args]
+       
+       for option in ["--nocolor", "-n"]:
+               if option in args:
+                       nocolor()
+                       args.remove(option)
+                       
+       verbose = False
+       for option in ["--verbose", "-v"]:
+               if option in args:
+                       verbose = True
+                       args.remove(option)
+
+       list_cve = False
+       for option in ["--cve", "-c"]:
+               if option in args:
+                       list_cve = True
+                       args.remove(option)
+       
+       least_change = True
+       for option in ["--emergelike", "-e"]:
+               if option in args:
+                       least_change = False
+                       args.remove(option)
+
+       # sanity checking
+       if len(args) <= 0:
+               sys.stderr.write("no option given: what should I do ?\n")
+               mode="help"
+       elif len(args) > 1:
+               sys.stderr.write("please use only one command per call\n")
+               mode = "help"
+       else:
+               # in what mode are we ?
+               args = args[0]
+               for m in optionmap:
+                       if args in [o for o in m[:-1]]:
+                               mode = m[1][2:]
+
+except GetoptError, e:
+       sys.stderr.write("unknown option given: ")
+       sys.stderr.write(str(e)+"\n")
+       mode = "help"
+
+# we need a set of glsa for most operation modes
+if len(params) <= 0 and mode in ["fix", "test", "pretend", "dump", "inject", "mail"]:
+       sys.stderr.write("\nno GLSA given, so we'll do nothing for now. \n")
+       sys.stderr.write("If you want to run on all GLSA please tell me so \n")
+       sys.stderr.write("(specify \"all\" as parameter)\n\n")
+       mode = "help"
+elif len(params) <= 0 and mode == "list":
+       params.append("new")
+       
+# show help message
+if mode == "help":
+       sys.stderr.write("\nSyntax: glsa-check <option> [glsa-list]\n\n")
+       for m in optionmap:
+               sys.stderr.write(m[0] + "\t" + m[1] + "   \t: " + m[-1] + "\n")
+               for o in m[2:-1]:
+                       sys.stderr.write("\t" + o + "\n")
+       sys.stderr.write("\nglsa-list can contain an arbitrary number of GLSA ids, \n")
+       sys.stderr.write("filenames containing GLSAs or the special identifiers \n")
+       sys.stderr.write("'all', 'new' and 'affected'\n")
+       sys.exit(1)
+
+# we need root priviledges for write access
+if mode in ["fix", "inject"] and os.geteuid() != 0:
+       sys.stderr.write("\nThis tool needs root access to "+mode+" this GLSA\n\n")
+       sys.exit(2)
+
+# show version and copyright information
+if mode == "version":
+       sys.stderr.write("\n"+ __program__ + ", version " + __version__ + "\n")
+       sys.stderr.write("Author: " + __author__ + "\n")
+       sys.stderr.write("This program is licensed under the GPL, version 2\n\n")
+       sys.exit(0)
+
+# delay this for speed increase
+from portage.glsa import *
+
+vardb = portage.db[portage.settings["ROOT"]]["vartree"].dbapi
+portdb = portage.db["/"]["porttree"].dbapi
+
+# build glsa lists
+completelist = get_glsa_list(portage.settings)
+
+checklist = get_applied_glsas(portage.settings)
+todolist = [e for e in completelist if e not in checklist]
+
+glsalist = []
+if "new" in params:
+       glsalist = todolist
+       params.remove("new")
+       
+if "all" in params:
+       glsalist = completelist
+       params.remove("all")
+if "affected" in params:
+       # replaced completelist with todolist on request of wschlich
+       for x in todolist:
+               try:
+                       myglsa = Glsa(x, portage.settings, vardb, portdb)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (x, e)))
+                       continue
+               if myglsa.isVulnerable():
+                       glsalist.append(x)
+       params.remove("affected")
+
+# remove invalid parameters
+for p in params[:]:
+       if not (p in completelist or os.path.exists(p)):
+               sys.stderr.write(("(removing %s from parameter list as it isn't a valid GLSA specification)\n" % p))
+               params.remove(p)
+
+glsalist.extend([g for g in params if g not in glsalist])
+
+def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr):
+       fd2.write(white("[A]")+" means this GLSA was already applied,\n")
+       fd2.write(green("[U]")+" means the system is not affected and\n")
+       fd2.write(red("[N]")+" indicates that the system might be affected.\n\n")
+
+       for myid in myglsalist:
+               try:
+                       myglsa = Glsa(myid, portage.settings, vardb, portdb)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               fd2.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+                       continue
+               if myglsa.isApplied():
+                       status = "[A]"
+                       color = white
+               elif myglsa.isVulnerable():
+                       status = "[N]"
+                       color = red
+               else:
+                       status = "[U]"
+                       color = green
+
+               if verbose:
+                       access = ("[%-8s] " % myglsa.access)
+               else:
+                       access=""
+
+               fd1.write(color(myglsa.nr) + " " + color(status) + " " + color(access) + myglsa.title + " (")
+               if not verbose:
+                       for pkg in myglsa.packages.keys()[:3]:
+                               fd1.write(" " + pkg + " ")
+                       if len(myglsa.packages) > 3:
+                               fd1.write("... ")
+               else:
+                       for pkg in myglsa.packages.keys():
+                               mylist = vardb.match(portage.dep_getkey(pkg))
+                               if len(mylist) > 0:
+                                       pkg = color(" ".join(mylist))
+                               fd1.write(" " + pkg + " ")
+
+               fd1.write(")")
+               if list_cve:
+                       fd1.write(" "+(",".join([r[:13] for r in myglsa.references if r[:4] in ["CAN-", "CVE-"]])))
+               fd1.write("\n")         
+       return 0
+
+if mode == "list":
+       sys.exit(summarylist(glsalist))
+
+# dump, fix, inject and fix are nearly the same code, only the glsa method call differs
+if mode in ["dump", "fix", "inject", "pretend"]:
+       for myid in glsalist:
+               try:
+                       myglsa = Glsa(myid, portage.settings, vardb, portdb)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+                       continue
+               if mode == "dump":
+                       myglsa.dump()
+               elif mode == "fix":
+                       sys.stdout.write("fixing "+myid+"\n")
+                       mergelist = myglsa.getMergeList(least_change=least_change)
+                       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 " + portage.settings["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)
+                       myglsa.inject()
+               elif mode == "pretend":
+                       sys.stdout.write("Checking GLSA "+myid+"\n")
+                       mergelist = myglsa.getMergeList(least_change=least_change)
+                       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")
+               elif mode == "inject":
+                       sys.stdout.write("injecting " + myid + "\n")
+                       myglsa.inject()
+               sys.stdout.write("\n")
+       sys.exit(0)
+
+# test is a bit different as Glsa.test() produces no output
+if mode == "test":
+       outputlist = []
+       for myid in glsalist:
+               try:
+                       myglsa = Glsa(myid, portage.settings, vardb, portdb)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+                       continue
+               if myglsa.isVulnerable():
+                       if verbose:
+                               outputlist.append(str(myglsa.nr)+" ( "+myglsa.title+" ) ")
+                       else:
+                               outputlist.append(str(myglsa.nr))
+       if len(outputlist) > 0:
+               sys.stderr.write("This system is affected by the following GLSAs:\n")
+               sys.stdout.write("\n".join(outputlist)+"\n")
+       else:
+               sys.stderr.write("This system is not affected by any of the listed GLSAs\n")
+       sys.exit(0)
+
+# mail mode as requested by solar
+if mode == "mail":
+       import portage.mail, socket
+       from StringIO import StringIO
+       from email.mime.text import MIMEText
+       
+       # color doesn't make any sense for mail
+       nocolor()
+
+       if portage.settings.has_key("PORTAGE_ELOG_MAILURI"):
+               myrecipient = portage.settings["PORTAGE_ELOG_MAILURI"].split()[0]
+       else:
+               myrecipient = "root@localhost"
+       
+       if portage.settings.has_key("PORTAGE_ELOG_MAILFROM"):
+               myfrom = portage.settings["PORTAGE_ELOG_MAILFROM"]
+       else:
+               myfrom = "glsa-check"
+
+       mysubject = "[glsa-check] Summary for %s" % socket.getfqdn()
+
+       # need a file object for summarylist()
+       myfd = StringIO()
+       myfd.write("GLSA Summary report for host %s\n" % socket.getfqdn())
+       myfd.write("(Command was: %s)\n\n" % " ".join(sys.argv))
+       summarylist(glsalist, fd1=myfd, fd2=myfd)
+       summary = str(myfd.getvalue())
+       myfd.close()
+
+       myattachments = []
+       for myid in glsalist:
+               try:
+                       myglsa = Glsa(myid, portage.settings, vardb, portdb)
+               except (GlsaTypeException, GlsaFormatException), e:
+                       if verbose:
+                               sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
+                       continue
+               myfd = StringIO()
+               myglsa.dump(outstream=myfd)
+               myattachments.append(MIMEText(str(myfd.getvalue()), _charset="utf8"))
+               myfd.close()
+               
+       mymessage = portage.mail.create_message(myfrom, myrecipient, mysubject, summary, myattachments)
+       portage.mail.send_mail(portage.settings, mymessage)
+               
+       sys.exit(0)
+       
+# something wrong here, all valid paths are covered with sys.exit()
+sys.stderr.write("nothing more to do\n")
+sys.exit(2)
index d650041e6ce2169eae02f902d7933c7d7d9f51a5..bc0702155619d140d2386be01c303396d1b92ba6 100644 (file)
@@ -946,6 +946,28 @@ class Package(object):
                        self._digraph_node = (self.type_name, self.root, self.cpv, status)
                return self._digraph_node
 
+class DependencyArg(object):
+       def __init__(self, arg=None, root_config=None):
+               self.arg = arg
+               self.root_config = root_config
+
+class AtomArg(DependencyArg):
+       def __init__(self, atom=None, **kwargs):
+               DependencyArg.__init__(self, **kwargs)
+               self.atom = atom
+
+class PackageArg(DependencyArg):
+       def __init__(self, package=None, **kwargs):
+               DependencyArg.__init__(self, **kwargs)
+               self.package = package
+               self.atom = "=" + package.cpv
+
+class SetArg(DependencyArg):
+       def __init__(self, set=None, **kwargs):
+               DependencyArg.__init__(self, **kwargs)
+               self.set = set
+               self.name = self.arg[len(SETPREFIX):]
+
 class Dependency(object):
        __slots__ = ("__weakref__", "atom", "blocker", "depth",
                "parent", "priority", "root")
@@ -1688,7 +1710,7 @@ class depgraph(object):
                portdb = self.trees[myroot]["porttree"].dbapi
                bindb = self.trees[myroot]["bintree"].dbapi
                pkgsettings = self.pkgsettings[myroot]
-               arg_atoms = []
+               args = []
                onlydeps = "--onlydeps" in self.myopts
                for x in myfiles:
                        ext = os.path.splitext(x)[1]
@@ -1715,9 +1737,8 @@ class depgraph(object):
                                pkg = Package(type_name="binary", root=myroot,
                                        cpv=mykey, built=True, metadata=metadata,
                                        onlydeps=onlydeps)
-                               if not self.create(pkg, arg=x):
-                                       return 0, myfavorites
-                               arg_atoms.append((x, "="+mykey))
+                               args.append(PackageArg(arg=x, package=pkg,
+                                       root_config=root_config))
                        elif ext==".ebuild":
                                ebuild_path = portage.util.normalize_path(os.path.abspath(x))
                                pkgdir = os.path.dirname(ebuild_path)
@@ -1754,9 +1775,8 @@ class depgraph(object):
                                metadata["USE"] = pkgsettings["USE"]
                                pkg = Package(type_name="ebuild", root=myroot,
                                        cpv=mykey, metadata=metadata, onlydeps=onlydeps)
-                               if not self.create(pkg, arg=x):
-                                       return 0, myfavorites
-                               arg_atoms.append((x, "="+mykey))
+                               args.append(PackageArg(arg=x, package=pkg,
+                                       root_config=root_config))
                        else:
                                if x in ("system", "world"):
                                        x = SETPREFIX + x
@@ -1773,9 +1793,8 @@ class depgraph(object):
                                        expanded_set = InternalPackageSet(
                                                initial_atoms=getSetAtoms(s))
                                        self._sets[s] = expanded_set
-                                       for atom in expanded_set:
-                                               self._set_atoms.add(atom)
-                                               arg_atoms.append((x, atom))
+                                       args.append(SetArg(arg=x, set=expanded_set,
+                                               root_config=root_config))
                                        if not oneshot:
                                                myfavorites.append(x)
                                        continue
@@ -1793,7 +1812,8 @@ class depgraph(object):
                                #   2) It takes away freedom from the resolver to choose other
                                #      possible expansions when necessary.
                                if "/" in x:
-                                       arg_atoms.append((x, x))
+                                       args.append(AtomArg(arg=x, atom=x,
+                                               root_config=root_config))
                                        continue
                                try:
                                        try:
@@ -1814,7 +1834,8 @@ class depgraph(object):
                                                        cp not in e[0]:
                                                        raise
                                                del e
-                                       arg_atoms.append((x, mykey))
+                                       args.append(AtomArg(arg=x, atom=mykey,
+                                               root_config=root_config))
                                except ValueError, e:
                                        if not e.args or not isinstance(e.args[0], list) or \
                                                len(e.args[0]) < 2:
@@ -1831,34 +1852,50 @@ class depgraph(object):
                        # This is currently disabled for sets since greedy SLOT
                        # atoms could be a property of the set itself.
                        greedy_atoms = []
-                       for myarg, atom in arg_atoms:
-                               greedy_atoms.append((myarg, atom))
-                               if myarg.startswith(SETPREFIX):
+                       for arg in args:
+                               greedy_atoms.append(arg)
+                               if not isinstance(arg, (AtomArg, PackageArg)):
                                        continue
-                               for greedy_atom in self._greedy_slot_atoms(myroot, atom):
-                                       greedy_atoms.append((myarg, greedy_atom))
-                       arg_atoms = greedy_atoms
+                               for greedy_atom in self._greedy_slot_atoms(myroot, arg.atom):
+                                       greedy_atoms.append(
+                                               AtomArg(arg=arg.arg, atom=greedy_atom,
+                                                       root_config=root_config))
+                       args = greedy_atoms
+                       del greedy_atoms
 
                # Create the "args" package set from atoms and
-               # packages given as arguments. Normal package
-               # sets have already be processed above.
+               # packages given as arguments.
                args_set = self._sets["args"]
-               for myarg, myatom in arg_atoms:
-                       if myarg.startswith(SETPREFIX):
+               expanded_args = []
+               for arg in args:
+                       if isinstance(arg, SetArg):
+                               for atom in arg.set:
+                                       self._set_atoms.add(atom)
+                                       expanded_args.append(AtomArg(arg=arg.arg, atom=atom,
+                                               root_config=root_config))
                                continue
+                       expanded_args.append(arg)
+                       myatom = arg.atom
                        if myatom in args_set:
                                continue
                        args_set.add(myatom)
                        self._set_atoms.add(myatom)
                        if not oneshot:
                                myfavorites.append(myatom)
+               args = expanded_args
+               del expanded_args
                pprovideddict = pkgsettings.pprovideddict
-               for arg, atom in arg_atoms:
+               for arg in args:
+                               atom = arg.atom
                                try:
                                        pprovided = pprovideddict.get(portage.dep_getkey(atom))
                                        if pprovided and portage.match_from_list(atom, pprovided):
                                                # A provided package has been specified on the command line.
-                                               self._pprovided_args.append((arg, atom))
+                                               self._pprovided_args.append((arg.arg, arg.atom))
+                                               continue
+                                       if isinstance(arg, PackageArg):
+                                               if not self.create(arg.package, arg=arg.arg):
+                                                       return 0, myfavorites
                                                continue
                                        self._populate_filtered_repo(myroot, atom)
                                        pkg, existing_node = self._select_package(
@@ -1868,7 +1905,7 @@ class depgraph(object):
                                                if len(refs) == 1 and "args" in refs:
                                                        self._show_unsatisfied_dep(myroot, atom)
                                                        return 0, myfavorites
-                                               self._missing_args.append((arg, atom))
+                                               self._missing_args.append((arg.arg, arg.atom))
                                                continue
                                        if not self.create(pkg):
                                                sys.stderr.write(("\n\n!!! Problem resolving " + \
index 1917279393a9eb844de246cb478007f3b2bc0131..8eaddfac7e4f2440ce7637576ea0da187b05b5e0 100644 (file)
@@ -1130,8 +1130,10 @@ class config(object):
                                self.puseforce_list.append(cpdict)
                        del rawpuseforce
 
+                       # make.globals should not be relative to config_root
+                       # because it only contains constants.
                        try:
-                               self.mygcfg   = getconfig(os.path.join(config_root, BPREFIX.lstrip(os.path.sep), "etc", "make.globals"))
+                               self.mygcfg   = getconfig(os.path.join(BPREFIX, "etc", "make.globals"))
 
                                if self.mygcfg is None:
                                        self.mygcfg = {}
@@ -2627,6 +2629,13 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero
                                writemsg("openpty failed: '%s'\n" % str(e), noiselevel=1)
                                del e
                                master_fd, slave_fd = os.pipe()
+               if got_pty:
+                       # Disable post-processing of output since otherwise weird
+                       # things like \n -> \r\n transformations may occur.
+                       import termios
+                       mode = termios.tcgetattr(slave_fd)
+                       mode[1] &= ~termios.OPOST
+                       termios.tcsetattr(slave_fd, termios.TCSANOW, mode)
 
                # We must set non-blocking mode before we close the slave_fd
                # since otherwise the fcntl call can fail on FreeBSD (the child
index 7e205d63a791d201db510c28834a282779a65ae9..2d2f27b3039ac405e560ea7d640e35dc700553e7 100644 (file)
@@ -19,6 +19,18 @@ opMapping = {"le": "<=", "lt": "<", "eq": "=", "gt": ">", "ge": ">=",
 NEWLINE_ESCAPE = "!;\\n"       # some random string to mark newlines that should be preserved
 SPACE_ESCAPE = "!;_"           # some random string to mark spaces that should be preserved
 
+def get_applied_glsas(settings):
+       """
+       Return a list of applied or injected GLSA IDs
+       
+       @type   settings: portage.config
+       @param  settings: portage config instance
+       @rtype:         list
+       @return:        list of glsa IDs
+       """
+       return grabfile(os.path.join(os.sep, settings["ROOT"], CACHE_PATH.lstrip(os.sep), "glsa"))
+
+
 # TODO: use the textwrap module instead
 def wrap(text, width, caption=""):
        """
@@ -553,8 +565,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), "glsa"))
-               return (self.nr in aList)
+               return (self.nr in get_applied_glsas())
 
        def inject(self):
                """
@@ -590,4 +601,4 @@ class Glsa:
                                        self.portdbapi, self.vardbapi, minimize=least_change)
                                if update:
                                        rValue.append(update)
-               return ["="+x for x in rValue]
+               return rValue
index b5332699078e21a1db1b78d096ef2112a9d230d7..f93b34ef88fc74d4eccc88c0216a8d6eb4f38582 100644 (file)
@@ -5,8 +5,8 @@
 import os
 import portage.glsa as glsa
 from portage.util import grabfile, write_atomic
-from portage.const import CACHE_PATH
 from portage.sets.base import PackageSet
+from portage.versions import catpkgsplit, pkgcmp
 
 __all__ = ["SecuritySet", "NewGlsaSet", "NewAffectedSet", "AffectedSet"]
 
@@ -21,13 +21,12 @@ class SecuritySet(PackageSet):
                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")
                self._least_change = least_change
 
        def getGlsaList(self, skip_applied):
                glsaindexlist = glsa.get_glsa_list(self._settings)
                if skip_applied:
-                       applied_list = grabfile(self._checkfile)
+                       applied_list = glsa.get_applied_glsas(self._settings)
                        glsaindexlist = set(glsaindexlist).difference(applied_list)
                        glsaindexlist = list(glsaindexlist)
                glsaindexlist.sort()
@@ -40,20 +39,34 @@ class SecuritySet(PackageSet):
                        myglsa = glsa.Glsa(glsaid, self._settings, self._vardbapi, self._portdbapi)
                        #print glsaid, myglsa.isVulnerable(), myglsa.isApplied(), myglsa.getMergeList()
                        if self.useGlsa(myglsa):
-                               atomlist += myglsa.getMergeList(least_change=self._least_change)
-               self._setAtoms(atomlist)
+                               atomlist += ["="+x for x in myglsa.getMergeList(least_change=self._least_change)]
+               self._setAtoms(self._reduce(atomlist))
+       
+       def _reduce(self, atomlist):
+               mydict = {}
+               for atom in atomlist[:]:
+                       cpv = self._portdbapi.match(atom)[0]
+                       slot = self._portdbapi.aux_get(cpv, ["SLOT"])[0]
+                       cps = "/".join(catpkgsplit(cpv)[0:2]) + ":" + slot
+                       if not cps in mydict:
+                               mydict[cps] = (atom, cpv)
+                       else:
+                               other_cpv = mydict[cps][1]
+                               if pkgcmp(catpkgsplit(cpv)[1:], catpkgsplit(other_cpv)[1:]) > 0:
+                                       atomlist.remove(mydict[cps][0])
+                                       mydict[cps] = (atom, cpv)
+               return atomlist
        
        def useGlsa(self, myglsa):
                return True
 
        def updateAppliedList(self):
                glsaindexlist = self.getGlsaList(True)
-               applied_list = grabfile(self._checkfile)
+               applied_list = glsa.get_applied_glsas(self._settings)
                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))
+                       if not myglsa.isVulnerable() and not myglsa.nr in applied_list:
+                               myglsa.inject()
        
        def singleBuilder(cls, options, settings, trees):
                if "use_emerge_resoler" in options \
index 286ad31da04bcbe761a66cd5de23c49e279a59da..ba1b2422fffeaccf9cde8b7f8aed860931b6a615 100644 (file)
@@ -38,6 +38,7 @@ class CommandOutputSet(PackageSet):
                        self._setAtoms(text.split("\n"))
                
        def singleBuilder(self, options, settings, trees):
-               if not command in options:
+               if not "command" in options:
                        raise SetConfigError("no command specified")
                return CommandOutputSet(options["command"])
+       singleBuilder = classmethod(singleBuilder)
index f582723bcbd5bf1fa4835b485c01bce3db6502dd..1ba6e58478952043f576f553903498f55055c36c 100644 (file)
@@ -30,10 +30,11 @@ class SpawnTestCase(TestCase):
                        f = open(logfile, 'r')
                        log_content = f.read()
                        f.close()
-                       # When logging passes through a pty, it's lines will be separated
-                       # by '\r\n', so use splitlines before comparing results.
-                       self.assertEqual(test_string.splitlines(),
-                               log_content.splitlines())
+                       # When logging passes through a pty, this comparison will fail
+                       # unless the oflag terminal attributes have the termios.OPOST
+                       # bit disabled. Otherwise, tranformations such as \n -> \r\n
+                       # may occur.
+                       self.assertEqual(test_string, log_content)
                finally:
                        if logfile:
                                try: