| #!/usr/bin/python -O |
| |
| import sys, os |
| from optparse import OptionParser, OptionValueError |
| |
| import re |
| try: |
| import portage |
| except ImportError: |
| sys.path.insert(0, "/usr/lib/portage/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.okay = [] |
| self.world_file = os.path.join("/", portage_const.WORLD_FILE) |
| self.found = os.access(self.world_file, os.R_OK) |
| |
| for atom in open(self.world_file).read().split(): |
| if not portage.isvalidatom(atom): |
| self.invalid.append(atom) |
| elif not portage.db["/"]["vartree"].dbapi.match(atom): |
| self.not_installed.append(atom) |
| else: |
| 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) |
| 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(self.okay)) |
| 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:]) |