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