/
cfloader.py
117 lines (109 loc) · 3.89 KB
/
cfloader.py
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
#
# Load our configuration file.
#
# Note that we do not validate the existence of objects here: we don't
# check to see if files or users exist.
import readcf
import util
class BadInput(Exception):
pass
# Unlike rule and action files, our overall configuration file is completely
# interdependant. As a result, we hang all our parsing off a class so we can
# do lookups easily.
class NannyConfig:
def __init__(self):
self.cf = {}
# Initialized here so we can always just .append() to it later.
self.cf['listen'] = []
# __str__'s job is vastly complicated by listen.
def __str__(self):
a = []
ks = self.cf.keys()
ks.sort()
for k in ks:
if k in ('dropipafter', 'expireevery'):
a.append("%s %ss" % (k, self.cf[k]))
elif k != 'listen':
a.append("%s %s" % (k, self.cf[k]))
self.cf['listen'].sort()
for h, p in self.cf['listen']:
a.append('listen %s@%s' % (p, h))
return "\n".join(a) + "\n"
def __getitem__(self, name):
return self.cf[name]
def has_key(self, name):
return self.cf.has_key(name)
def __contains__(self, name):
return name in self.cf
def parseline(self, line, lineno):
__pychecker__ = 'no-argsused'
n = line.split()
# Everything we have is of the form 'directive argument',
# so we can be really simple:
if len(n) != 2:
raise BadInput, "badly formatted line"
# Every directive except 'listen' can only be given once,
# so we can do this checking very easily.
if n[0] != "listen" and self.cf.has_key(n[0]):
raise BadInput, "can only give one %s directive" % \
(n[0],)
# These three do no contents-checking: they just store it.
if n[0] in ('rulefile', 'actionfile', 'user', 'aftermaxthreads'):
self.cf[n[0]] = n[1]
# I really need a better name for this.
elif n[0] == 'dropipafter':
self.cf[n[0]] = util.getsecs_or_raise(n[1], BadInput)
elif n[0] == 'expireevery':
self.cf[n[0]] = util.getsecs_or_raise(n[1], BadInput)
elif n[0] == 'maxthreads':
self.cf[n[0]] = util.int_or_raise(n[1], BadInput)
elif n[0] == 'listen':
# Listen stores host/port pairs in a list, since
# we can legally accept multiple listen directives.
# We insist that the port is always specified, but
# the IP address can be wildcarded.
r = util.gethostport(n[1])
if not r:
raise BadInput, "bad argument to listen"
if r[1] == '':
raise BadInput, "listen requires a port"
self.cf['listen'].append(r)
elif n[0] == 'onfileerror':
if n[1] not in ('drop', 'use-old'):
raise BadInput, "unknown option for onfileerror"
self.cf[n[0]] = n[1]
elif n[0] == "substitutions":
if n[1] not in ("off", "on"):
raise BadInput, "substitutions must be off or on"
self.cf[n[0]] = n[1]
else:
raise BadInput, "unknown config file directive "+n[0]
return None
# Do we have a complete configuration? A complete configuration
# has at least one 'listen' and both 'rulefile' and 'actionfile'.
# 'user' and 'dropipafter' are both optional.
def insurecomplete(self):
if len(self.cf['listen']) == 0:
raise BadInput, "no listen directives specified"
for k in ("rulefile", "actionfile"):
if not self.cf.has_key(k):
raise BadInput, "no %s directive given" % (k,)
if 'dropipafter' in self.cf and 'expireevery' in self.cf:
if self.cf['expireevery'] < 0:
raise BadInput, "Dropipafter conflicts with an expireevery that turns expiry processing off"
# After the file read has completed but before returning, we force a
# verification that the configuration is complete -- that it specifies
# at least *some* value for everything we need. We do it here because
# the consistency requirement is a meta-format issue of the config
# file.
def fromfile(fp, fname):
cf = NannyConfig()
readcf.readcf(fp, fname, cf.parseline, None, BadInput)
cf.insurecomplete()
return cf
def parsefile(fname):
try:
fp = open(fname, "r")
except EnvironmentError, e:
raise BadInput, "cannot open %s: %s" % (fname, str(e))
return fromfile(fp, fname)