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")
+ myglsalist.sort()
for myid in myglsalist:
try:
myglsa = Glsa(myid, portage.settings, vardb, portdb)
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))
+ 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")
+ if verbose:
+ summarylist(outputlist)
+ else:
+ 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)
isn't stricly required, but it should always be used as the default
handler might be changed in future versions.</para></footnote>.
That option defines which handler class should be used to
- create the set. Other universal options available for single sets are
- <varname>name</varname> (which is usually not needed as the name
+ create the set. Other universal options available for single sets are:
+ <itemizedlist>
+ <listitem><varname>name</varname> (which is usually not needed as the name
of the set is generated from the section name if <varname>name</varname>
- is missing) and <varname>world-candidate</varname>, which determines if
- given package should be added to the <varname>world</varname> set. Some
- handler classes might require additional options for their configuration,
+ is missing)</listitem>
+ <listitem><varname>world-candidate</varname>, which determines if
+ given package should be added to the <varname>world</varname> set</listitem>
+ <listitem><varname>extend</varname> to include the contents of other package sets
+ </listitem>
+ <listitem><varname>remove</varname> to remove the contents of other package sets
+ </listitem>
+ <listitem><varname>intersect</varname> to only include packages that are also
+ included in one or more other package sets</listitem>
+ </itemizedlist>
+ Some handler classes might require additional options for their configuration,
these will be covered later in this chapter.
</para>
<para>
sets each section still requires the <varname>class</varname> option,
but to indicate that the section should generate multiple sets it's
also necessary to set the <varname>multiset</varname> option to
- <parameter>true</parameter>. The <varname>world-candidate</varname>
- option is also supported like with single sets.
+ <parameter>true</parameter>. The <varname>world-candidate</varname>,
+ <varname>extend</varname>, <varname>remove</varname> and
+ <varname>intersect</varname> options are also supported like with
+ single sets (they'll apply to all sets generated by the section).
</para>
<para>
As it doesn't make much sense to specify a single name for multiple sets
</sect3>
</sect2>
+ <sect2 id='config-set-classes-AgeSet'>
+ <title>portage.sets.dbapi.AgeSet</title>
+ <para>
+ Package sets created by this class will include installed packages that
+ have been installed before / after a given date.
+ </para>
+
+ <sect3>
+ <title>Single Set Configuration</title>
+ <para>
+ In single set configurations this class supports the following options:
+ <itemizedlist>
+ <listitem><varname>age</varname>: Optional, defaults to 7. Specifies
+ the number of days passed since installation to use as cut-off point.
+ </listitem>
+ <listitem><varname>mode</varname>: Optional, defaults to "older". Must
+ be either "older" or "newer" to select packages installed either
+ before resp. after the cut-off-date given by <varname>age</varname>.
+ E.g. the defaults will select all installed packages that have been
+ installed more than one week ago.
+ </listitem>
+ </itemizedlist>
+ </para>
+ </sect3>
+ </sect2>
+
<sect2 id='config-set-classes-CategorySet'>
<title>portage.sets.dbapi.CategorySet</title>
<para>
retval = os.EX_OK
setconfig = root_config.setconfig
- # display errors that occured while loading the SetConfig instance
- for e in setconfig.errors:
- print colorize("BAD", "Error during set creation: %s" % e)
-
sets = setconfig.getSets()
- # emerge relies on the existance of sets with names "world" and "system"
- required_sets = ("world", "system")
-
- for s in required_sets:
- if s not in sets:
- msg = ["emerge: incomplete set configuration, " + \
- "no \"%s\" set defined" % s]
- msg.append(" sets defined: %s" % ", ".join(sets))
- for line in msg:
- sys.stderr.write(line + "\n")
- retval = 1
- unmerge_actions = ("unmerge", "prune", "clean", "depclean")
-
# In order to know exactly which atoms/sets should be added to the
# world file, the depgraph performs set expansion later. It will get
# confused about where the atoms came from if it's not allowed to
myfiles = newargs
del newargs
newargs = []
-
+
+ # separators for set arguments
+ ARG_START = "{"
+ ARG_END = "}"
+
# WARNING: all operators must be of equal length
IS_OPERATOR = "/@"
DIFF_OPERATOR = "-@"
UNION_OPERATOR = "+@"
+ for i in range(0, len(myfiles)):
+ if myfiles[i].startswith(SETPREFIX):
+ start = 0
+ end = 0
+ x = myfiles[i][len(SETPREFIX):]
+ newset = ""
+ while x:
+ start = x.find(ARG_START)
+ end = x.find(ARG_END)
+ if start > 0 and start < end:
+ namepart = x[:start]
+ argpart = x[start+1:end]
+
+ # TODO: implement proper quoting
+ args = argpart.split(",")
+ options = {}
+ for a in args:
+ if "=" in a:
+ k, v = a.split("=", 1)
+ options[k] = v
+ else:
+ options[a] = "True"
+ setconfig.update(namepart, options)
+ newset += (x[:start-len(namepart)]+namepart)
+ x = x[end+len(ARG_END):]
+ else:
+ newset += x
+ x = ""
+ myfiles[i] = SETPREFIX+newset
+
+ sets = setconfig.getSets()
+
+ # display errors that occured while loading the SetConfig instance
+ for e in setconfig.errors:
+ print colorize("BAD", "Error during set creation: %s" % e)
+
+ # emerge relies on the existance of sets with names "world" and "system"
+ required_sets = ("world", "system")
+
+ for s in required_sets:
+ if s not in sets:
+ msg = ["emerge: incomplete set configuration, " + \
+ "no \"%s\" set defined" % s]
+ msg.append(" sets defined: %s" % ", ".join(sets))
+ for line in msg:
+ sys.stderr.write(line + "\n")
+ retval = 1
+ unmerge_actions = ("unmerge", "prune", "clean", "depclean")
+
for a in myfiles:
if a.startswith(SETPREFIX):
# support simple set operations (intersection, difference and union)
rValue = opMapping[versionNode.getAttribute("range")] \
+ pkgname \
+ "-" + getText(versionNode, format="strip")
+ if "slot" in versionNode.attributes and versionNode.getAttribute("slot") != "*":
+ rValue += ":"+versionNode.getAttribute("slot")
return str(rValue)
def makeVersion(versionNode):
@rtype: String
@return: the version string
"""
- return opMapping[versionNode.getAttribute("range")] \
+ rValue = opMapping[versionNode.getAttribute("range")] \
+ getText(versionNode, format="strip")
+ if "slot" in versionNode.attributes and versionNode.getAttribute("slot") != "*":
+ rValue += ":"+versionNode.getAttribute("slot")
+ return rValue
def match(atom, dbapi, match_type="default"):
"""
@return: a list with the matching versions
"""
if match_type == "default" or not hasattr(dbapi, "xmatch"):
- mylist = dbapi.match(re.sub("-r[0-9]+$", "", revisionAtom[2:]))
+ mylist = dbapi.match(re.sub(r'-r[0-9]+(:[^ ]+)?$', r'\1', revisionAtom[2:]))
else:
- mylist = dbapi.xmatch(match_type, re.sub("-r[0-9]+$", "", revisionAtom[2:]))
+ mylist = dbapi.xmatch(match_type, re.sub(r'-r[0-9]+(:[^ ]+)?$', r'\1', revisionAtom[2:]))
rValue = []
for v in mylist:
r1 = pkgsplit(v)[-1][1:]
rValue += "-"+c_pv[3]
return rValue
+def format_date(datestr):
+ """
+ Takes a date (announced, revised) date from a GLSA and formats
+ it as readable text (i.e. "January 1, 2008").
+
+ @type date: String
+ @param date: the date string to reformat
+ @rtype: String
+ @return: a reformatted string, or the original string
+ if it cannot be reformatted.
+ """
+ splitdate = datestr.split("-", 2)
+ if len(splitdate) != 3:
+ return datestr
+
+ # This cannot raise an error as we use () instead of []
+ splitdate = (int(x) for x in splitdate)
+
+ from datetime import date
+ try:
+ d = date(*splitdate)
+ except ValueError:
+ return datestr
+
+ # TODO We could format to local date format '%x' here?
+ return d.strftime("%B %d, %Y")
# simple Exception classes to catch specific errors
class GlsaTypeException(Exception):
self.DOM = xml.dom.minidom.parse(myfile)
if not self.DOM.doctype:
raise GlsaTypeException(None)
- elif self.DOM.doctype.systemId != "http://www.gentoo.org/dtd/glsa.dtd":
+ elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa.dtd":
+ self.dtdversion = 0
+ elif self.DOM.doctype.systemId == "http://www.gentoo.org/dtd/glsa-2.dtd":
+ self.dtdversion = 2
+ else:
raise GlsaTypeException(self.DOM.doctype.systemId)
myroot = self.DOM.getElementsByTagName("glsa")[0]
if self.type == "id" and myroot.getAttribute("id") != self.nr:
# the simple (single, required, top-level, #PCDATA) tags first
self.title = getText(myroot.getElementsByTagName("title")[0], format="strip")
self.synopsis = getText(myroot.getElementsByTagName("synopsis")[0], format="strip")
- self.announced = getText(myroot.getElementsByTagName("announced")[0], format="strip")
- self.revised = getText(myroot.getElementsByTagName("revised")[0], format="strip")
+ self.announced = format_date(getText(myroot.getElementsByTagName("announced")[0], format="strip"))
+
+ count = 1
+ # Support both formats of revised:
+ # <revised>December 30, 2007: 02</revised>
+ # <revised count="2">2007-12-30</revised>
+ revisedEl = myroot.getElementsByTagName("revised")[0]
+ self.revised = getText(revisedEl, format="strip")
+ if (revisedEl.attributes.has_key("count")):
+ count = revisedEl.getAttribute("count")
+ elif (self.revised.find(":") >= 0):
+ (self.revised, count) = self.revised.split(":")
+
+ self.revised = format_date(self.revised)
+
+ try:
+ self.count = int(count)
+ except ValueError:
+ # TODO should this rais a GlsaFormatException?
+ self.count = 1
# now the optional and 0-n toplevel, #PCDATA tags and references
try:
outstream.write((width*"=")+"\n")
outstream.write(wrap(self.synopsis, width, caption="Synopsis: ")+"\n")
outstream.write("Announced on: %s\n" % self.announced)
- outstream.write("Last revised on: %s\n\n" % self.revised)
+ outstream.write("Last revised on: %s : %02d\n\n" % (self.revised, self.count))
if self.glsatype == "ebuild":
for k in self.packages.keys():
pkg = self.packages[k]
@rtype: Boolean
@returns: True if the GLSA was applied, False if not
"""
- return (self.nr in get_applied_glsas())
+ return (self.nr in get_applied_glsas(self.config))
def inject(self):
"""
self._parsed = False
self.active = []
- def _parse(self):
- if self._parsed:
+ def update(self, setname, options):
+ self.errors = []
+ if not setname in self.psets:
+ options["name"] = setname
+
+ # for the unlikely case that there is already a section with the requested setname
+ import random
+ while setname in self.sections():
+ setname = "%08d" % random.randint(0, 10**10)
+
+ self.add_section(setname)
+ for k, v in options.items():
+ self.set(setname, k, v)
+ else:
+ section = self.psets[setname].creator
+ if self.has_option(section, "multiset") and self.getboolean(section, "multiset"):
+ self.errors.append("Invalid request to reconfigure set '%s' generated by multiset section '%s'" % (setname, section))
+ return
+ for k, v in options.items():
+ self.set(section, k, v)
+ self._parse(update=True)
+
+ def _parse(self, update=False):
+ if self._parsed and not update:
return
for sname in self.sections():
# find classname for current section, default to file based sets
self.errors.append("Configuration error in section '%s': %s" % (sname, str(e)))
continue
for x in newsets:
- if x in self.psets:
+ if x in self.psets and not update:
self.errors.append("Redefinition of set '%s' (sections: '%s', '%s')" % (x, self.psets[x].creator, sname))
newsets[x].creator = sname
if self.has_option(sname, "world-candidate") and not self.getboolean(sname, "world-candidate"):
setname = self.get(sname, "name")
except NoOptionError:
setname = sname
- if setname in self.psets:
+ if setname in self.psets and not update:
self.errors.append("Redefinition of set '%s' (sections: '%s', '%s')" % (setname, self.psets[setname].creator, sname))
if hasattr(setclass, "singleBuilder"):
try:
def getSetAtoms(self, setname, ignorelist=None):
myset = self.getSets()[setname]
myatoms = myset.getAtoms()
+
+ extend = set()
+ remove = set()
+ intersect = set()
+
if ignorelist is None:
ignorelist = set()
+ if not setname in ignorelist:
+ if self.has_option(myset.creator, "extend"):
+ extend.update(self.get(myset.creator, "extend").split())
+ if self.has_option(myset.creator, "remove"):
+ remove.update(self.get(myset.creator, "remove").split())
+ if self.has_option(myset.creator, "intersect"):
+ intersect.update(self.get(myset.creator, "intersect").split())
+
ignorelist.add(setname)
for n in myset.getNonAtoms():
- if n[0] == SETPREFIX and n[1:] in self.psets:
- if n[1:] not in ignorelist:
- myatoms.update(self.getSetAtoms(n[1:],
- ignorelist=ignorelist))
+ if n.startswith(SETPREFIX) and n[len(SETPREFIX):] in self.psets:
+ extend.add(n[len(SETPREFIX):])
+
+ for s in ignorelist:
+ extend.discard(s)
+ remove.discard(s)
+ intersect.discard(s)
+
+ for s in extend:
+ myatoms.update(self.getSetAtoms(s, ignorelist=ignorelist))
+ for s in remove:
+ myatoms.difference_update(self.getSetAtoms(s, ignorelist=ignorelist))
+ for s in intersect:
+ myatoms.intersection_update(self.getSetAtoms(s, ignorelist=ignorelist))
+
return myatoms
def load_default_config(settings, trees):