4
4
# Mandos Monitor - Control and monitor the Mandos server
6
# Copyright © 2009-2016 Teddy Hogeborn
7
# Copyright © 2009-2016 Björn Påhlsson
6
# Copyright © 2009-2014 Teddy Hogeborn
7
# Copyright © 2009-2014 Björn Påhlsson
9
9
# This program is free software: you can redistribute it and/or modify
10
10
# it under the terms of the GNU General Public License as published by
103
101
super(MandosClientPropertyCache, self).__init__(**kwargs)
105
def properties_changed(self, interface, properties, invalidated):
106
"""This is called whenever we get a PropertiesChanged signal
107
It updates the changed properties in the "properties" dict.
103
def _property_changed(self, property, value):
104
"""Helper which takes positional arguments"""
105
return self.property_changed(property=property, value=value)
107
def property_changed(self, property=None, value=None):
108
"""This is called whenever we get a PropertyChanged signal
109
It updates the changed property in the "properties" dict.
109
111
# Update properties dict with new value
110
if interface == client_interface:
111
self.properties.update(properties)
112
self.properties[property] = value
113
114
def delete(self):
114
115
self.property_changed_match.remove()
170
171
if flag and self._update_timer_callback_tag is None:
171
172
# Will update the shown timer value every second
172
self._update_timer_callback_tag = (GLib.timeout_add
173
self._update_timer_callback_tag = (gobject.timeout_add
174
175
self.update_timer))
175
176
elif not (flag or self._update_timer_callback_tag is None):
176
GLib.source_remove(self._update_timer_callback_tag)
177
gobject.source_remove(self._update_timer_callback_tag)
177
178
self._update_timer_callback_tag = None
179
180
def checker_completed(self, exitstatus, condition, command):
180
181
if exitstatus == 0:
181
self.logger('Checker for client {} (command "{}")'
182
self.logger('Checker for client {0} (command "{1}")'
182
183
' succeeded'.format(self.properties["Name"],
183
184
command), level=0)
187
188
if os.WIFEXITED(condition):
188
self.logger('Checker for client {} (command "{}") failed'
189
self.logger('Checker for client {0} (command "{1}")'
190
' failed with exit code {2}'
190
191
.format(self.properties["Name"], command,
191
192
os.WEXITSTATUS(condition)))
192
193
elif os.WIFSIGNALED(condition):
193
self.logger('Checker for client {} (command "{}") was'
194
' killed by signal {}'
194
self.logger('Checker for client {0} (command "{1}") was'
195
' killed by signal {2}'
195
196
.format(self.properties["Name"], command,
196
197
os.WTERMSIG(condition)))
198
elif os.WCOREDUMP(condition):
199
self.logger('Checker for client {0} (command "{1}")'
201
.format(self.properties["Name"], command))
203
self.logger('Checker for client {0} completed'
205
.format(self.properties["Name"]))
199
208
def checker_started(self, command):
200
209
"""Server signals that a checker started."""
201
self.logger('Client {} started checker "{}"'
210
self.logger('Client {0} started checker "{1}"'
202
211
.format(self.properties["Name"],
203
212
command), level=0)
205
214
def got_secret(self):
206
self.logger('Client {} received its secret'
215
self.logger('Client {0} received its secret'
207
216
.format(self.properties["Name"]))
209
218
def need_approval(self, timeout, default):
211
message = 'Client {} needs approval within {} seconds'
220
message = 'Client {0} needs approval within {1} seconds'
213
message = 'Client {} will get its secret in {} seconds'
222
message = 'Client {0} will get its secret in {1} seconds'
214
223
self.logger(message.format(self.properties["Name"],
217
226
def rejected(self, reason):
218
self.logger('Client {} was rejected; reason: {}'
227
self.logger('Client {0} was rejected; reason: {1}'
219
228
.format(self.properties["Name"], reason))
221
230
def selectable(self):
266
275
timer = datetime.timedelta()
267
276
if self.properties["ApprovedByDefault"]:
268
message = "Approval in {}. (d)eny?"
277
message = "Approval in {0}. (d)eny?"
270
message = "Denial in {}. (a)pprove?"
279
message = "Denial in {0}. (a)pprove?"
271
280
message = message.format(str(timer).rsplit(".", 1)[0])
272
281
self.using_timer(True)
273
282
elif self.properties["LastCheckerStatus"] != 0:
281
290
timer = max(expires - datetime.datetime.utcnow(),
282
291
datetime.timedelta())
283
292
message = ('A checker has failed! Time until client'
293
' gets disabled: {0}'
285
294
.format(str(timer).rsplit(".", 1)[0]))
286
295
self.using_timer(True)
288
297
message = "enabled"
289
298
self.using_timer(False)
290
self._text = "{}{}".format(base, message)
299
self._text = "{0}{1}".format(base, message)
292
301
if not urwid.supports_unicode():
293
302
self._text = self._text.encode("ascii", "replace")
306
315
self.update_hook()
308
317
def update_timer(self):
309
"""called by GLib. Will indefinitely loop until
310
GLib.source_remove() on tag is called
318
"""called by gobject. Will indefinitely loop until
319
gobject.source_remove() on tag is called"""
313
321
return True # Keep calling this
315
323
def delete(self, **kwargs):
316
324
if self._update_timer_callback_tag is not None:
317
GLib.source_remove(self._update_timer_callback_tag)
325
gobject.source_remove(self._update_timer_callback_tag)
318
326
self._update_timer_callback_tag = None
319
327
for match in self.match_objects:
334
342
This overrides the method from urwid.FlowWidget"""
336
self.proxy.Set(client_interface, "Enabled",
337
dbus.Boolean(True), ignore_reply = True,
338
dbus_interface = dbus.PROPERTIES_IFACE)
344
self.proxy.Enable(dbus_interface = client_interface,
340
self.proxy.Set(client_interface, "Enabled", False,
342
dbus_interface = dbus.PROPERTIES_IFACE)
347
self.proxy.Disable(dbus_interface = client_interface,
344
350
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
345
351
dbus_interface = client_interface,
354
360
ignore_reply=True)
356
self.proxy.Set(client_interface, "CheckerRunning",
357
dbus.Boolean(True), ignore_reply = True,
358
dbus_interface = dbus.PROPERTIES_IFACE)
362
self.proxy.StartChecker(dbus_interface = client_interface,
360
self.proxy.Set(client_interface, "CheckerRunning",
361
dbus.Boolean(False), ignore_reply = True,
362
dbus_interface = dbus.PROPERTIES_IFACE)
365
self.proxy.StopChecker(dbus_interface = client_interface,
364
368
self.proxy.CheckedOK(dbus_interface = client_interface,
365
369
ignore_reply=True)
376
def properties_changed(self, interface, properties, invalidated):
377
"""Call self.update() if any properties changed.
380
def property_changed(self, property=None, **kwargs):
381
"""Call self.update() if old value is not new value.
378
382
This overrides the method from MandosClientPropertyCache"""
379
old_values = { key: self.properties.get(key)
380
for key in properties.keys() }
381
super(MandosClientWidget, self).properties_changed(
382
interface, properties, invalidated)
383
if any(old_values[key] != self.properties.get(key)
384
for key in old_values):
383
property_name = str(property)
384
old_value = self.properties.get(property_name)
385
super(MandosClientWidget, self).property_changed(
386
property=property, **kwargs)
387
if self.properties.get(property_name) != old_value:
463
466
"q: Quit ?: Help"))
465
468
self.busname = domain + '.Mandos'
466
self.main_loop = GLib.MainLoop()
469
self.main_loop = gobject.MainLoop()
468
471
def client_not_found(self, fingerprint, address):
469
self.log_message("Client with address {} and fingerprint {}"
470
" could not be found"
472
self.log_message("Client with address {0} and fingerprint"
473
" {1} could not be found"
471
474
.format(address, fingerprint))
473
476
def rebuild(self):
491
494
if level < self.log_level:
493
496
timestamp = datetime.datetime.now().isoformat()
494
self.log_message_raw("{}: {}".format(timestamp, message),
497
self.log_message_raw("{0}: {1}".format(timestamp, message),
497
500
def log_message_raw(self, markup, level=1):
522
525
self.log_wrap = "clip"
523
526
for textwidget in self.log:
524
527
textwidget.set_wrap_mode(self.log_wrap)
525
self.log_message("Wrap mode: {}".format(self.log_wrap),
528
self.log_message("Wrap mode: {0}".format(self.log_wrap),
528
def find_and_remove_client(self, path, interfaces):
531
def find_and_remove_client(self, path, name):
529
532
"""Find a client by its object path and remove it.
531
This is connected to the InterfacesRemoved signal from the
534
This is connected to the ClientRemoved signal from the
532
535
Mandos server object."""
533
if client_interface not in interfaces:
534
# Not a Mandos client object; ignore
537
537
client = self.clients_dict[path]
540
self.log_message("Unknown client {!r} removed"
540
self.log_message("Unknown client {0!r} ({1!r}) removed"
545
def add_new_client(self, path, ifs_and_props):
546
"""Find a client by its object path and remove it.
548
This is connected to the InterfacesAdded signal from the
549
Mandos server object.
551
if client_interface not in ifs_and_props:
552
# Not a Mandos client object; ignore
545
def add_new_client(self, path):
554
546
client_proxy_object = self.bus.get_object(self.busname, path)
555
547
self.add_client(MandosClientWidget(server_proxy_object
556
548
=self.mandos_serv,
605
594
mandos_clients = dbus.Dictionary()
607
596
(self.mandos_serv
608
.connect_to_signal("InterfacesRemoved",
597
.connect_to_signal("ClientRemoved",
609
598
self.find_and_remove_client,
611
= dbus.OBJECT_MANAGER_IFACE,
599
dbus_interface=server_interface,
612
600
byte_arrays=True))
613
601
(self.mandos_serv
614
.connect_to_signal("InterfacesAdded",
602
.connect_to_signal("ClientAdded",
615
603
self.add_new_client,
617
= dbus.OBJECT_MANAGER_IFACE,
604
dbus_interface=server_interface,
618
605
byte_arrays=True))
619
606
(self.mandos_serv
620
607
.connect_to_signal("ClientNotFound",