summaryrefslogtreecommitdiffstats
path: root/bin/emaint
blob: 2054f42c715507a48e9dc8432b24a19fc6e845cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/python -O

import sys
from copy import copy
from optparse import OptionParser, OptionValueError



import os, portage, portage_const
class WorldHandler(object):

	def name():
		return "world"
	name = staticmethod(name)

	def __init__(self):
		self.invalid = []
		self.not_installed = []
		self.masked = []
		self.unavailable = []
		self.okay = []
		self.found = os.access(portage_const.WORLD_FILE, os.R_OK)

		for atom in open(portage_const.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)
			elif not portage.db["/"]["porttree"].dbapi.xmatch("match-all", atom):
				self.unavailable.append(atom)
			elif not portage.db["/"]["porttree"].dbapi.match("match-visible", atom):
				self.masked.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)
			errors += map(lambda x: "'%s' is masked (manually fix by adding to package.keywords)" % x, self.masked)
			errors += map(lambda x: "'%s' has no ebuilds available" % x, self.unavailable)
			if self.masked:
				errors += "Masked ebuilds can be manually unmasked with package.keywords or package.unmask"
		else:
			errors.append(portage_const.WORLD_FILE + " could not be opened for reading")
		return errors

	def fix(self):
		errors = []
		try:
			open(portage_const.WORLD_FILE, "w").write("\n".join(self.okay))
		except OSError:
			errors.append(portage_const.WORLD_FILE + " could not be opened for writing")
		return errors


modules = {"world" : WorldHandler}


module_names = modules.keys()
module_names.sort()
module_names.insert(0, "all")


def exclusive(option, unused1, unused2, unused3, var=None):
	if not var:
		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()
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"