Enable BytesWarnings.
[portage.git] / bin / emaint
index 0e9ba853d8f770d037935886065b84e45eda92c3..c5415c3e3e2418533c018d70d28794f273d225d2 100755 (executable)
-#!/usr/bin/python -O
+#!/usr/bin/python -bbO
+# Copyright 2005-2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
 
-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)
-
-       def _check_world(self, onProgress):
-               categories = set(portage.settings.categories)
-               myroot = portage.settings["ROOT"]
-               vardb = portage.db[myroot]["vartree"].dbapi
-
-               world_atoms = open(self.world_file).read().split()
-               maxval = len(world_atoms)
-               if onProgress:
-                       onProgress(maxval, 0)
-               for i, atom in enumerate(world_atoms):
-                       if not portage.isvalidatom(atom):
-                               self.invalid.append(atom)
-                               if onProgress:
-                                       onProgress(maxval, i+1)
-                               continue
-                       okay = True
-                       if not vardb.match(atom):
-                               self.not_installed.append(atom)
-                               okay = False
-                       if portage.catsplit(atom)[0] not in categories:
-                               self.invalid_category.append(atom)
-                               okay = False
-                       if okay:
-                               self.okay.append(atom)
-                       if onProgress:
-                               onProgress(maxval, i+1)
-
-       def check(self, onProgress=None):
-               self._check_world(onProgress)
-               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, onProgress=None):
-               self._check_world(onProgress)
-               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))
+"""System health checks and maintenance utilities.
+"""
 
+from __future__ import print_function
 
-       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]]]
+import sys
+import errno
+# This block ensures that ^C interrupts are handled quietly.
+try:
+       import signal
 
+       def exithandler(signum, _frame):
+               signal.signal(signal.SIGINT, signal.SIG_IGN)
+               signal.signal(signal.SIGTERM, signal.SIG_IGN)
+               sys.exit(128 + signum)
 
-       if action == "-c/--check":
-               status = "Checking %s for problems"
-               func = "check"
-       else:
-               status = "Attempting to fix %s"
-               func = "fix"
+       signal.signal(signal.SIGINT, exithandler)
+       signal.signal(signal.SIGTERM, exithandler)
+       signal.signal(signal.SIGPIPE, signal.SIG_DFL)
 
+except KeyboardInterrupt:
+       sys.exit(1)
 
-       for task in tasks:
-               print status % task.name()
-               inst = task()
-               result = getattr(inst, func)()
-               if result:
-                       print
-                       print "\n".join(result)
-                       print "\n"
+from os import path as osp
+pym_path = osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym")
+sys.path.insert(0, pym_path)
+import portage
+portage._internal_caller = True
+from portage.emaint.main import emaint_main
 
-       print "Finished"
-
-if __name__ == "__main__":
+try:
        emaint_main(sys.argv[1:])
+except IOError as e:
+       if e.errno == errno.EACCES:
+               print("\nemaint: Need superuser access")
+               sys.exit(1)
+       else:
+               raise