170
170
# End of Avahi example code
173
def _datetime_to_dbus_struct(dt, variant_level=0):
174
"""Convert a UTC datetime.datetime() to a D-Bus struct.
175
The format is special to this application, since we could not find
176
any other standard way."""
177
return dbus.Struct((dbus.Int16(dt.year),
181
dbus.Byte(dt.minute),
182
dbus.Byte(dt.second),
183
dbus.UInt32(dt.microsecond)),
185
variant_level=variant_level)
173
188
class Client(dbus.service.Object):
174
189
"""A representation of a client host served by this server.
176
name: string; from the config file, used in log messages
191
name: string; from the config file, used in log messages
177
192
fingerprint: string (40 or 32 hexadecimal digits); used to
178
193
uniquely identify the client
179
secret: bytestring; sent verbatim (over TLS) to client
180
host: string; available for use by the checker command
181
created: datetime.datetime(); (UTC) object creation
182
started: datetime.datetime(); (UTC) last started
194
secret: bytestring; sent verbatim (over TLS) to client
195
host: string; available for use by the checker command
196
created: datetime.datetime(); (UTC) object creation
197
last_started: datetime.datetime(); (UTC)
183
199
last_checked_ok: datetime.datetime(); (UTC) or None
184
timeout: datetime.timedelta(); How long from last_checked_ok
185
until this client is invalid
186
interval: datetime.timedelta(); How often to start a new checker
187
stop_hook: If set, called by stop() as stop_hook(self)
188
checker: subprocess.Popen(); a running checker process used
189
to see if the client lives.
190
'None' if no process is running.
200
timeout: datetime.timedelta(); How long from last_checked_ok
201
until this client is invalid
202
interval: datetime.timedelta(); How often to start a new checker
203
stop_hook: If set, called by stop() as stop_hook(self)
204
checker: subprocess.Popen(); a running checker process used
205
to see if the client lives.
206
'None' if no process is running.
191
207
checker_initiator_tag: a gobject event source tag, or None
192
208
stop_initiator_tag: - '' -
193
209
checker_callback_tag: - '' -
282
306
self.stop_initiator_tag = (gobject.timeout_add
283
307
(self._timeout_milliseconds,
285
310
# Emit D-Bus signal
286
self.StateChanged(True)
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)))
289
318
"""Stop this client."""
290
if getattr(self, "started", None) is not None:
291
logger.info(u"Stopping client %s", self.name)
319
if not getattr(self, "started", False):
321
logger.info(u"Stopping client %s", self.name)
294
322
if getattr(self, "stop_initiator_tag", False):
295
323
gobject.source_remove(self.stop_initiator_tag)
296
324
self.stop_initiator_tag = None
310
339
self.stop_hook = None
313
def checker_callback(self, pid, condition):
342
def checker_callback(self, pid, condition, command):
314
343
"""The checker has completed, so take appropriate actions."""
315
344
self.checker_callback_tag = None
316
345
self.checker = None
317
if (os.WIFEXITED(condition)
347
self.PropertyChanged(dbus.String(u"checker_running"),
348
dbus.Boolean(False, variant_level=1))
349
if (os.WIFEXITED(condition)
318
350
and (os.WEXITSTATUS(condition) == 0)):
319
351
logger.info(u"Checker for %(name)s succeeded",
321
353
# Emit D-Bus signal
322
self.CheckerCompleted(True)
354
self.CheckerCompleted(dbus.Boolean(True),
355
dbus.UInt16(condition),
356
dbus.String(command))
323
357
self.bump_timeout()
324
358
elif not os.WIFEXITED(condition):
325
359
logger.warning(u"Checker for %(name)s crashed?",
327
361
# Emit D-Bus signal
328
self.CheckerCompleted(False)
362
self.CheckerCompleted(dbus.Boolean(False),
363
dbus.UInt16(condition),
364
dbus.String(command))
330
366
logger.info(u"Checker for %(name)s failed",
332
368
# Emit D-Bus signal
333
self.CheckerCompleted(False)
369
self.CheckerCompleted(dbus.Boolean(False),
370
dbus.UInt16(condition),
371
dbus.String(command))
335
373
def bump_timeout(self):
336
374
"""Bump up the timeout for this client.
357
399
# is as it should be.
358
400
if self.checker is None:
360
# In case check_command has exactly one % operator
361
command = self.check_command % self.host
402
# In case checker_command has exactly one % operator
403
command = self.checker_command % self.host
362
404
except TypeError:
363
405
# Escape attributes for the shell
364
406
escaped_attrs = dict((key, re.escape(str(val)))
366
408
vars(self).iteritems())
368
command = self.check_command % escaped_attrs
410
command = self.checker_command % escaped_attrs
369
411
except TypeError, error:
370
412
logger.error(u'Could not format string "%s":'
371
u' %s', self.check_command, error)
413
u' %s', self.checker_command, error)
372
414
return True # Try again later
374
416
logger.info(u"Starting checker %r for %s",
422
469
## D-Bus methods & signals
423
470
_interface = u"org.mandos_system.Mandos.Client"
425
def _datetime_to_dbus_struct(dt):
426
return dbus.Struct(dt.year, dt.month, dt.day, dt.hour,
427
dt.minute, dt.second, dt.microsecond,
430
472
# BumpTimeout - method
431
473
BumpTimeout = dbus.service.method(_interface)(bump_timeout)
432
474
BumpTimeout.__name__ = "BumpTimeout"
434
# IntervalChanged - signal
435
@dbus.service.signal(_interface, signature="t")
436
def IntervalChanged(self, t):
440
476
# CheckerCompleted - signal
441
@dbus.service.signal(_interface, signature="b")
442
def CheckerCompleted(self, success):
477
@dbus.service.signal(_interface, signature="bqs")
478
def CheckerCompleted(self, success, condition, command):
446
# CheckerIsRunning - method
447
@dbus.service.method(_interface, out_signature="b")
448
def CheckerIsRunning(self):
449
"D-Bus getter method"
450
return self.checker is not None
452
482
# CheckerStarted - signal
453
483
@dbus.service.signal(_interface, signature="s")
454
484
def CheckerStarted(self, command):
458
# GetChecker - method
459
@dbus.service.method(_interface, out_signature="s")
460
def GetChecker(self):
461
"D-Bus getter method"
462
return self.checker_command
464
# GetCreated - method
465
@dbus.service.method(_interface, out_signature="(nyyyyyu)")
466
def GetCreated(self):
467
"D-Bus getter method"
468
return datetime_to_dbus_struct(self.created)
470
# GetFingerprint - method
471
@dbus.service.method(_interface, out_signature="s")
472
def GetFingerprint(self):
473
"D-Bus getter method"
474
return self.fingerprint
477
@dbus.service.method(_interface, out_signature="s")
479
"D-Bus getter method"
482
# GetInterval - method
483
@dbus.service.method(_interface, out_signature="t")
484
def GetInterval(self):
485
"D-Bus getter method"
486
return self._interval_milliseconds
489
@dbus.service.method(_interface, out_signature="s")
491
"D-Bus getter method"
494
# GetStarted - method
495
@dbus.service.method(_interface, out_signature="(nyyyyyu)")
496
def GetStarted(self):
497
"D-Bus getter method"
498
if self.started is not None:
499
return datetime_to_dbus_struct(self.started)
501
return dbus.Struct(0, 0, 0, 0, 0, 0, 0,
504
# GetTimeout - method
505
@dbus.service.method(_interface, out_signature="t")
506
def GetTimeout(self):
507
"D-Bus getter method"
508
return self._timeout_milliseconds
488
# GetAllProperties - method
489
@dbus.service.method(_interface, out_signature="a{sv}")
490
def GetAllProperties(self):
492
return dbus.Dictionary({
494
dbus.String(self.name, variant_level=1),
495
dbus.String("fingerprint"):
496
dbus.String(self.fingerprint, variant_level=1),
498
dbus.String(self.host, variant_level=1),
499
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
506
else dbus.Boolean(False, variant_level=1)),
507
dbus.String("started"):
508
dbus.Boolean(self.started, variant_level=1),
509
dbus.String("last_checked_ok"):
510
(_datetime_to_dbus_struct(self.last_checked_ok,
512
if self.last_checked_ok is not None
513
else dbus.Boolean (False, variant_level=1)),
514
dbus.String("timeout"):
515
dbus.UInt64(self._timeout_milliseconds,
517
dbus.String("interval"):
518
dbus.UInt64(self._interval_milliseconds,
520
dbus.String("checker"):
521
dbus.String(self.checker_command,
523
dbus.String("checker_running"):
524
dbus.Boolean(self.checker is not None,
528
# IsStillValid - method
529
IsStillValid = (dbus.service.method(_interface, out_signature="b")
531
IsStillValid.__name__ = "IsStillValid"
533
# PropertyChanged - signal
534
@dbus.service.signal(_interface, signature="sv")
535
def PropertyChanged(self, property, value):
510
539
# SetChecker - method
511
540
@dbus.service.method(_interface, in_signature="s")
536
560
"D-Bus setter method"
537
561
self.secret = str(secret)
563
# SetTimeout - method
564
@dbus.service.method(_interface, in_signature="t")
565
def SetTimeout(self, milliseconds):
566
self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
540
569
Start = dbus.service.method(_interface)(start)
541
570
Start.__name__ = "Start"
543
572
# StartChecker - method
544
StartChecker = dbus.service.method(_interface)(start_checker)
545
StartChecker.__name__ = "StartChecker"
547
# StateChanged - signal
548
@dbus.service.signal(_interface, signature="b")
549
def StateChanged(self, started):
553
# StillValid - method
554
StillValid = (dbus.service.method(_interface, out_signature="b")
556
StillValid.__name__ = "StillValid"
573
@dbus.service.method(_interface)
574
def StartChecker(self):
559
Stop = dbus.service.method(_interface)(stop)
560
Stop.__name__ = "Stop"
579
@dbus.service.method(_interface)
562
584
# StopChecker - method
563
585
StopChecker = dbus.service.method(_interface)(stop_checker)
564
586
StopChecker.__name__ = "StopChecker"
566
# TimeoutChanged - signal
567
@dbus.service.signal(_interface, signature="t")
568
def TimeoutChanged(self, t):
572
del _datetime_to_dbus_struct
1075
1087
signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1076
1088
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:
1129
mandos_server = MandosServer()
1078
1131
for client in clients:
1133
mandos_server.ClientAdded(client.dbus_object_path,
1134
client.GetAllProperties())
1081
1137
tcp_server.enable()