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
41
41
from dbus.mainloop.glib import DBusGMainLoop
43
from gi.repository import GObject
44
44
except ImportError:
45
from gi.repository import GObject as gobject
45
import gobject as GObject
51
if sys.version_info[0] == 2:
51
if sys.version_info.major == 2:
54
54
locale.setlocale(locale.LC_ALL, '')
101
106
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.
108
def properties_changed(self, interface, properties, invalidated):
109
"""This is called whenever we get a PropertiesChanged signal
110
It updates the changed properties in the "properties" dict.
111
112
# Update properties dict with new value
112
self.properties[property] = value
113
if interface == client_interface:
114
self.properties.update(properties)
114
116
def delete(self):
115
117
self.property_changed_match.remove()
171
173
if flag and self._update_timer_callback_tag is None:
172
174
# Will update the shown timer value every second
173
self._update_timer_callback_tag = (gobject.timeout_add
175
self._update_timer_callback_tag = (GObject.timeout_add
175
177
self.update_timer))
176
178
elif not (flag or self._update_timer_callback_tag is None):
177
gobject.source_remove(self._update_timer_callback_tag)
179
GObject.source_remove(self._update_timer_callback_tag)
178
180
self._update_timer_callback_tag = None
180
182
def checker_completed(self, exitstatus, condition, command):
181
183
if exitstatus == 0:
182
self.logger('Checker for client {0} (command "{1}")'
184
self.logger('Checker for client {} (command "{}")'
183
185
' succeeded'.format(self.properties["Name"],
184
186
command), level=0)
188
190
if os.WIFEXITED(condition):
189
self.logger('Checker for client {0} (command "{1}")'
190
' failed with exit code {2}'
191
self.logger('Checker for client {} (command "{}") failed'
191
193
.format(self.properties["Name"], command,
192
194
os.WEXITSTATUS(condition)))
193
195
elif os.WIFSIGNALED(condition):
194
self.logger('Checker for client {0} (command "{1}") was'
195
' killed by signal {2}'
196
self.logger('Checker for client {} (command "{}") was'
197
' killed by signal {}'
196
198
.format(self.properties["Name"], command,
197
199
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
202
def checker_started(self, command):
209
203
"""Server signals that a checker started."""
210
self.logger('Client {0} started checker "{1}"'
204
self.logger('Client {} started checker "{}"'
211
205
.format(self.properties["Name"],
212
206
command), level=0)
214
208
def got_secret(self):
215
self.logger('Client {0} received its secret'
209
self.logger('Client {} received its secret'
216
210
.format(self.properties["Name"]))
218
212
def need_approval(self, timeout, default):
220
message = 'Client {0} needs approval within {1} seconds'
214
message = 'Client {} needs approval within {} seconds'
222
message = 'Client {0} will get its secret in {1} seconds'
216
message = 'Client {} will get its secret in {} seconds'
223
217
self.logger(message.format(self.properties["Name"],
226
220
def rejected(self, reason):
227
self.logger('Client {0} was rejected; reason: {1}'
221
self.logger('Client {} was rejected; reason: {}'
228
222
.format(self.properties["Name"], reason))
230
224
def selectable(self):
275
269
timer = datetime.timedelta()
276
270
if self.properties["ApprovedByDefault"]:
277
message = "Approval in {0}. (d)eny?"
271
message = "Approval in {}. (d)eny?"
279
message = "Denial in {0}. (a)pprove?"
273
message = "Denial in {}. (a)pprove?"
280
274
message = message.format(str(timer).rsplit(".", 1)[0])
281
275
self.using_timer(True)
282
276
elif self.properties["LastCheckerStatus"] != 0:
290
284
timer = max(expires - datetime.datetime.utcnow(),
291
285
datetime.timedelta())
292
286
message = ('A checker has failed! Time until client'
293
' gets disabled: {0}'
294
288
.format(str(timer).rsplit(".", 1)[0]))
295
289
self.using_timer(True)
297
291
message = "enabled"
298
292
self.using_timer(False)
299
self._text = "{0}{1}".format(base, message)
293
self._text = "{}{}".format(base, message)
301
295
if not urwid.supports_unicode():
302
296
self._text = self._text.encode("ascii", "replace")
315
309
self.update_hook()
317
311
def update_timer(self):
318
"""called by gobject. Will indefinitely loop until
319
gobject.source_remove() on tag is called"""
312
"""called by GObject. Will indefinitely loop until
313
GObject.source_remove() on tag is called"""
321
315
return True # Keep calling this
323
317
def delete(self, **kwargs):
324
318
if self._update_timer_callback_tag is not None:
325
gobject.source_remove(self._update_timer_callback_tag)
319
GObject.source_remove(self._update_timer_callback_tag)
326
320
self._update_timer_callback_tag = None
327
321
for match in self.match_objects:
342
336
This overrides the method from urwid.FlowWidget"""
344
self.proxy.Enable(dbus_interface = client_interface,
338
self.proxy.Set(client_interface, "Enabled",
339
dbus.Boolean(True), ignore_reply = True,
340
dbus_interface = dbus.PROPERTIES_IFACE)
347
self.proxy.Disable(dbus_interface = client_interface,
342
self.proxy.Set(client_interface, "Enabled", False,
344
dbus_interface = dbus.PROPERTIES_IFACE)
350
346
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
351
347
dbus_interface = client_interface,
360
356
ignore_reply=True)
362
self.proxy.StartChecker(dbus_interface = client_interface,
358
self.proxy.Set(client_interface, "CheckerRunning",
359
dbus.Boolean(True), ignore_reply = True,
360
dbus_interface = dbus.PROPERTIES_IFACE)
365
self.proxy.StopChecker(dbus_interface = client_interface,
362
self.proxy.Set(client_interface, "CheckerRunning",
363
dbus.Boolean(False), ignore_reply = True,
364
dbus_interface = dbus.PROPERTIES_IFACE)
368
366
self.proxy.CheckedOK(dbus_interface = client_interface,
369
367
ignore_reply=True)
380
def property_changed(self, property=None, **kwargs):
381
"""Call self.update() if old value is not new value.
378
def properties_changed(self, interface, properties, invalidated):
379
"""Call self.update() if any properties changed.
382
380
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:
381
old_values = { key: self.properties.get(key)
382
for key in properties.keys() }
383
super(MandosClientWidget, self).properties_changed(
384
interface, properties, invalidated)
385
if any(old_values[key] != self.properties.get(key)
386
for key in old_values):
466
465
"q: Quit ?: Help"))
468
467
self.busname = domain + '.Mandos'
469
self.main_loop = gobject.MainLoop()
468
self.main_loop = GObject.MainLoop()
471
470
def client_not_found(self, fingerprint, address):
472
self.log_message("Client with address {0} and fingerprint"
473
" {1} could not be found"
471
self.log_message("Client with address {} and fingerprint {}"
472
" could not be found"
474
473
.format(address, fingerprint))
476
475
def rebuild(self):
494
493
if level < self.log_level:
496
495
timestamp = datetime.datetime.now().isoformat()
497
self.log_message_raw("{0}: {1}".format(timestamp, message),
496
self.log_message_raw("{}: {}".format(timestamp, message),
500
499
def log_message_raw(self, markup, level=1):
525
524
self.log_wrap = "clip"
526
525
for textwidget in self.log:
527
526
textwidget.set_wrap_mode(self.log_wrap)
528
self.log_message("Wrap mode: {0}".format(self.log_wrap),
527
self.log_message("Wrap mode: {}".format(self.log_wrap),
531
def find_and_remove_client(self, path, name):
530
def find_and_remove_client(self, path, interfaces):
532
531
"""Find a client by its object path and remove it.
534
This is connected to the ClientRemoved signal from the
533
This is connected to the InterfacesRemoved signal from the
535
534
Mandos server object."""
535
if client_interface not in interfaces:
536
# Not a Mandos client object; ignore
537
539
client = self.clients_dict[path]
540
self.log_message("Unknown client {0!r} ({1!r}) removed"
542
self.log_message("Unknown client {!r} removed"
545
def add_new_client(self, path):
547
def add_new_client(self, path, ifs_and_props):
548
"""Find a client by its object path and remove it.
550
This is connected to the InterfacesAdded signal from the
551
Mandos server object.
553
if client_interface not in ifs_and_props:
554
# Not a Mandos client object; ignore
546
556
client_proxy_object = self.bus.get_object(self.busname, path)
547
557
self.add_client(MandosClientWidget(server_proxy_object
548
558
=self.mandos_serv,
594
607
mandos_clients = dbus.Dictionary()
596
609
(self.mandos_serv
597
.connect_to_signal("ClientRemoved",
610
.connect_to_signal("InterfacesRemoved",
598
611
self.find_and_remove_client,
599
dbus_interface=server_interface,
613
= dbus.OBJECT_MANAGER_IFACE,
600
614
byte_arrays=True))
601
615
(self.mandos_serv
602
.connect_to_signal("ClientAdded",
616
.connect_to_signal("InterfacesAdded",
603
617
self.add_new_client,
604
dbus_interface=server_interface,
619
= dbus.OBJECT_MANAGER_IFACE,
605
620
byte_arrays=True))
606
621
(self.mandos_serv
607
622
.connect_to_signal("ClientNotFound",