# $Header: $
# This program is licensed under the GPL, version 2
-import os
import sys
+import os
import codecs
-try:
- import portage
-except ImportError:
- sys.path.insert(0, "/usr/lib/portage/pym")
- import portage
+from functools import reduce
-try:
- from portage.output import *
-except ImportError:
- from output import *
+import portage
+from portage.output import *
from getopt import getopt, GetoptError
__program__ = "glsa-check"
__author__ = "Marius Mauch <genone@gentoo.org>"
-__version__ = open("/usr/share/gentoolkit/VERSION").read().strip()
+__version__ = "svn"
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)"],
+["-l", "--list", "list the GLSAs"],
+["-d", "--dump", "--print", "show all information about the GLSAs"],
+["-t", "--test", "test if this system is affected by the GLSAs"],
+["-p", "--pretend", "show the necessary steps to apply the GLSAs"],
+["-f", "--fix", "try to auto-apply the GLSAs (experimental)"],
["-i", "--inject", "inject the given GLSA into the glsa_injected file"],
["-n", "--nocolor", "disable colors (option)"],
-["-e", "--emergelike", "do not use a least-change algorithm (option)"],
+["-e", "--emergelike", "upgrade to latest version (not least-change, option)"],
["-h", "--help", "show this help message"],
["-V", "--version", "some information about this tool"],
["-v", "--verbose", "print more information (option)"],
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])])
- args = [a for a,b in args]
-
+ 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:
if option in args:
list_cve = True
args.remove(option)
-
+
least_change = True
for option in ["--emergelike", "-e"]:
if option in args:
if args in [o for o in m[:-1]]:
mode = m[1][2:]
-except GetoptError, e:
+except GetoptError as e:
sys.stderr.write("unknown option given: ")
sys.stderr.write(str(e)+"\n")
mode = "HELP"
sys.stderr.write("(specify \"all\" as parameter)\n\n")
mode = "HELP"
elif len(params) <= 0 and mode == "list":
- params.append("new")
-
+ params.append("affected")
+
# show help message
if mode == "help" or mode == "HELP":
msg = "Syntax: glsa-check <option> [glsa-list]\n\n"
msg += "\t" + o + "\n"
msg += "\nglsa-list can contain an arbitrary number of GLSA ids, \n"
msg += "filenames containing GLSAs or the special identifiers \n"
- msg += "'all', 'new' and 'affected'\n"
+ msg += "'all' and 'affected'\n"
if mode == "help":
sys.stdout.write(msg)
sys.exit(0)
if quiet:
glsaconfig["EMERGE_OPTS"] += " --quiet"
-vardb = portage.db["/"]["vartree"].dbapi
-portdb = portage.db["/"]["porttree"].dbapi
+vardb = portage.db[portage.root]["vartree"].dbapi
+portdb = portage.db[portage.root]["porttree"].dbapi
# Check that we really have a glsa dir to work on
if not (os.path.exists(glsaconfig["GLSA_DIR"]) and os.path.isdir(glsaconfig["GLSA_DIR"])):
glsalist = []
if "new" in params:
- glsalist = todolist
params.remove("new")
-
+ sys.stderr.write("Warning: The 'new' glsa-list target has been removed, using 'affected'.\n")
+ params.append("affected")
+
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, glsaconfig)
- except (GlsaTypeException, GlsaFormatException), e:
+ except (GlsaTypeException, GlsaFormatException) as e:
if verbose:
sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (x, e)))
continue
glsalist.extend([g for g in params if g not in glsalist])
def summarylist(myglsalist, fd1=sys.stdout, fd2=sys.stderr, encoding="utf-8"):
+ # Get to the raw streams in py3k before wrapping them with an encoded writer
+ # to avoid writing bytes to a text stream (stdout/stderr are text streams
+ # by default in py3k)
+ if hasattr(fd1, "buffer"):
+ fd1 = fd1.buffer
+ if hasattr(fd2, "buffer"):
+ fd2 = fd2.buffer
fd1 = codecs.getwriter(encoding)(fd1)
fd2 = codecs.getwriter(encoding)(fd2)
if not quiet:
for myid in myglsalist:
try:
myglsa = Glsa(myid, glsaconfig)
- except (GlsaTypeException, GlsaFormatException), e:
+ except (GlsaTypeException, GlsaFormatException) as e:
if verbose:
fd2.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
continue
if verbose:
access = ("[%-8s] " % myglsa.access)
else:
- access=""
+ access = ""
fd1.write(color(myglsa.nr) + " " + color(status) + " " + color(access) + myglsa.title + " (")
if not verbose:
- for pkg in myglsa.packages.keys()[:3]:
+ for pkg in list(myglsa.packages.keys())[:3]:
fd1.write(" " + pkg + " ")
if len(myglsa.packages) > 3:
fd1.write("... ")
for myid in glsalist:
try:
myglsa = Glsa(myid, glsaconfig)
- except (GlsaTypeException, GlsaFormatException), e:
+ except (GlsaTypeException, GlsaFormatException) as 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 GLSA "+myid+"\n")
+ if not quiet:
+ sys.stdout.write("Fixing GLSA "+myid+"\n")
if not myglsa.isVulnerable():
- sys.stdout.write(">>> no vulnerable packages installed\n")
+ if not quiet:
+ sys.stdout.write(">>> no vulnerable packages installed\n")
else:
+ if quiet:
+ sys.stdout.write("Fixing GLSA "+myid+"\n")
mergelist = myglsa.getMergeList(least_change=least_change)
if mergelist == []:
sys.stdout.write(">>> cannot fix GLSA, no unaffected packages available\n")
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:
+ if exitcode >= 1 << 8:
exitcode >>= 8
if exitcode:
sys.exit(exitcode)
if len(mergelist):
sys.stdout.write("\n")
elif mode == "pretend":
- sys.stdout.write("Checking GLSA "+myid+"\n")
+ if not quiet:
+ sys.stdout.write("Checking GLSA "+myid+"\n")
if not myglsa.isVulnerable():
- sys.stdout.write(">>> no vulnerable packages installed\n")
+ if not quiet:
+ sys.stdout.write(">>> no vulnerable packages installed\n")
else:
+ if quiet:
+ sys.stdout.write("Checking GLSA "+myid+"\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")
+
+ # first, extract the atoms that cannot be upgraded (where key == "")
+ no_upgrades = []
if "" in mergedict:
- sys.stdout.write("\n>>> For the following packages, no upgrade path exists:\n")
- sys.stdout.write(" " + ", ".join(mergedict[""]))
+ no_upgrades = mergedict[""]
+ del mergedict[""]
+
+ # see if anything is left that can be upgraded
+ if mergedict:
+ sys.stdout.write(">>> Updates that will be performed:\n")
+ for (upd, vuln) in mergedict.items():
+ sys.stdout.write(" " + green(upd) + " (vulnerable: " + red(", ".join(vuln)) + ")\n")
+
+ if no_upgrades:
+ sys.stdout.write(">>> No upgrade path exists for these packages:\n")
+ sys.stdout.write(" " + red(", ".join(no_upgrades)) + "\n")
elif mode == "inject":
sys.stdout.write("injecting " + myid + "\n")
myglsa.inject()
for myid in glsalist:
try:
myglsa = Glsa(myid, glsaconfig)
- except (GlsaTypeException, GlsaFormatException), e:
+ except (GlsaTypeException, GlsaFormatException) as e:
if verbose:
sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
continue
import portage.mail as portage_mail
except ImportError:
import portage_mail
-
+
import socket
- from StringIO import StringIO
+ from io import BytesIO
try:
from email.mime.text import MIMEText
except ImportError:
myrecipient = glsaconfig["PORTAGE_ELOG_MAILURI"].split()[0]
else:
myrecipient = "root@localhost"
-
+
if "PORTAGE_ELOG_MAILFROM" in glsaconfig:
myfrom = glsaconfig["PORTAGE_ELOG_MAILFROM"]
else:
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))
+ myfd = BytesIO()
+ line = "GLSA Summary report for host %s\n" % socket.getfqdn()
+ myfd.write(line.encode("utf-8"))
+ line = "(Command was: %s)\n\n" % " ".join(sys.argv)
+ myfd.write(line.encode("utf-8"))
summarylist(glsalist, fd1=myfd, fd2=myfd)
- summary = str(myfd.getvalue())
+ summary = myfd.getvalue().decode("utf-8")
myfd.close()
myattachments = []
for myid in glsalist:
try:
myglsa = Glsa(myid, glsaconfig)
- except (GlsaTypeException, GlsaFormatException), e:
+ except (GlsaTypeException, GlsaFormatException) as e:
if verbose:
sys.stderr.write(("invalid GLSA: %s (error message was: %s)\n" % (myid, e)))
continue
- myfd = StringIO()
+ myfd = BytesIO()
myglsa.dump(outstream=myfd)
- myattachments.append(MIMEText(str(myfd.getvalue()), _charset="utf8"))
+ attachment = myfd.getvalue().decode("utf-8")
+ myattachments.append(MIMEText(attachment, _charset="utf8"))
myfd.close()
- if glsalist or not quiet:
+ if glsalist or not quiet:
mymessage = portage_mail.create_message(myfrom, myrecipient, mysubject, summary, myattachments)
portage_mail.send_mail(glsaconfig, 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)