1
#!/usr/bin/python3 -bbI
2
2
# -*- mode: python; coding: utf-8 -*-
4
4
# Mandos Monitor - Control and monitor the Mandos server
6
# Copyright © 2009-2012 Teddy Hogeborn
7
# Copyright © 2009-2012 Björn Påhlsson
9
# This program is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU General Public License as published by
6
# Copyright © 2009-2019 Teddy Hogeborn
7
# Copyright © 2009-2019 Björn Påhlsson
9
# This file is part of Mandos.
11
# Mandos is free software: you can redistribute it and/or modify it
12
# under the terms of the GNU General Public License as published by
11
13
# the Free Software Foundation, either version 3 of the License, or
12
14
# (at your option) any later version.
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# Mandos is distributed in the hope that it will be useful, but
17
# WITHOUT ANY WARRANTY; without even the implied warranty of
16
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
19
# GNU General Public License for more details.
19
21
# You should have received a copy of the GNU General Public License
20
# along with this program. If not, see
21
# <http://www.gnu.org/licenses/>.
22
# along with Mandos. If not, see <http://www.gnu.org/licenses/>.
23
24
# Contact the authors at <mandos@recompile.se>.
26
26
from __future__ import (division, absolute_import, print_function,
29
from future_builtins import *
30
from future_builtins import *
37
41
import urwid.curses_display
40
44
from dbus.mainloop.glib import DBusGMainLoop
45
from gi.repository import GLib
49
locale.setlocale(locale.LC_ALL, '')
52
logging.getLogger('dbus.proxies').setLevel(logging.CRITICAL)
49
if sys.version_info.major == 2:
54
# Show warnings by default
55
if not sys.warnoptions:
56
warnings.simplefilter("default")
58
log = logging.getLogger(os.path.basename(sys.argv[0]))
59
logging.basicConfig(level="NOTSET", # Show all messages
60
format="%(message)s") # Show basic log messages
62
logging.captureWarnings(True) # Show warnings via the logging system
64
locale.setlocale(locale.LC_ALL, "")
66
logging.getLogger("dbus.proxies").setLevel(logging.CRITICAL)
67
logging.getLogger("urwid").setLevel(logging.INFO)
54
69
# Some useful constants
55
domain = 'se.recompile'
56
server_interface = domain + '.Mandos'
57
client_interface = domain + '.Mandos.Client'
60
# Always run in monochrome mode
61
urwid.curses_display.curses.has_colors = lambda : False
63
# Urwid doesn't support blinking, but we want it. Since we have no
64
# use for underline on its own, we make underline also always blink.
65
urwid.curses_display.curses.A_UNDERLINE |= (
66
urwid.curses_display.curses.A_BLINK)
70
domain = "se.recompile"
71
server_interface = domain + ".Mandos"
72
client_interface = domain + ".Mandos.Client"
76
dbus.OBJECT_MANAGER_IFACE
77
except AttributeError:
78
dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
68
81
def isoformat_to_datetime(iso):
69
82
"Parse an ISO 8601 date string to a datetime.datetime()"
81
int(second), # Whole seconds
82
int(fraction*1000000)) # Microseconds
84
class MandosClientPropertyCache(object):
94
int(second), # Whole seconds
95
int(fraction*1000000)) # Microseconds
98
class MandosClientPropertyCache:
85
99
"""This wraps a Mandos Client D-Bus proxy object, caches the
86
100
properties and calls a hook function when any of them are
89
def __init__(self, proxy_object=None, *args, **kwargs):
90
self.proxy = proxy_object # Mandos Client proxy object
92
self.properties = dict()
103
def __init__(self, proxy_object=None, properties=None, **kwargs):
104
self.proxy = proxy_object # Mandos Client proxy object
105
self.properties = dict() if properties is None else properties
93
106
self.property_changed_match = (
94
self.proxy.connect_to_signal("PropertyChanged",
95
self.property_changed,
107
self.proxy.connect_to_signal("PropertiesChanged",
108
self.properties_changed,
109
dbus.PROPERTIES_IFACE,
97
110
byte_arrays=True))
99
self.properties.update(
100
self.proxy.GetAll(client_interface,
101
dbus_interface = dbus.PROPERTIES_IFACE))
103
#XXX This breaks good super behaviour
104
# super(MandosClientPropertyCache, self).__init__(
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.
112
if properties is None:
113
self.properties.update(self.proxy.GetAll(
115
dbus_interface=dbus.PROPERTIES_IFACE))
117
super(MandosClientPropertyCache, self).__init__(**kwargs)
119
def properties_changed(self, interface, properties, invalidated):
120
"""This is called whenever we get a PropertiesChanged signal
121
It updates the changed properties in the "properties" dict.
111
123
# Update properties dict with new value
112
self.properties[property] = value
114
def delete(self, *args, **kwargs):
124
if interface == client_interface:
125
self.properties.update(properties)
115
128
self.property_changed_match.remove()
116
super(MandosClientPropertyCache, self).__init__(
120
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
131
class MandosClientWidget(MandosClientPropertyCache, urwid.Widget):
121
132
"""A Mandos Client which is visible on the screen.
135
_sizing = frozenset(["flow"])
124
137
def __init__(self, server_proxy_object=None, update_hook=None,
125
delete_hook=None, logger=None, *args, **kwargs):
138
delete_hook=None, **kwargs):
126
139
# Called on update
127
140
self.update_hook = update_hook
128
141
# Called on delete
129
142
self.delete_hook = delete_hook
130
143
# Mandos Server proxy object
131
144
self.server_proxy_object = server_proxy_object
135
146
self._update_timer_callback_tag = None
136
self._update_timer_callback_lock = 0
138
148
# The widget shown normally
139
149
self._text_widget = urwid.Text("")
140
150
# The widget shown when we have focus
141
151
self._focus_text_widget = urwid.Text("")
142
super(MandosClientWidget, self).__init__(
143
update_hook=update_hook, delete_hook=delete_hook,
152
super(MandosClientWidget, self).__init__(**kwargs)
146
154
self.opened = False
148
last_checked_ok = isoformat_to_datetime(self.properties
151
if self.properties ["LastCheckerStatus"] != 0:
152
self.using_timer(True)
154
if self.need_approval:
155
self.using_timer(True)
157
156
self.match_objects = (
158
157
self.proxy.connect_to_signal("CheckerCompleted",
159
158
self.checker_completed,
176
175
client_interface,
177
176
byte_arrays=True))
178
#self.logger('Created client {0}'
179
# .format(self.properties["Name"]))
181
def property_changed(self, property=None, value=None):
182
super(self, MandosClientWidget).property_changed(property,
184
if property == "ApprovalPending":
185
using_timer(bool(value))
186
if property == "LastCheckerStatus":
187
using_timer(value != 0)
188
#self.logger('Checker for client {0} (command "{1}") was '
189
# ' successful'.format(self.properties["Name"],
177
log.debug("Created client %s", self.properties["Name"])
192
179
def using_timer(self, flag):
193
180
"""Call this method with True or False when timer should be
194
181
activated or deactivated.
196
old = self._update_timer_callback_lock
198
self._update_timer_callback_lock += 1
200
self._update_timer_callback_lock -= 1
201
if old == 0 and self._update_timer_callback_lock:
183
if flag and self._update_timer_callback_tag is None:
202
184
# Will update the shown timer value every second
203
self._update_timer_callback_tag = (gobject.timeout_add
206
elif old and self._update_timer_callback_lock == 0:
207
gobject.source_remove(self._update_timer_callback_tag)
185
self._update_timer_callback_tag = (
186
GLib.timeout_add(1000,
187
glib_safely(self.update_timer)))
188
elif not (flag or self._update_timer_callback_tag is None):
189
GLib.source_remove(self._update_timer_callback_tag)
208
190
self._update_timer_callback_tag = None
210
192
def checker_completed(self, exitstatus, condition, command):
211
193
if exitstatus == 0:
194
log.debug('Checker for client %s (command "%s")'
195
" succeeded", self.properties["Name"], command)
215
199
if os.WIFEXITED(condition):
216
self.logger('Checker for client {0} (command "{1}")'
217
' failed with exit code {2}'
218
.format(self.properties["Name"], command,
219
os.WEXITSTATUS(condition)))
200
log.info('Checker for client %s (command "%s") failed'
201
" with exit code %d", self.properties["Name"],
202
command, os.WEXITSTATUS(condition))
220
203
elif os.WIFSIGNALED(condition):
221
self.logger('Checker for client {0} (command "{1}") was'
222
' killed by signal {2}'
223
.format(self.properties["Name"], command,
224
os.WTERMSIG(condition)))
225
elif os.WCOREDUMP(condition):
226
self.logger('Checker for client {0} (command "{1}")'
228
.format(self.properties["Name"], command))
230
self.logger('Checker for client {0} completed'
232
.format(self.properties["Name"]))
204
log.info('Checker for client %s (command "%s") was'
205
" killed by signal %d", self.properties["Name"],
206
command, os.WTERMSIG(condition))
235
209
def checker_started(self, command):
236
"""Server signals that a checker started. This could be useful
237
to log in the future. """
238
#self.logger('Client {0} started checker "{1}"'
239
# .format(self.properties["Name"],
210
"""Server signals that a checker started."""
211
log.debug('Client %s started checker "%s"',
212
self.properties["Name"], command)
243
214
def got_secret(self):
244
self.logger('Client {0} received its secret'
245
.format(self.properties["Name"]))
215
log.info("Client %s received its secret",
216
self.properties["Name"])
247
218
def need_approval(self, timeout, default):
249
message = 'Client {0} needs approval within {1} seconds'
220
message = "Client %s needs approval within %f seconds"
251
message = 'Client {0} will get its secret in {1} seconds'
252
self.logger(message.format(self.properties["Name"],
254
self.using_timer(True)
222
message = "Client %s will get its secret in %f seconds"
223
log.info(message, self.properties["Name"], timeout/1000)
256
225
def rejected(self, reason):
257
self.logger('Client {0} was rejected; reason: {1}'
258
.format(self.properties["Name"], reason))
226
log.info("Client %s was rejected; reason: %s",
227
self.properties["Name"], reason)
260
229
def selectable(self):
261
230
"""Make this a "selectable" widget.
262
This overrides the method from urwid.FlowWidget."""
231
This overrides the method from urwid.Widget."""
265
234
def rows(self, maxcolrow, focus=False):
266
235
"""How many rows this widget will occupy might depend on
267
236
whether we have focus or not.
268
This overrides the method from urwid.FlowWidget"""
237
This overrides the method from urwid.Widget"""
269
238
return self.current_widget(focus).rows(maxcolrow, focus=focus)
271
240
def current_widget(self, focus=False):
272
241
if focus or self.opened:
273
242
return self._focus_widget
274
243
return self._widget
276
245
def update(self):
277
246
"Called when what is visible on the screen should be updated."
278
247
# How to add standout mode to a style
279
with_standout = { "normal": "standout",
280
"bold": "bold-standout",
282
"underline-blink-standout",
283
"bold-underline-blink":
284
"bold-underline-blink-standout",
248
with_standout = {"normal": "standout",
249
"bold": "bold-standout",
251
"underline-blink-standout",
252
"bold-underline-blink":
253
"bold-underline-blink-standout",
287
256
# Rebuild focus and non-focus widgets using current properties
289
258
# Base part of a client. Name!
290
base = '{name}: '.format(name=self.properties["Name"])
259
base = "{name}: ".format(name=self.properties["Name"])
291
260
if not self.properties["Enabled"]:
292
261
message = "DISABLED"
262
self.using_timer(False)
293
263
elif self.properties["ApprovalPending"]:
294
timeout = datetime.timedelta(milliseconds
264
timeout = datetime.timedelta(
265
milliseconds=self.properties["ApprovalDelay"])
297
266
last_approval_request = isoformat_to_datetime(
298
267
self.properties["LastApprovalRequest"])
299
268
if last_approval_request is not None:
300
timer = timeout - (datetime.datetime.utcnow()
301
- last_approval_request)
269
timer = max(timeout - (datetime.datetime.utcnow()
270
- last_approval_request),
271
datetime.timedelta())
303
273
timer = datetime.timedelta()
304
274
if self.properties["ApprovedByDefault"]:
305
message = "Approval in {0}. (d)eny?"
275
message = "Approval in {}. (d)eny?"
307
message = "Denial in {0}. (a)pprove?"
308
message = message.format(unicode(timer).rsplit(".", 1)[0])
277
message = "Denial in {}. (a)pprove?"
278
message = message.format(str(timer).rsplit(".", 1)[0])
279
self.using_timer(True)
309
280
elif self.properties["LastCheckerStatus"] != 0:
310
281
# When checker has failed, show timer until client expires
311
282
expires = self.properties["Expires"]
337
311
# Run update hook, if any
338
312
if self.update_hook is not None:
339
313
self.update_hook()
341
315
def update_timer(self):
342
"""called by gobject. Will indefinitely loop until
343
gobject.source_remove() on tag is called"""
316
"""called by GLib. Will indefinitely loop until
317
GLib.source_remove() on tag is called
345
320
return True # Keep calling this
347
def delete(self, *args, **kwargs):
322
def delete(self, **kwargs):
348
323
if self._update_timer_callback_tag is not None:
349
gobject.source_remove(self._update_timer_callback_tag)
324
GLib.source_remove(self._update_timer_callback_tag)
350
325
self._update_timer_callback_tag = None
351
326
for match in self.match_objects:
353
328
self.match_objects = ()
354
329
if self.delete_hook is not None:
355
330
self.delete_hook(self)
356
return super(MandosClientWidget, self).delete(*args, **kwargs)
331
return super(MandosClientWidget, self).delete(**kwargs)
358
333
def render(self, maxcolrow, focus=False):
359
334
"""Render differently if we have focus.
360
This overrides the method from urwid.FlowWidget"""
335
This overrides the method from urwid.Widget"""
361
336
return self.current_widget(focus).render(maxcolrow,
364
339
def keypress(self, maxcolrow, key):
366
This overrides the method from urwid.FlowWidget"""
341
This overrides the method from urwid.Widget"""
368
self.proxy.Enable(dbus_interface = client_interface,
343
self.proxy.Set(client_interface, "Enabled",
344
dbus.Boolean(True), ignore_reply=True,
345
dbus_interface=dbus.PROPERTIES_IFACE)
371
self.proxy.Disable(dbus_interface = client_interface,
347
self.proxy.Set(client_interface, "Enabled", False,
349
dbus_interface=dbus.PROPERTIES_IFACE)
374
351
self.proxy.Approve(dbus.Boolean(True, variant_level=1),
375
dbus_interface = client_interface,
352
dbus_interface=client_interface,
376
353
ignore_reply=True)
378
355
self.proxy.Approve(dbus.Boolean(False, variant_level=1),
379
dbus_interface = client_interface,
356
dbus_interface=client_interface,
380
357
ignore_reply=True)
381
358
elif key == "R" or key == "_" or key == "ctrl k":
382
359
self.server_proxy_object.RemoveClient(self.proxy
384
361
ignore_reply=True)
386
self.proxy.StartChecker(dbus_interface = client_interface,
363
self.proxy.Set(client_interface, "CheckerRunning",
364
dbus.Boolean(True), ignore_reply=True,
365
dbus_interface=dbus.PROPERTIES_IFACE)
389
self.proxy.StopChecker(dbus_interface = client_interface,
367
self.proxy.Set(client_interface, "CheckerRunning",
368
dbus.Boolean(False), ignore_reply=True,
369
dbus_interface=dbus.PROPERTIES_IFACE)
392
self.proxy.CheckedOK(dbus_interface = client_interface,
371
self.proxy.CheckedOK(dbus_interface=client_interface,
393
372
ignore_reply=True)
395
374
# elif key == "p" or key == "=":
404
def property_changed(self, property=None, value=None,
406
"""Call self.update() if old value is not new value.
383
def properties_changed(self, interface, properties, invalidated):
384
"""Call self.update() if any properties changed.
407
385
This overrides the method from MandosClientPropertyCache"""
408
property_name = unicode(property)
409
old_value = self.properties.get(property_name)
410
super(MandosClientWidget, self).property_changed(
411
property=property, value=value, *args, **kwargs)
412
if self.properties.get(property_name) != old_value:
386
old_values = {key: self.properties.get(key)
387
for key in properties.keys()}
388
super(MandosClientWidget, self).properties_changed(
389
interface, properties, invalidated)
390
if any(old_values[key] != self.properties.get(key)
391
for key in old_values):
395
def glib_safely(func, retval=True):
396
def safe_func(*args, **kwargs):
398
return func(*args, **kwargs)
416
405
class ConstrainedListBox(urwid.ListBox):
417
406
"""Like a normal urwid.ListBox, but will consume all "up" or
418
407
"down" key presses, thus not allowing any containing widgets to
419
408
use them as an excuse to shift focus away from this widget.
421
def keypress(self, maxcolrow, key):
422
ret = super(ConstrainedListBox, self).keypress(maxcolrow, key)
410
def keypress(self, *args, **kwargs):
411
ret = (super(ConstrainedListBox, self)
412
.keypress(*args, **kwargs))
423
413
if ret in ("up", "down"):
428
class UserInterface(object):
429
419
"""This is the entire user interface - the whole screen
430
420
with boxes, lists of client widgets, etc.
432
422
def __init__(self, max_log_length=1000):
433
423
DBusGMainLoop(set_as_default=True)
435
425
self.screen = urwid.curses_display.Screen()
437
427
self.screen.register_palette((
439
429
"default", "default", None),
441
"default", "default", "bold"),
431
"bold", "default", "bold"),
442
432
("underline-blink",
443
"default", "default", "underline"),
433
"underline,blink", "default", "underline,blink"),
445
"default", "default", "standout"),
435
"standout", "default", "standout"),
446
436
("bold-underline-blink",
447
"default", "default", ("bold", "underline")),
437
"bold,underline,blink", "default",
438
"bold,underline,blink"),
448
439
("bold-standout",
449
"default", "default", ("bold", "standout")),
440
"bold,standout", "default", "bold,standout"),
450
441
("underline-blink-standout",
451
"default", "default", ("underline", "standout")),
442
"underline,blink,standout", "default",
443
"underline,blink,standout"),
452
444
("bold-underline-blink-standout",
453
"default", "default", ("bold", "underline",
445
"bold,underline,blink,standout", "default",
446
"bold,underline,blink,standout"),
457
449
if urwid.supports_unicode():
458
self.divider = "─" # \u2500
459
#self.divider = "━" # \u2501
450
self.divider = "─" # \u2500
461
#self.divider = "-" # \u002d
462
self.divider = "_" # \u005f
452
self.divider = "_" # \u005f
464
454
self.screen.start()
466
456
self.size = self.screen.get_cols_rows()
468
458
self.clients = urwid.SimpleListWalker([])
469
459
self.clients_dict = {}
471
461
# We will add Text widgets to this list
462
self.log = urwid.SimpleListWalker([])
473
463
self.max_log_length = max_log_length
475
465
# We keep a reference to the log widget so we can remove it
476
466
# from the ListWalker without it getting destroyed
477
467
self.logbox = ConstrainedListBox(self.log)
479
469
# This keeps track of whether self.uilist currently has
480
470
# self.logbox in it or not
481
471
self.log_visible = True
482
472
self.log_wrap = "any"
474
self.loghandler = UILogHandler(self)
485
self.log_message_raw(("bold",
486
"Mandos Monitor version " + version))
487
self.log_message_raw(("bold",
490
self.busname = domain + '.Mandos'
491
self.main_loop = gobject.MainLoop()
493
def client_not_found(self, fingerprint, address):
494
self.log_message("Client with address {0} and fingerprint"
495
" {1} could not be found"
496
.format(address, fingerprint))
477
self.add_log_line(("bold",
478
"Mandos Monitor version " + version))
479
self.add_log_line(("bold", "q: Quit ?: Help"))
481
self.busname = domain + ".Mandos"
482
self.main_loop = GLib.MainLoop()
484
def client_not_found(self, key_id, address):
485
log.info("Client with address %s and key ID %s could"
486
" not be found", address, key_id)
498
488
def rebuild(self):
499
489
"""This rebuilds the User Interface.
500
490
Call this when the widget layout needs to change"""
502
#self.uilist.append(urwid.ListBox(self.clients))
492
# self.uilist.append(urwid.ListBox(self.clients))
503
493
self.uilist.append(urwid.Frame(ConstrainedListBox(self.
505
#header=urwid.Divider(),
495
# header=urwid.Divider(),
508
urwid.Divider(div_char=
497
footer=urwid.Divider(
498
div_char=self.divider)))
510
499
if self.log_visible:
511
500
self.uilist.append(self.logbox)
512
501
self.topwidget = urwid.Pile(self.uilist)
514
def log_message(self, message):
515
timestamp = datetime.datetime.now().isoformat()
516
self.log_message_raw(timestamp + ": " + message)
518
def log_message_raw(self, markup):
519
"""Add a log message to the log buffer."""
503
def add_log_line(self, markup):
520
504
self.log.append(urwid.Text(markup, wrap=self.log_wrap))
521
if (self.max_log_length
522
and len(self.log) > self.max_log_length):
523
del self.log[0:len(self.log)-self.max_log_length-1]
524
self.logbox.set_focus(len(self.logbox.body.contents),
505
if self.max_log_length:
506
if len(self.log) > self.max_log_length:
507
del self.log[0:(len(self.log) - self.max_log_length)]
508
self.logbox.set_focus(len(self.logbox.body.contents)-1,
525
509
coming_from="above")
528
512
def toggle_log_display(self):
529
513
"""Toggle visibility of the log buffer."""
530
514
self.log_visible = not self.log_visible
532
#self.log_message("Log visibility changed to: "
533
# + unicode(self.log_visible))
516
log.debug("Log visibility changed to: %s", self.log_visible)
535
518
def change_log_display(self):
536
519
"""Change type of log display.
537
520
Currently, this toggles wrapping of text lines."""
541
524
self.log_wrap = "clip"
542
525
for textwidget in self.log:
543
526
textwidget.set_wrap_mode(self.log_wrap)
544
#self.log_message("Wrap mode: " + self.log_wrap)
546
def find_and_remove_client(self, path, name):
527
log.debug("Wrap mode: %s", self.log_wrap)
529
def find_and_remove_client(self, path, interfaces):
547
530
"""Find a client by its object path and remove it.
549
This is connected to the ClientRemoved signal from the
532
This is connected to the InterfacesRemoved signal from the
550
533
Mandos server object."""
534
if client_interface not in interfaces:
535
# Not a Mandos client object; ignore
552
538
client = self.clients_dict[path]
555
self.log_message("Unknown client {0!r} ({1!r}) removed"
541
log.warning("Unknown client %s removed", path)
560
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
561
554
client_proxy_object = self.bus.get_object(self.busname, path)
562
self.add_client(MandosClientWidget(server_proxy_object
565
=client_proxy_object,
555
self.add_client(MandosClientWidget(
556
server_proxy_object=self.mandos_serv,
557
proxy_object=client_proxy_object,
558
update_hook=self.refresh,
559
delete_hook=self.remove_client,
560
properties=dict(ifs_and_props[client_interface])),
574
563
def add_client(self, client, path=None):
575
564
self.clients.append(client)
577
566
path = client.proxy.object_path
578
567
self.clients_dict[path] = client
579
self.clients.sort(None, lambda c: c.properties["Name"])
568
self.clients.sort(key=lambda c: c.properties["Name"])
582
571
def remove_client(self, client, path=None):
583
572
self.clients.remove(client)
585
574
path = client.proxy.object_path
586
575
del self.clients_dict[path]
587
if not self.clients_dict:
588
# Work around bug in Urwid 0.9.8.3 - if a SimpleListWalker
589
# is completely emptied, we need to recreate it.
590
self.clients = urwid.SimpleListWalker([])
594
578
def refresh(self):
595
579
"""Redraw the screen"""
596
580
canvas = self.topwidget.render(self.size, focus=True)
597
581
self.screen.draw_screen(self.size, canvas)
600
584
"""Start the main loop and exit when it's done."""
585
log.addHandler(self.loghandler)
586
self.orig_log_propagate = log.propagate
587
log.propagate = False
588
self.orig_log_level = log.level
601
590
self.bus = dbus.SystemBus()
602
591
mandos_dbus_objc = self.bus.get_object(
603
592
self.busname, "/", follow_name_owner_changes=True)
604
self.mandos_serv = dbus.Interface(mandos_dbus_objc,
593
self.mandos_serv = dbus.Interface(
594
mandos_dbus_objc, dbus_interface=server_interface)
608
596
mandos_clients = (self.mandos_serv
609
597
.GetAllClientsWithProperties())
598
if not mandos_clients:
599
log.warning("Note: Server has no clients.")
610
600
except dbus.exceptions.DBusException:
601
log.warning("Note: No Mandos server running.")
611
602
mandos_clients = dbus.Dictionary()
613
604
(self.mandos_serv
614
.connect_to_signal("ClientRemoved",
605
.connect_to_signal("InterfacesRemoved",
615
606
self.find_and_remove_client,
616
dbus_interface=server_interface,
607
dbus_interface=dbus.OBJECT_MANAGER_IFACE,
617
608
byte_arrays=True))
618
609
(self.mandos_serv
619
.connect_to_signal("ClientAdded",
610
.connect_to_signal("InterfacesAdded",
620
611
self.add_new_client,
621
dbus_interface=server_interface,
612
dbus_interface=dbus.OBJECT_MANAGER_IFACE,
622
613
byte_arrays=True))
623
614
(self.mandos_serv
624
615
.connect_to_signal("ClientNotFound",
625
616
self.client_not_found,
626
617
dbus_interface=server_interface,
627
618
byte_arrays=True))
628
for path, client in mandos_clients.iteritems():
619
for path, client in mandos_clients.items():
629
620
client_proxy_object = self.bus.get_object(self.busname,
631
self.add_client(MandosClientWidget(server_proxy_object
634
=client_proxy_object,
622
self.add_client(MandosClientWidget(
623
server_proxy_object=self.mandos_serv,
624
proxy_object=client_proxy_object,
626
update_hook=self.refresh,
627
delete_hook=self.remove_client),
645
self._input_callback_tag = (gobject.io_add_watch
631
self._input_callback_tag = (
633
GLib.IOChannel.unix_new(sys.stdin.fileno()),
634
GLib.PRIORITY_DEFAULT, GLib.IO_IN,
635
glib_safely(self.process_input)))
649
636
self.main_loop.run()
650
637
# Main loop has finished, we should close everything now
651
gobject.source_remove(self._input_callback_tag)
638
GLib.source_remove(self._input_callback_tag)
639
with warnings.catch_warnings():
640
warnings.simplefilter("ignore", BytesWarning)
655
644
self.main_loop.quit()
645
log.removeHandler(self.loghandler)
646
log.propagate = self.orig_log_propagate
657
648
def process_input(self, source, condition):
658
649
keys = self.screen.get_input()
659
translations = { "ctrl n": "down", # Emacs
660
"ctrl p": "up", # Emacs
661
"ctrl v": "page down", # Emacs
662
"meta v": "page up", # Emacs
663
" ": "page down", # less
664
"f": "page down", # less
665
"b": "page up", # less
650
translations = {"ctrl n": "down", # Emacs
651
"ctrl p": "up", # Emacs
652
"ctrl v": "page down", # Emacs
653
"meta v": "page up", # Emacs
654
" ": "page down", # less
655
"f": "page down", # less
656
"b": "page up", # less
671
662
key = translations[key]
672
663
except KeyError: # :-)
675
666
if key == "q" or key == "Q":
678
669
elif key == "window resize":
679
670
self.size = self.screen.get_cols_rows()
681
elif key == "\f": # Ctrl-L
672
elif key == "ctrl l":
683
675
elif key == "l" or key == "D":
684
676
self.toggle_log_display()
716
709
self.topwidget.set_focus(self.logbox)
718
#elif (key == "end" or key == "meta >" or key == "G"
720
# pass # xxx end-of-buffer
721
#elif (key == "home" or key == "meta <" or key == "g"
723
# pass # xxx beginning-of-buffer
724
#elif key == "ctrl e" or key == "$":
725
# pass # xxx move-end-of-line
726
#elif key == "ctrl a" or key == "^":
727
# pass # xxx move-beginning-of-line
728
#elif key == "ctrl b" or key == "meta (" or key == "h":
730
#elif key == "ctrl f" or key == "meta )" or key == "l":
733
# pass # scroll up log
735
# pass # scroll down log
712
if log.level < logging.INFO:
713
log.setLevel(logging.INFO)
714
log.info("Verbose mode: Off")
716
log.setLevel(logging.NOTSET)
717
log.info("Verbose mode: On")
718
# elif (key == "end" or key == "meta >" or key == "G"
720
# pass # xxx end-of-buffer
721
# elif (key == "home" or key == "meta <" or key == "g"
723
# pass # xxx beginning-of-buffer
724
# elif key == "ctrl e" or key == "$":
725
# pass # xxx move-end-of-line
726
# elif key == "ctrl a" or key == "^":
727
# pass # xxx move-beginning-of-line
728
# elif key == "ctrl b" or key == "meta (" or key == "h":
730
# elif key == "ctrl f" or key == "meta )" or key == "l":
733
# pass # scroll up log
735
# pass # scroll down log
736
736
elif self.topwidget.selectable():
737
737
self.topwidget.keypress(self.size, key)
742
class UILogHandler(logging.Handler):
743
def __init__(self, ui, *args, **kwargs):
745
super(UILogHandler, self).__init__(*args, **kwargs)
747
logging.Formatter("%(asctime)s: %(message)s"))
748
def emit(self, record):
749
msg = self.format(record)
750
if record.levelno > logging.INFO:
752
self.ui.add_log_line(msg)
741
755
ui = UserInterface()
744
758
except KeyboardInterrupt:
747
ui.log_message(unicode(e))
759
with warnings.catch_warnings():
760
warnings.filterwarnings("ignore", "", BytesWarning)
763
with warnings.catch_warnings():
764
warnings.filterwarnings("ignore", "", BytesWarning)