#!/usr/bin/python -O import sys, os from optparse import OptionParser, OptionValueError if not hasattr(__builtins__, "set"): from sets import Set as set import re 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 import portage_const, portage_exception class WorldHandler(object): def name(): return "world" name = staticmethod(name) def __init__(self): self.invalid = [] self.not_installed = [] self.invalid_category = [] self.okay = [] self.world_file = os.path.join("/", portage_const.WORLD_FILE) self.found = os.access(self.world_file, os.R_OK) categories = set(portage.settings.categories) myroot = portage.settings["ROOT"] vardb = portage.db[myroot]["vartree"].dbapi for atom in open(self.world_file).read().split(): if not portage.isvalidatom(atom): self.invalid.append(atom) continue cp = portage.dep_getkey(atom) okay = True if not vardb.match(atom): self.not_installed.append(atom) okay = False if portage.catsplit(cp)[0] not in categories: self.invalid_category.append(atom) okay = False if okay: self.okay.append(atom) def check(self): errors = [] if self.found: errors += map(lambda x: "'%s' is not a valid atom" % x, self.invalid) errors += map(lambda x: "'%s' is not installed" % x, self.not_installed) errors += map(lambda x: "'%s' has a category that is not listed in /etc/portage/categories" % x, self.invalid_category) else: errors.append(self.world_file + " could not be opened for reading") return errors def fix(self): errors = [] try: portage.write_atomic(self.world_file, "\n".join(sorted(self.okay)) + "\n") except portage_exception.PortageException: errors.append(self.world_file + " could not be opened for writing") return errors class VdbKeyHandler(object): def name(): return "vdbkeys" name = staticmethod(name) def __init__(self): self.list = portage.db["/"]["vartree"].dbapi.cpv_all() self.missing = [] self.keys = ["HOMEPAGE", "SRC_URI", "KEYWORDS", "DESCRIPTION"] for p in self.list: mydir = os.path.join(os.sep, portage.settings["ROOT"], portage_const.VDB_PATH, p)+os.sep ismissing = True for k in self.keys: if os.path.exists(mydir+k): ismissing = False break if ismissing: self.missing.append(p) def check(self): return ["%s has missing keys" % x for x in self.missing] def fix(self): errors = [] for p in self.missing: mydir = os.path.join(os.sep, portage.settings["ROOT"], portage_const.VDB_PATH, p)+os.sep if not os.access(mydir+"environment.bz2", os.R_OK): errors.append("Can't access %s" % (mydir+"environment.bz2")) elif not os.access(mydir, os.W_OK): errors.append("Can't create files in %s" % mydir) else: env = os.popen("bzip2 -dcq "+mydir+"environment.bz2", "r") envlines = env.read().split("\n") env.close() for k in self.keys: s = [l for l in envlines if l.startswith(k+"=")] if len(s) > 1: errors.append("multiple matches for %s found in %senvironment.bz2" % (k, mydir)) elif len(s) == 0: s = "" else: s = s[0].split("=",1)[1] s = s.lstrip("$").strip("\'\"") s = re.sub("(\\\\[nrt])+", " ", s) s = " ".join(s.split()).strip() if s != "": try: keyfile = open(mydir+os.sep+k, "w") keyfile.write(s+"\n") keyfile.close() except (IOError, OSError), e: errors.append("Could not write %s, reason was: %s" % (mydir+k, e)) return errors def emaint_main(myargv): # TODO: Create a system that allows external modules to be added without # the need for hard coding. modules = {"world" : WorldHandler} module_names = modules.keys() module_names.sort() module_names.insert(0, "all") def exclusive(option, *args, **kw): var = kw.get("var", None) if var is None: raise ValueError("var not specified to exclusive()") if getattr(parser, var, ""): raise OptionValueError("%s and %s are exclusive options" % (getattr(parser, var), option)) setattr(parser, var, str(option)) usage = "usage: emaint [options] " + " | ".join(module_names) usage+= "\n\nCurrently emaint can only check and fix problems with one's world\n" usage+= "file. Future versions will integrate other portage check-and-fix\n" usage+= "tools and provide a single interface to system health checks." parser = OptionParser(usage=usage) parser.add_option("-c", "--check", help="check for problems", action="callback", callback=exclusive, callback_kwargs={"var":"action"}) parser.add_option("-f", "--fix", help="attempt to fix problems", action="callback", callback=exclusive, callback_kwargs={"var":"action"}) parser.action = None (options, args) = parser.parse_args(args=myargv) if len(args) != 1: parser.error("Incorrect number of arguments") if args[0] not in module_names: parser.error("%s target is not a known target" % args[0]) if parser.action: action = parser.action else: print "Defaulting to --check" action = "-c/--check" if args[0] == "all": tasks = modules.values() else: tasks = [modules[args[0]]] if action == "-c/--check": status = "Checking %s for problems" func = "check" else: status = "Attempting to fix %s" func = "fix" for task in tasks: print status % task.name() inst = task() result = getattr(inst, func)() if result: print print "\n".join(result) print "\n" print "Finished" if __name__ == "__main__": emaint_main(sys.argv[1:])