194
184
secret: bytestring; sent verbatim (over TLS) to client
195
185
host: string; available for use by the checker command
196
186
created: datetime.datetime(); (UTC) object creation
197
last_started: datetime.datetime(); (UTC)
187
last_enabled: datetime.datetime(); (UTC)
199
189
last_checked_ok: datetime.datetime(); (UTC) or None
200
190
timeout: datetime.timedelta(); How long from last_checked_ok
201
191
until this client is invalid
202
192
interval: datetime.timedelta(); How often to start a new checker
203
stop_hook: If set, called by stop() as stop_hook(self)
193
disable_hook: If set, called by disable() as disable_hook(self)
204
194
checker: subprocess.Popen(); a running checker process used
205
195
to see if the client lives.
206
196
'None' if no process is running.
207
197
checker_initiator_tag: a gobject event source tag, or None
208
stop_initiator_tag: - '' -
198
disable_initiator_tag: - '' -
209
199
checker_callback_tag: - '' -
210
200
checker_command: string; External command which is run to check if
211
201
client lives. %() expansions are done at
212
202
runtime with vars(self) as dict, so that for
213
203
instance %(name)s can be used in the command.
214
dbus_object_path: dbus.ObjectPath
216
_timeout: Real variable for 'timeout'
217
_interval: Real variable for 'interval'
218
_timeout_milliseconds: Used when calling gobject.timeout_add()
219
_interval_milliseconds: - '' -
204
use_dbus: bool(); Whether to provide D-Bus interface and signals
205
dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
221
def _set_timeout(self, timeout):
222
"Setter function for the 'timeout' attribute"
223
self._timeout = timeout
224
self._timeout_milliseconds = ((self.timeout.days
225
* 24 * 60 * 60 * 1000)
226
+ (self.timeout.seconds * 1000)
227
+ (self.timeout.microseconds
230
self.PropertyChanged(dbus.String(u"timeout"),
231
(dbus.UInt64(self._timeout_milliseconds,
233
timeout = property(lambda self: self._timeout, _set_timeout)
236
def _set_interval(self, interval):
237
"Setter function for the 'interval' attribute"
238
self._interval = interval
239
self._interval_milliseconds = ((self.interval.days
240
* 24 * 60 * 60 * 1000)
241
+ (self.interval.seconds
243
+ (self.interval.microseconds
246
self.PropertyChanged(dbus.String(u"interval"),
247
(dbus.UInt64(self._interval_milliseconds,
249
interval = property(lambda self: self._interval, _set_interval)
252
def __init__(self, name = None, stop_hook=None, config=None):
207
def timeout_milliseconds(self):
208
"Return the 'timeout' attribute in milliseconds"
209
return ((self.timeout.days * 24 * 60 * 60 * 1000)
210
+ (self.timeout.seconds * 1000)
211
+ (self.timeout.microseconds // 1000))
213
def interval_milliseconds(self):
214
"Return the 'interval' attribute in milliseconds"
215
return ((self.interval.days * 24 * 60 * 60 * 1000)
216
+ (self.interval.seconds * 1000)
217
+ (self.interval.microseconds // 1000))
219
def __init__(self, name = None, disable_hook=None, config=None,
253
221
"""Note: the 'checker' key in 'config' sets the
254
222
'checker_command' attribute and *not* the 'checker'
256
self.dbus_object_path = (dbus.ObjectPath
258
+ name.replace(".", "_")))
259
dbus.service.Object.__init__(self, bus,
260
self.dbus_object_path)
261
225
if config is None:
264
227
logger.debug(u"Creating client %r", self.name)
228
self.use_dbus = use_dbus
230
self.dbus_object_path = (dbus.ObjectPath
232
+ self.name.replace(".", "_")))
233
dbus.service.Object.__init__(self, bus,
234
self.dbus_object_path)
265
235
# Uppercase and remove spaces from fingerprint for later
266
236
# comparison purposes with return value from the fingerprint()
281
251
self.host = config.get("host", "")
282
252
self.created = datetime.datetime.utcnow()
284
self.last_started = None
254
self.last_enabled = None
285
255
self.last_checked_ok = None
286
256
self.timeout = string_to_delta(config["timeout"])
287
257
self.interval = string_to_delta(config["interval"])
288
self.stop_hook = stop_hook
258
self.disable_hook = disable_hook
289
259
self.checker = None
290
260
self.checker_initiator_tag = None
291
self.stop_initiator_tag = None
261
self.disable_initiator_tag = None
292
262
self.checker_callback_tag = None
293
263
self.checker_command = config["checker"]
296
266
"""Start this client's checker and timeout hooks"""
297
self.last_started = datetime.datetime.utcnow()
267
self.last_enabled = datetime.datetime.utcnow()
298
268
# Schedule a new checker to be started an 'interval' from now,
299
269
# and every interval from then on.
300
270
self.checker_initiator_tag = (gobject.timeout_add
301
(self._interval_milliseconds,
271
(self.interval_milliseconds(),
302
272
self.start_checker))
303
273
# Also start a new checker *right now*.
304
274
self.start_checker()
305
# Schedule a stop() when 'timeout' has passed
306
self.stop_initiator_tag = (gobject.timeout_add
307
(self._timeout_milliseconds,
311
self.PropertyChanged(dbus.String(u"started"),
312
dbus.Boolean(True, variant_level=1))
313
self.PropertyChanged(dbus.String(u"last_started"),
314
(_datetime_to_dbus_struct
315
(self.last_started, variant_level=1)))
275
# Schedule a disable() when 'timeout' has passed
276
self.disable_initiator_tag = (gobject.timeout_add
277
(self.timeout_milliseconds(),
282
self.PropertyChanged(dbus.String(u"enabled"),
283
dbus.Boolean(True, variant_level=1))
284
self.PropertyChanged(dbus.String(u"last_enabled"),
285
(_datetime_to_dbus(self.last_enabled,
318
"""Stop this client."""
319
if not getattr(self, "started", False):
289
"""Disable this client."""
290
if not getattr(self, "enabled", False):
321
logger.info(u"Stopping client %s", self.name)
322
if getattr(self, "stop_initiator_tag", False):
323
gobject.source_remove(self.stop_initiator_tag)
324
self.stop_initiator_tag = None
292
logger.info(u"Disabling client %s", self.name)
293
if getattr(self, "disable_initiator_tag", False):
294
gobject.source_remove(self.disable_initiator_tag)
295
self.disable_initiator_tag = None
325
296
if getattr(self, "checker_initiator_tag", False):
326
297
gobject.source_remove(self.checker_initiator_tag)
327
298
self.checker_initiator_tag = None
328
299
self.stop_checker()
333
self.PropertyChanged(dbus.String(u"started"),
334
dbus.Boolean(False, variant_level=1))
300
if self.disable_hook:
301
self.disable_hook(self)
305
self.PropertyChanged(dbus.String(u"enabled"),
306
dbus.Boolean(False, variant_level=1))
335
307
# Do not run this again if called by a gobject.timeout_add
338
310
def __del__(self):
339
self.stop_hook = None
311
self.disable_hook = None
342
314
def checker_callback(self, pid, condition, command):
343
315
"""The checker has completed, so take appropriate actions."""
344
316
self.checker_callback_tag = None
345
317
self.checker = None
347
self.PropertyChanged(dbus.String(u"checker_running"),
348
dbus.Boolean(False, variant_level=1))
320
self.PropertyChanged(dbus.String(u"checker_running"),
321
dbus.Boolean(False, variant_level=1))
349
322
if (os.WIFEXITED(condition)
350
323
and (os.WEXITSTATUS(condition) == 0)):
351
324
logger.info(u"Checker for %(name)s succeeded",
354
self.CheckerCompleted(dbus.Boolean(True),
355
dbus.UInt16(condition),
356
dbus.String(command))
328
self.CheckerCompleted(dbus.Boolean(True),
329
dbus.UInt16(condition),
330
dbus.String(command))
357
331
self.bump_timeout()
358
332
elif not os.WIFEXITED(condition):
359
333
logger.warning(u"Checker for %(name)s crashed?",
362
self.CheckerCompleted(dbus.Boolean(False),
363
dbus.UInt16(condition),
364
dbus.String(command))
337
self.CheckerCompleted(dbus.Boolean(False),
338
dbus.UInt16(condition),
339
dbus.String(command))
366
341
logger.info(u"Checker for %(name)s failed",
369
self.CheckerCompleted(dbus.Boolean(False),
370
dbus.UInt16(condition),
371
dbus.String(command))
345
self.CheckerCompleted(dbus.Boolean(False),
346
dbus.UInt16(condition),
347
dbus.String(command))
373
349
def bump_timeout(self):
374
350
"""Bump up the timeout for this client.
497
478
dbus.String("host"):
498
479
dbus.String(self.host, variant_level=1),
499
480
dbus.String("created"):
500
_datetime_to_dbus_struct(self.created,
502
dbus.String("last_started"):
503
(_datetime_to_dbus_struct(self.last_started,
505
if self.last_started is not None
481
_datetime_to_dbus(self.created, variant_level=1),
482
dbus.String("last_enabled"):
483
(_datetime_to_dbus(self.last_enabled,
485
if self.last_enabled is not None
506
486
else dbus.Boolean(False, variant_level=1)),
507
dbus.String("started"):
508
dbus.Boolean(self.started, variant_level=1),
487
dbus.String("enabled"):
488
dbus.Boolean(self.enabled, variant_level=1),
509
489
dbus.String("last_checked_ok"):
510
(_datetime_to_dbus_struct(self.last_checked_ok,
490
(_datetime_to_dbus(self.last_checked_ok,
512
492
if self.last_checked_ok is not None
513
493
else dbus.Boolean (False, variant_level=1)),
514
494
dbus.String("timeout"):
515
dbus.UInt64(self._timeout_milliseconds,
495
dbus.UInt64(self.timeout_milliseconds(),
516
496
variant_level=1),
517
497
dbus.String("interval"):
518
dbus.UInt64(self._interval_milliseconds,
498
dbus.UInt64(self.interval_milliseconds(),
519
499
variant_level=1),
520
500
dbus.String("checker"):
521
501
dbus.String(self.checker_command,
541
521
def SetChecker(self, checker):
542
522
"D-Bus setter method"
543
523
self.checker_command = checker
525
self.PropertyChanged(dbus.String(u"checker"),
526
dbus.String(self.checker_command,
545
529
# SetHost - method
546
530
@dbus.service.method(_interface, in_signature="s")
547
531
def SetHost(self, host):
548
532
"D-Bus setter method"
535
self.PropertyChanged(dbus.String(u"host"),
536
dbus.String(self.host, variant_level=1))
551
538
# SetInterval - method
552
539
@dbus.service.method(_interface, in_signature="t")
553
540
def SetInterval(self, milliseconds):
554
self.interval = datetime.timdeelta(0, 0, 0, milliseconds)
541
self.interval = datetime.timedelta(0, 0, 0, milliseconds)
543
self.PropertyChanged(dbus.String(u"interval"),
544
(dbus.UInt64(self.interval_milliseconds(),
556
547
# SetSecret - method
557
548
@dbus.service.method(_interface, in_signature="ay",
897
parser = OptionParser(version = "%%prog %s" % version)
892
parser = optparse.OptionParser(version = "%%prog %s" % version)
898
893
parser.add_option("-i", "--interface", type="string",
899
894
metavar="IF", help="Bind to interface IF")
900
895
parser.add_option("-a", "--address", type="string",
901
896
help="Address to listen for requests on")
902
897
parser.add_option("-p", "--port", type="int",
903
898
help="Port number to receive requests on")
904
parser.add_option("--check", action="store_true", default=False,
899
parser.add_option("--check", action="store_true",
905
900
help="Run self-test")
906
901
parser.add_option("--debug", action="store_true",
907
902
help="Debug mode; run in foreground and log to"
937
937
server_config.read(os.path.join(options.configdir, "mandos.conf"))
938
938
# Convert the SafeConfigParser object to a dict
939
939
server_settings = server_config.defaults()
940
# Use getboolean on the boolean config option
940
# Use getboolean on the boolean config options
941
941
server_settings["debug"] = (server_config.getboolean
942
942
("DEFAULT", "debug"))
943
server_settings["use_dbus"] = (server_config.getboolean
944
("DEFAULT", "use_dbus"))
943
945
del server_config
945
947
# Override the settings from the config file with command line
946
948
# options, if set.
947
949
for option in ("interface", "address", "port", "debug",
948
"priority", "servicename", "configdir"):
950
"priority", "servicename", "configdir",
949
952
value = getattr(options, option)
950
953
if value is not None:
951
954
server_settings[option] = value
953
956
# Now we have our good server settings in "server_settings"
955
959
debug = server_settings["debug"]
960
use_dbus = server_settings["use_dbus"]
958
963
syslogger.setLevel(logging.WARNING)
1087
1088
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1088
1089
signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1090
class MandosServer(dbus.service.Object):
1091
"""A D-Bus proxy object"""
1093
dbus.service.Object.__init__(self, bus,
1095
_interface = u"org.mandos_system.Mandos"
1097
@dbus.service.signal(_interface, signature="oa{sv}")
1098
def ClientAdded(self, objpath, properties):
1102
@dbus.service.signal(_interface, signature="o")
1103
def ClientRemoved(self, objpath):
1107
@dbus.service.method(_interface, out_signature="ao")
1108
def GetAllClients(self):
1109
return dbus.Array(c.dbus_object_path for c in clients)
1111
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1112
def GetAllClientsWithProperties(self):
1113
return dbus.Dictionary(
1114
((c.dbus_object_path, c.GetAllProperties())
1118
@dbus.service.method(_interface, in_signature="o")
1119
def RemoveClient(self, object_path):
1121
if c.dbus_object_path == object_path:
1092
class MandosServer(dbus.service.Object):
1093
"""A D-Bus proxy object"""
1095
dbus.service.Object.__init__(self, bus,
1097
_interface = u"org.mandos_system.Mandos"
1099
@dbus.service.signal(_interface, signature="oa{sv}")
1100
def ClientAdded(self, objpath, properties):
1104
@dbus.service.signal(_interface, signature="o")
1105
def ClientRemoved(self, objpath):
1109
@dbus.service.method(_interface, out_signature="ao")
1110
def GetAllClients(self):
1111
return dbus.Array(c.dbus_object_path for c in clients)
1113
@dbus.service.method(_interface, out_signature="a{oa{sv}}")
1114
def GetAllClientsWithProperties(self):
1115
return dbus.Dictionary(
1116
((c.dbus_object_path, c.GetAllProperties())
1120
@dbus.service.method(_interface, in_signature="o")
1121
def RemoveClient(self, object_path):
1123
if c.dbus_object_path == object_path:
1125
# Don't signal anything except ClientRemoved
1129
self.ClientRemoved(object_path)
1132
@dbus.service.method(_interface)
1129
mandos_server = MandosServer()
1138
mandos_server = MandosServer()
1131
1140
for client in clients:
1133
mandos_server.ClientAdded(client.dbus_object_path,
1134
client.GetAllProperties())
1143
mandos_server.ClientAdded(client.dbus_object_path,
1144
client.GetAllProperties())
1137
1147
tcp_server.enable()
1138
1148
tcp_server.server_activate()