3 import sys, os, time, signal
4 from optparse import OptionParser, OptionValueError
5 if not hasattr(__builtins__, "set"):
6 from sets import Set as set
11 from os import path as osp
12 sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
15 import portage.const, portage.exception, portage.output
16 class WorldHandler(object):
20 name = staticmethod(name)
24 self.not_installed = []
25 self.invalid_category = []
27 self.world_file = os.path.join("/", portage.const.WORLD_FILE)
28 self.found = os.access(self.world_file, os.R_OK)
30 def _check_world(self, onProgress):
31 categories = set(portage.settings.categories)
32 myroot = portage.settings["ROOT"]
33 vardb = portage.db[myroot]["vartree"].dbapi
35 world_atoms = open(self.world_file).read().split()
36 maxval = len(world_atoms)
39 for i, atom in enumerate(world_atoms):
40 if not portage.isvalidatom(atom):
41 self.invalid.append(atom)
43 onProgress(maxval, i+1)
46 if not vardb.match(atom):
47 self.not_installed.append(atom)
49 if portage.catsplit(atom)[0] not in categories:
50 self.invalid_category.append(atom)
53 self.okay.append(atom)
55 onProgress(maxval, i+1)
57 def check(self, onProgress=None):
58 self._check_world(onProgress)
61 errors += map(lambda x: "'%s' is not a valid atom" % x, self.invalid)
62 errors += map(lambda x: "'%s' is not installed" % x, self.not_installed)
63 errors += map(lambda x: "'%s' has a category that is not listed in /etc/portage/categories" % x, self.invalid_category)
65 errors.append(self.world_file + " could not be opened for reading")
68 def fix(self, onProgress=None):
69 self._check_world(onProgress)
72 portage.write_atomic(self.world_file, "\n".join(self.okay))
73 except portage.exception.PortageException:
74 errors.append(self.world_file + " could not be opened for writing")
77 class VdbKeyHandler(object):
80 name = staticmethod(name)
83 self.list = portage.db["/"]["vartree"].dbapi.cpv_all()
85 self.keys = ["HOMEPAGE", "SRC_URI", "KEYWORDS", "DESCRIPTION"]
88 mydir = os.path.join(os.sep, portage.settings["ROOT"], portage.const.VDB_PATH, p)+os.sep
91 if os.path.exists(mydir+k):
95 self.missing.append(p)
98 return ["%s has missing keys" % x for x in self.missing]
104 for p in self.missing:
105 mydir = os.path.join(os.sep, portage.settings["ROOT"], portage.const.VDB_PATH, p)+os.sep
106 if not os.access(mydir+"environment.bz2", os.R_OK):
107 errors.append("Can't access %s" % (mydir+"environment.bz2"))
108 elif not os.access(mydir, os.W_OK):
109 errors.append("Can't create files in %s" % mydir)
111 env = os.popen("bzip2 -dcq "+mydir+"environment.bz2", "r")
112 envlines = env.read().split("\n")
115 s = [l for l in envlines if l.startswith(k+"=")]
117 errors.append("multiple matches for %s found in %senvironment.bz2" % (k, mydir))
121 s = s[0].split("=",1)[1]
122 s = s.lstrip("$").strip("\'\"")
123 s = re.sub("(\\\\[nrt])+", " ", s)
124 s = " ".join(s.split()).strip()
127 keyfile = open(mydir+os.sep+k, "w")
128 keyfile.write(s+"\n")
130 except (IOError, OSError), e:
131 errors.append("Could not write %s, reason was: %s" % (mydir+k, e))
135 class ProgressHandler(object):
140 self.min_display_latency = 0.2
142 def onProgress(self, maxval, curval):
145 cur_time = time.time()
146 if cur_time - self.last_update >= self.min_display_latency:
147 self.last_update = cur_time
151 raise NotImplementedError(self)
153 def emaint_main(myargv):
155 # TODO: Create a system that allows external modules to be added without
156 # the need for hard coding.
157 modules = {"world" : WorldHandler}
159 module_names = modules.keys()
161 module_names.insert(0, "all")
163 def exclusive(option, *args, **kw):
164 var = kw.get("var", None)
166 raise ValueError("var not specified to exclusive()")
167 if getattr(parser, var, ""):
168 raise OptionValueError("%s and %s are exclusive options" % (getattr(parser, var), option))
169 setattr(parser, var, str(option))
172 usage = "usage: emaint [options] " + " | ".join(module_names)
174 usage+= "\n\nCurrently emaint can only check and fix problems with one's world\n"
175 usage+= "file. Future versions will integrate other portage check-and-fix\n"
176 usage+= "tools and provide a single interface to system health checks."
179 parser = OptionParser(usage=usage)
180 parser.add_option("-c", "--check", help="check for problems",
181 action="callback", callback=exclusive, callback_kwargs={"var":"action"})
182 parser.add_option("-f", "--fix", help="attempt to fix problems",
183 action="callback", callback=exclusive, callback_kwargs={"var":"action"})
187 (options, args) = parser.parse_args(args=myargv)
189 parser.error("Incorrect number of arguments")
190 if args[0] not in module_names:
191 parser.error("%s target is not a known target" % args[0])
194 action = parser.action
196 print "Defaulting to --check"
197 action = "-c/--check"
200 tasks = modules.values()
202 tasks = [modules[args[0]]]
205 if action == "-c/--check":
206 status = "Checking %s for problems"
209 status = "Attempting to fix %s"
212 isatty = sys.stdout.isatty()
214 print status % task.name()
218 progressBar = portage.output.TermProgressBar()
219 progressHandler = ProgressHandler()
221 progressBar.set(progressHandler.curval, progressHandler.maxval)
222 progressHandler.display = display
223 def sigwinch_handler(signum, frame):
224 lines, progressBar.term_columns = \
225 portage.output.get_term_size()
226 signal.signal(signal.SIGWINCH, sigwinch_handler)
227 result = getattr(inst, func)(onProgress=progressHandler.onProgress)
229 # make sure the final progress is displayed
230 progressHandler.display()
232 signal.signal(signal.SIGWINCH, signal.SIG_DFL)
235 print "\n".join(result)
240 if __name__ == "__main__":
241 emaint_main(sys.argv[1:])