/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk
1185 by Teddy Hogeborn
Add -I option to python 3 on the interpreter script ("shebang") line
1
#!/usr/bin/python3 -bbI
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
2
# -*- mode: python; coding: utf-8 -*-
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
3
#
444 by Teddy Hogeborn
Update copyright year to "2010" wherever appropriate.
4
# Mandos Monitor - Control and monitor the Mandos server
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
5
#
969 by Teddy Hogeborn
Update copyright year to 2019
6
# Copyright © 2009-2019 Teddy Hogeborn
7
# Copyright © 2009-2019 Björn Påhlsson
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
8
#
907 by Teddy Hogeborn
Alter copyright notices slightly. Actual license is unchanged!
9
# This file is part of Mandos.
10
#
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
444 by Teddy Hogeborn
Update copyright year to "2010" wherever appropriate.
13
# the Free Software Foundation, either version 3 of the License, or
14
# (at your option) any later version.
15
#
907 by Teddy Hogeborn
Alter copyright notices slightly. Actual license is unchanged!
16
#     Mandos is distributed in the hope that it will be useful, but
17
#     WITHOUT ANY WARRANTY; without even the implied warranty of
444 by Teddy Hogeborn
Update copyright year to "2010" wherever appropriate.
18
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
#     GNU General Public License for more details.
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
20
#
444 by Teddy Hogeborn
Update copyright year to "2010" wherever appropriate.
21
# You should have received a copy of the GNU General Public License
907 by Teddy Hogeborn
Alter copyright notices slightly. Actual license is unchanged!
22
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
23
#
505.1.2 by Teddy Hogeborn
Change "fukt.bsnet.se" to "recompile.se" throughout.
24
# Contact the authors at <mandos@recompile.se>.
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
25
#
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
26
488 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
27
from __future__ import (division, absolute_import, print_function,
28
                        unicode_literals)
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
29
try:
30
    from future_builtins import *
31
except ImportError:
32
    pass
579 by Teddy Hogeborn
* mandos: Use all new builtins.
33
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
34
import sys
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
35
import os
1174 by Teddy Hogeborn
Address Python 3 bytes/str warnings
36
import warnings
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
37
import datetime
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
38
import locale
39
import logging
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
40
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
41
import urwid.curses_display
42
import urwid
43
44
from dbus.mainloop.glib import DBusGMainLoop
830 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
45
from gi.repository import GLib
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
46
47
import dbus
48
723.1.7 by Teddy Hogeborn
Use the .major attribute on sys.version_info instead of using "[0]".
49
if sys.version_info.major == 2:
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
50
    str = unicode
51
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
52
log = logging.getLogger(os.path.basename(sys.argv[0]))
53
logging.basicConfig(level="NOTSET", # Show all messages
54
                    format="%(message)s") # Show basic log messages
55
56
logging.captureWarnings(True)   # Show warnings via the logging system
57
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
58
locale.setlocale(locale.LC_ALL, "")
411 by Teddy Hogeborn
More consistent terminology: Clients are no longer "invalid" - they
59
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
60
logging.getLogger("dbus.proxies").setLevel(logging.CRITICAL)
24.1.153 by Björn Påhlsson
early commit to ease todays coding
61
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
62
# Some useful constants
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
63
domain = "se.recompile"
64
server_interface = domain + ".Mandos"
65
client_interface = domain + ".Mandos.Client"
237.4.124 by Teddy Hogeborn
* Makefile (version): Change to 1.8.9.
66
version = "1.8.9"
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
67
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
68
try:
69
    dbus.OBJECT_MANAGER_IFACE
70
except AttributeError:
71
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
72
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
73
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
74
def isoformat_to_datetime(iso):
75
    "Parse an ISO 8601 date string to a datetime.datetime()"
76
    if not iso:
77
        return None
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
78
    d, t = iso.split("T", 1)
79
    year, month, day = d.split("-", 2)
80
    hour, minute, second = t.split(":", 2)
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
81
    second, fraction = divmod(float(second), 1)
82
    return datetime.datetime(int(year),
83
                             int(month),
84
                             int(day),
85
                             int(hour),
86
                             int(minute),
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
87
                             int(second),            # Whole seconds
88
                             int(fraction*1000000))  # Microseconds
89
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
90
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
91
class MandosClientPropertyCache(object):
92
    """This wraps a Mandos Client D-Bus proxy object, caches the
93
    properties and calls a hook function when any of them are
94
    changed.
95
    """
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
96
    def __init__(self, proxy_object=None, properties=None, **kwargs):
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
97
        self.proxy = proxy_object  # Mandos Client proxy object
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
98
        self.properties = dict() if properties is None else properties
488 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
99
        self.property_changed_match = (
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
100
            self.proxy.connect_to_signal("PropertiesChanged",
101
                                         self.properties_changed,
102
                                         dbus.PROPERTIES_IFACE,
488 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
103
                                         byte_arrays=True))
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
104
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
105
        if properties is None:
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
106
            self.properties.update(self.proxy.GetAll(
107
                client_interface,
108
                dbus_interface=dbus.PROPERTIES_IFACE))
109
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
110
        super(MandosClientPropertyCache, self).__init__(**kwargs)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
111
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
112
    def properties_changed(self, interface, properties, invalidated):
113
        """This is called whenever we get a PropertiesChanged signal
114
        It updates the changed properties in the "properties" dict.
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
115
        """
116
        # Update properties dict with new value
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
117
        if interface == client_interface:
118
            self.properties.update(properties)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
119
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
120
    def delete(self):
488 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
121
        self.property_changed_match.remove()
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
122
123
124
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
125
    """A Mandos Client which is visible on the screen.
126
    """
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
127
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
128
    def __init__(self, server_proxy_object=None, update_hook=None,
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
129
                 delete_hook=None, **kwargs):
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
130
        # Called on update
131
        self.update_hook = update_hook
132
        # Called on delete
133
        self.delete_hook = delete_hook
134
        # Mandos Server proxy object
135
        self.server_proxy_object = server_proxy_object
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
136
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
137
        self._update_timer_callback_tag = None
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
138
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
139
        # The widget shown normally
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
140
        self._text_widget = urwid.Text("")
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
141
        # The widget shown when we have focus
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
142
        self._focus_text_widget = urwid.Text("")
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
143
        super(MandosClientWidget, self).__init__(**kwargs)
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
144
        self.update()
145
        self.opened = False
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
146
488 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
147
        self.match_objects = (
148
            self.proxy.connect_to_signal("CheckerCompleted",
149
                                         self.checker_completed,
150
                                         client_interface,
151
                                         byte_arrays=True),
152
            self.proxy.connect_to_signal("CheckerStarted",
153
                                         self.checker_started,
154
                                         client_interface,
155
                                         byte_arrays=True),
156
            self.proxy.connect_to_signal("GotSecret",
157
                                         self.got_secret,
158
                                         client_interface,
159
                                         byte_arrays=True),
160
            self.proxy.connect_to_signal("NeedApproval",
161
                                         self.need_approval,
162
                                         client_interface,
163
                                         byte_arrays=True),
164
            self.proxy.connect_to_signal("Rejected",
165
                                         self.rejected,
166
                                         client_interface,
167
                                         byte_arrays=True))
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
168
        log.debug("Created client %s", self.properties["Name"])
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
169
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
170
    def using_timer(self, flag):
171
        """Call this method with True or False when timer should be
172
        activated or deactivated.
173
        """
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
174
        if flag and self._update_timer_callback_tag is None:
24.1.179 by Björn Påhlsson
New feature:
175
            # Will update the shown timer value every second
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
176
            self._update_timer_callback_tag = (
177
                GLib.timeout_add(1000,
178
                                 glib_safely(self.update_timer)))
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
179
        elif not (flag or self._update_timer_callback_tag is None):
830 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
180
            GLib.source_remove(self._update_timer_callback_tag)
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
181
            self._update_timer_callback_tag = None
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
182
783 by Teddy Hogeborn
Revert change to D-Bus API.
183
    def checker_completed(self, exitstatus, condition, command):
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
184
        if exitstatus == 0:
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
185
            log.debug('Checker for client %s (command "%s")'
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
186
                      " succeeded", self.properties["Name"], command)
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
187
            self.update()
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
188
            return
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
189
        # Checker failed
783 by Teddy Hogeborn
Revert change to D-Bus API.
190
        if os.WIFEXITED(condition):
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
191
            log.info('Checker for client %s (command "%s") failed'
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
192
                     " with exit code %d", self.properties["Name"],
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
193
                     command, os.WEXITSTATUS(condition))
783 by Teddy Hogeborn
Revert change to D-Bus API.
194
        elif os.WIFSIGNALED(condition):
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
195
            log.info('Checker for client %s (command "%s") was'
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
196
                     " killed by signal %d", self.properties["Name"],
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
197
                     command, os.WTERMSIG(condition))
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
198
        self.update()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
199
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
200
    def checker_started(self, command):
713 by Teddy Hogeborn
mandos-monitor: New "verbose" mode to see less important log messages.
201
        """Server signals that a checker started."""
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
202
        log.debug('Client %s started checker "%s"',
203
                  self.properties["Name"], command)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
204
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
205
    def got_secret(self):
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
206
        log.info("Client %s received its secret",
207
                 self.properties["Name"])
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
208
24.1.153 by Björn Påhlsson
early commit to ease todays coding
209
    def need_approval(self, timeout, default):
210
        if not default:
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
211
            message = "Client %s needs approval within %f seconds"
24.1.153 by Björn Påhlsson
early commit to ease todays coding
212
        else:
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
213
            message = "Client %s will get its secret in %f seconds"
214
        log.info(message, self.properties["Name"], timeout/1000)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
215
24.1.153 by Björn Påhlsson
early commit to ease todays coding
216
    def rejected(self, reason):
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
217
        log.info("Client %s was rejected; reason: %s",
218
                 self.properties["Name"], reason)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
219
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
220
    def selectable(self):
221
        """Make this a "selectable" widget.
222
        This overrides the method from urwid.FlowWidget."""
223
        return True
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
224
463.1.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
225
    def rows(self, maxcolrow, focus=False):
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
226
        """How many rows this widget will occupy might depend on
227
        whether we have focus or not.
228
        This overrides the method from urwid.FlowWidget"""
463.1.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
229
        return self.current_widget(focus).rows(maxcolrow, focus=focus)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
230
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
231
    def current_widget(self, focus=False):
232
        if focus or self.opened:
233
            return self._focus_widget
234
        return self._widget
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
235
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
236
    def update(self):
237
        "Called when what is visible on the screen should be updated."
238
        # How to add standout mode to a style
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
239
        with_standout = {"normal": "standout",
240
                         "bold": "bold-standout",
241
                         "underline-blink":
242
                         "underline-blink-standout",
243
                         "bold-underline-blink":
244
                         "bold-underline-blink-standout",
245
                         }
246
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
247
        # Rebuild focus and non-focus widgets using current properties
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
248
24.1.154 by Björn Påhlsson
merge
249
        # Base part of a client. Name!
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
250
        base = "{name}: ".format(name=self.properties["Name"])
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
251
        if not self.properties["Enabled"]:
252
            message = "DISABLED"
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
253
            self.using_timer(False)
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
254
        elif self.properties["ApprovalPending"]:
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
255
            timeout = datetime.timedelta(
256
                milliseconds=self.properties["ApprovalDelay"])
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
257
            last_approval_request = isoformat_to_datetime(
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
258
                self.properties["LastApprovalRequest"])
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
259
            if last_approval_request is not None:
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
260
                timer = max(timeout - (datetime.datetime.utcnow()
261
                                       - last_approval_request),
262
                            datetime.timedelta())
442 by Teddy Hogeborn
* DBUS-API: Document new "LastApprovalRequest" client property.
263
            else:
264
                timer = datetime.timedelta()
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
265
            if self.properties["ApprovedByDefault"]:
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
266
                message = "Approval in {}. (d)eny?"
24.1.159 by Björn Påhlsson
added approval to mandos-ctl
267
            else:
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
268
                message = "Denial in {}. (a)pprove?"
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
269
            message = message.format(str(timer).rsplit(".", 1)[0])
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
270
            self.using_timer(True)
557 by Teddy Hogeborn
Use the new Client.LastCheckerStatus property.
271
        elif self.properties["LastCheckerStatus"] != 0:
572 by Teddy Hogeborn
* mandos-ctl: Break long lines.
272
            # When checker has failed, show timer until client expires
24.1.179 by Björn Påhlsson
New feature:
273
            expires = self.properties["Expires"]
274
            if expires == "":
275
                timer = datetime.timedelta(0)
276
            else:
572 by Teddy Hogeborn
* mandos-ctl: Break long lines.
277
                expires = (datetime.datetime.strptime
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
278
                           (expires, "%Y-%m-%dT%H:%M:%S.%f"))
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
279
                timer = max(expires - datetime.datetime.utcnow(),
280
                            datetime.timedelta())
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
281
            message = ("A checker has failed! Time until client"
282
                       " gets disabled: {}"
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
283
                       .format(str(timer).rsplit(".", 1)[0]))
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
284
            self.using_timer(True)
24.1.154 by Björn Påhlsson
merge
285
        else:
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
286
            message = "enabled"
581 by Teddy Hogeborn
* mandos (Client.enable, Client.disable, ClientDBus.approve): Call
287
            self.using_timer(False)
723.1.5 by Teddy Hogeborn
Use the new auto-numbered "{}" syntax for the .format() string method.
288
        self._text = "{}{}".format(base, message)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
289
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
290
        if not urwid.supports_unicode():
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
291
            self._text = self._text.encode("ascii", "replace")
292
        textlist = [("normal", self._text)]
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
293
        self._text_widget.set_text(textlist)
294
        self._focus_text_widget.set_text([(with_standout[text[0]],
295
                                           text[1])
296
                                          if isinstance(text, tuple)
297
                                          else text
298
                                          for text in textlist])
299
        self._widget = self._text_widget
300
        self._focus_widget = urwid.AttrWrap(self._focus_text_widget,
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
301
                                            "standout")
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
302
        # Run update hook, if any
303
        if self.update_hook is not None:
304
            self.update_hook()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
305
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
306
    def update_timer(self):
830 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
307
        """called by GLib. Will indefinitely loop until
308
        GLib.source_remove() on tag is called
309
        """
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
310
        self.update()
311
        return True             # Keep calling this
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
312
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
313
    def delete(self, **kwargs):
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
314
        if self._update_timer_callback_tag is not None:
830 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
315
            GLib.source_remove(self._update_timer_callback_tag)
417 by Teddy Hogeborn
* mandos (AvahiService.entry_group_state_changed): Better debug log
316
            self._update_timer_callback_tag = None
488 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
317
        for match in self.match_objects:
318
            match.remove()
319
        self.match_objects = ()
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
320
        if self.delete_hook is not None:
321
            self.delete_hook(self)
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
322
        return super(MandosClientWidget, self).delete(**kwargs)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
323
463.1.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
324
    def render(self, maxcolrow, focus=False):
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
325
        """Render differently if we have focus.
326
        This overrides the method from urwid.FlowWidget"""
463.1.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
327
        return self.current_widget(focus).render(maxcolrow,
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
328
                                                 focus=focus)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
329
463.1.1 by teddy at bsnet
* mandos-monitor: Use only unicode string literals.
330
    def keypress(self, maxcolrow, key):
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
331
        """Handle keys.
332
        This overrides the method from urwid.FlowWidget"""
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
333
        if key == "+":
781 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
334
            self.proxy.Set(client_interface, "Enabled",
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
335
                           dbus.Boolean(True), ignore_reply=True,
336
                           dbus_interface=dbus.PROPERTIES_IFACE)
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
337
        elif key == "-":
781 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
338
            self.proxy.Set(client_interface, "Enabled", False,
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
339
                           ignore_reply=True,
340
                           dbus_interface=dbus.PROPERTIES_IFACE)
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
341
        elif key == "a":
24.1.154 by Björn Påhlsson
merge
342
            self.proxy.Approve(dbus.Boolean(True, variant_level=1),
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
343
                               dbus_interface=client_interface,
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
344
                               ignore_reply=True)
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
345
        elif key == "d":
24.1.154 by Björn Påhlsson
merge
346
            self.proxy.Approve(dbus.Boolean(False, variant_level=1),
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
347
                               dbus_interface=client_interface,
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
348
                               ignore_reply=True)
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
349
        elif key == "R" or key == "_" or key == "ctrl k":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
350
            self.server_proxy_object.RemoveClient(self.proxy
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
351
                                                  .object_path,
352
                                                  ignore_reply=True)
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
353
        elif key == "s":
781 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
354
            self.proxy.Set(client_interface, "CheckerRunning",
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
355
                           dbus.Boolean(True), ignore_reply=True,
356
                           dbus_interface=dbus.PROPERTIES_IFACE)
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
357
        elif key == "S":
781 by Teddy Hogeborn
Deprecate some D-Bus methods in favor of D-Bus properties.
358
            self.proxy.Set(client_interface, "CheckerRunning",
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
359
                           dbus.Boolean(False), ignore_reply=True,
360
                           dbus_interface=dbus.PROPERTIES_IFACE)
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
361
        elif key == "C":
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
362
            self.proxy.CheckedOK(dbus_interface=client_interface,
482 by Teddy Hogeborn
* mandos: Tolerate restarting Avahi servers. Also Changed to new
363
                                 ignore_reply=True)
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
364
        # xxx
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
365
#         elif key == "p" or key == "=":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
366
#             self.proxy.pause()
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
367
#         elif key == "u" or key == ":":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
368
#             self.proxy.unpause()
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
369
#         elif key == "RET":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
370
#             self.open()
371
        else:
372
            return key
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
373
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
374
    def properties_changed(self, interface, properties, invalidated):
375
        """Call self.update() if any properties changed.
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
376
        This overrides the method from MandosClientPropertyCache"""
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
377
        old_values = {key: self.properties.get(key)
378
                      for key in properties.keys()}
732 by Teddy Hogeborn
Emit D-Bus "org.freedesktop.DBus.Properties.PropertiesChanged" signal.
379
        super(MandosClientWidget, self).properties_changed(
380
            interface, properties, invalidated)
381
        if any(old_values[key] != self.properties.get(key)
382
               for key in old_values):
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
383
            self.update()
384
385
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
386
def glib_safely(func, retval=True):
387
    def safe_func(*args, **kwargs):
388
        try:
389
            return func(*args, **kwargs)
390
        except Exception:
391
            log.exception("")
392
            return retval
393
    return safe_func
394
395
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
396
class ConstrainedListBox(urwid.ListBox):
397
    """Like a normal urwid.ListBox, but will consume all "up" or
398
    "down" key presses, thus not allowing any containing widgets to
399
    use them as an excuse to shift focus away from this widget.
400
    """
580 by Teddy Hogeborn
* mandos-monitor: Speedup: Use properties from D-Bus
401
    def keypress(self, *args, **kwargs):
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
402
        ret = (super(ConstrainedListBox, self)
403
               .keypress(*args, **kwargs))
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
404
        if ret in ("up", "down"):
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
405
            return
406
        return ret
407
408
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
409
class UserInterface(object):
410
    """This is the entire user interface - the whole screen
411
    with boxes, lists of client widgets, etc.
412
    """
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
413
    def __init__(self, max_log_length=1000):
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
414
        DBusGMainLoop(set_as_default=True)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
415
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
416
        self.screen = urwid.curses_display.Screen()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
417
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
418
        self.screen.register_palette((
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
419
                ("normal",
420
                 "default", "default", None),
421
                ("bold",
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
422
                 "bold", "default", "bold"),
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
423
                ("underline-blink",
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
424
                 "underline,blink", "default", "underline,blink"),
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
425
                ("standout",
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
426
                 "standout", "default", "standout"),
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
427
                ("bold-underline-blink",
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
428
                 "bold,underline,blink", "default",
429
                 "bold,underline,blink"),
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
430
                ("bold-standout",
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
431
                 "bold,standout", "default", "bold,standout"),
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
432
                ("underline-blink-standout",
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
433
                 "underline,blink,standout", "default",
434
                 "underline,blink,standout"),
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
435
                ("bold-underline-blink-standout",
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
436
                 "bold,underline,blink,standout", "default",
437
                 "bold,underline,blink,standout"),
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
438
                ))
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
439
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
440
        if urwid.supports_unicode():
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
441
            self.divider = "─"  # \u2500
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
442
        else:
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
443
            self.divider = "_"  # \u005f
444
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
445
        self.screen.start()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
446
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
447
        self.size = self.screen.get_cols_rows()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
448
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
449
        self.clients = urwid.SimpleListWalker([])
450
        self.clients_dict = {}
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
451
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
452
        # We will add Text widgets to this list
968 by Teddy Hogeborn
Do minor fix to make mandos-monitor work with new python-urwid
453
        self.log = urwid.SimpleListWalker([])
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
454
        self.max_log_length = max_log_length
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
455
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
456
        # We keep a reference to the log widget so we can remove it
457
        # from the ListWalker without it getting destroyed
458
        self.logbox = ConstrainedListBox(self.log)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
459
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
460
        # This keeps track of whether self.uilist currently has
461
        # self.logbox in it or not
462
        self.log_visible = True
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
463
        self.log_wrap = "any"
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
464
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
465
        self.loghandler = UILogHandler(self)
466
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
467
        self.rebuild()
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
468
        self.add_log_line(("bold",
469
                           "Mandos Monitor version " + version))
470
        self.add_log_line(("bold", "q: Quit  ?: Help"))
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
471
1184 by Teddy Hogeborn
mandos-monitor: Formatting changes only
472
        self.busname = domain + ".Mandos"
830 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
473
        self.main_loop = GLib.MainLoop()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
474
1175 by Teddy Hogeborn
mandos-monitor: Update message from "fingerprint" to "key ID"
475
    def client_not_found(self, key_id, address):
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
476
        log.info("Client with address %s and key ID %s could"
477
                 " not be found", address, key_id)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
478
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
479
    def rebuild(self):
480
        """This rebuilds the User Interface.
481
        Call this when the widget layout needs to change"""
482
        self.uilist = []
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
483
        # self.uilist.append(urwid.ListBox(self.clients))
422 by Teddy Hogeborn
Rename all D-Bus properties to conform to D-Bus naming conventions;
484
        self.uilist.append(urwid.Frame(ConstrainedListBox(self.
485
                                                          clients),
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
486
                                       # header=urwid.Divider(),
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
487
                                       header=None,
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
488
                                       footer=urwid.Divider(
489
                                           div_char=self.divider)))
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
490
        if self.log_visible:
491
            self.uilist.append(self.logbox)
492
        self.topwidget = urwid.Pile(self.uilist)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
493
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
494
    def add_log_line(self, markup):
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
495
        self.log.append(urwid.Text(markup, wrap=self.log_wrap))
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
496
        if self.max_log_length:
497
            if len(self.log) > self.max_log_length:
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
498
                del self.log[0:(len(self.log) - self.max_log_length)]
968 by Teddy Hogeborn
Do minor fix to make mandos-monitor work with new python-urwid
499
        self.logbox.set_focus(len(self.logbox.body.contents)-1,
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
500
                              coming_from="above")
409 by Teddy Hogeborn
* mandos (MandosServer.handle_ipc): Better log message.
501
        self.refresh()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
502
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
503
    def toggle_log_display(self):
504
        """Toggle visibility of the log buffer."""
505
        self.log_visible = not self.log_visible
506
        self.rebuild()
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
507
        log.debug("Log visibility changed to: %s", self.log_visible)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
508
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
509
    def change_log_display(self):
510
        """Change type of log display.
511
        Currently, this toggles wrapping of text lines."""
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
512
        if self.log_wrap == "clip":
513
            self.log_wrap = "any"
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
514
        else:
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
515
            self.log_wrap = "clip"
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
516
        for textwidget in self.log:
517
            textwidget.set_wrap_mode(self.log_wrap)
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
518
        log.debug("Wrap mode: %s", self.log_wrap)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
519
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
520
    def find_and_remove_client(self, path, interfaces):
488 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
521
        """Find a client by its object path and remove it.
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
522
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
523
        This is connected to the InterfacesRemoved signal from the
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
524
        Mandos server object."""
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
525
        if client_interface not in interfaces:
526
            # Not a Mandos client object; ignore
527
            return
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
528
        try:
529
            client = self.clients_dict[path]
530
        except KeyError:
531
            # not found?
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
532
            log.warning("Unknown client %s removed", path)
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
533
            return
488 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Save match tag for
534
        client.delete()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
535
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
536
    def add_new_client(self, path, ifs_and_props):
537
        """Find a client by its object path and remove it.
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
538
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
539
        This is connected to the InterfacesAdded signal from the
540
        Mandos server object.
541
        """
542
        if client_interface not in ifs_and_props:
543
            # Not a Mandos client object; ignore
544
            return
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
545
        client_proxy_object = self.bus.get_object(self.busname, path)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
546
        self.add_client(MandosClientWidget(
547
            server_proxy_object=self.mandos_serv,
548
            proxy_object=client_proxy_object,
549
            update_hook=self.refresh,
550
            delete_hook=self.remove_client,
551
            properties=dict(ifs_and_props[client_interface])),
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
552
                        path=path)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
553
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
554
    def add_client(self, client, path=None):
555
        self.clients.append(client)
556
        if path is None:
557
            path = client.proxy.object_path
558
        self.clients_dict[path] = client
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
559
        self.clients.sort(key=lambda c: c.properties["Name"])
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
560
        self.refresh()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
561
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
562
    def remove_client(self, client, path=None):
563
        self.clients.remove(client)
564
        if path is None:
565
            path = client.proxy.object_path
566
        del self.clients_dict[path]
567
        self.refresh()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
568
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
569
    def refresh(self):
570
        """Redraw the screen"""
571
        canvas = self.topwidget.render(self.size, focus=True)
572
        self.screen.draw_screen(self.size, canvas)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
573
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
574
    def run(self):
575
        """Start the main loop and exit when it's done."""
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
576
        log.addHandler(self.loghandler)
577
        self.orig_log_propagate = log.propagate
578
        log.propagate = False
579
        self.orig_log_level = log.level
580
        log.setLevel("INFO")
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
581
        self.bus = dbus.SystemBus()
582
        mandos_dbus_objc = self.bus.get_object(
583
            self.busname, "/", follow_name_owner_changes=True)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
584
        self.mandos_serv = dbus.Interface(
585
            mandos_dbus_objc, dbus_interface=server_interface)
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
586
        try:
587
            mandos_clients = (self.mandos_serv
588
                              .GetAllClientsWithProperties())
612 by Björn Påhlsson
added note messages when mandos-monitor starts without running server,
589
            if not mandos_clients:
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
590
                log.warning("Note: Server has no clients.")
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
591
        except dbus.exceptions.DBusException:
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
592
            log.warning("Note: No Mandos server running.")
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
593
            mandos_clients = dbus.Dictionary()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
594
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
595
        (self.mandos_serv
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
596
         .connect_to_signal("InterfacesRemoved",
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
597
                            self.find_and_remove_client,
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
598
                            dbus_interface=dbus.OBJECT_MANAGER_IFACE,
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
599
                            byte_arrays=True))
600
        (self.mandos_serv
785 by Teddy Hogeborn
Support the standard org.freedesktop.DBus.ObjectManager interface.
601
         .connect_to_signal("InterfacesAdded",
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
602
                            self.add_new_client,
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
603
                            dbus_interface=dbus.OBJECT_MANAGER_IFACE,
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
604
                            byte_arrays=True))
605
        (self.mandos_serv
606
         .connect_to_signal("ClientNotFound",
607
                            self.client_not_found,
608
                            dbus_interface=server_interface,
609
                            byte_arrays=True))
617 by Teddy Hogeborn
* mandos: Bug fix: Don't print output from checkers when running in
610
        for path, client in mandos_clients.items():
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
611
            client_proxy_object = self.bus.get_object(self.busname,
612
                                                      path)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
613
            self.add_client(MandosClientWidget(
614
                server_proxy_object=self.mandos_serv,
615
                proxy_object=client_proxy_object,
616
                properties=client,
617
                update_hook=self.refresh,
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
618
                delete_hook=self.remove_client),
537 by Björn Påhlsson
nicer stacktrace when mandos-monitor fail during startup
619
                            path=path)
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
620
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
621
        self.refresh()
1169 by Teddy Hogeborn
mandos-monitor: Use new GLib.io_add_watch() call signature
622
        self._input_callback_tag = (
623
            GLib.io_add_watch(
624
                GLib.IOChannel.unix_new(sys.stdin.fileno()),
625
                GLib.PRIORITY_DEFAULT, GLib.IO_IN,
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
626
                glib_safely(self.process_input)))
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
627
        self.main_loop.run()
628
        # Main loop has finished, we should close everything now
830 by Teddy Hogeborn
Server: Use python-gi instead of old python-gobject
629
        GLib.source_remove(self._input_callback_tag)
1174 by Teddy Hogeborn
Address Python 3 bytes/str warnings
630
        with warnings.catch_warnings():
631
            warnings.simplefilter("ignore", BytesWarning)
632
            self.screen.stop()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
633
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
634
    def stop(self):
635
        self.main_loop.quit()
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
636
        log.removeHandler(self.loghandler)
637
        log.propagate = self.orig_log_propagate
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
638
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
639
    def process_input(self, source, condition):
640
        keys = self.screen.get_input()
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
641
        translations = {"ctrl n": "down",       # Emacs
642
                        "ctrl p": "up",         # Emacs
643
                        "ctrl v": "page down",  # Emacs
644
                        "meta v": "page up",    # Emacs
645
                        " ": "page down",       # less
646
                        "f": "page down",       # less
647
                        "b": "page up",         # less
648
                        "j": "down",            # vi
649
                        "k": "up",              # vi
650
                        }
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
651
        for key in keys:
652
            try:
653
                key = translations[key]
654
            except KeyError:    # :-)
655
                pass
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
656
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
657
            if key == "q" or key == "Q":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
658
                self.stop()
659
                break
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
660
            elif key == "window resize":
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
661
                self.size = self.screen.get_cols_rows()
662
                self.refresh()
722 by Teddy Hogeborn
mandos-monitor: Bug fix: Make Ctrl-L actually redraw the screen.
663
            elif key == "ctrl l":
664
                self.screen.clear()
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
665
                self.refresh()
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
666
            elif key == "l" or key == "D":
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
667
                self.toggle_log_display()
668
                self.refresh()
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
669
            elif key == "w" or key == "i":
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
670
                self.change_log_display()
671
                self.refresh()
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
672
            elif key == "?" or key == "f1" or key == "esc":
407 by Teddy Hogeborn
* mandos-monitor (MandosClientWidget): Change "StopChecker" key to "S"
673
                if not self.log_visible:
674
                    self.log_visible = True
675
                    self.rebuild()
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
676
                self.add_log_line(("bold",
677
                                   "  ".join(("q: Quit",
678
                                              "?: Help",
679
                                              "l: Log window toggle",
680
                                              "TAB: Switch window",
681
                                              "w: Wrap (log lines)",
682
                                              "v: Toggle verbose log",
683
                                   ))))
684
                self.add_log_line(("bold",
685
                                   "  ".join(("Clients:",
686
                                              "+: Enable",
687
                                              "-: Disable",
688
                                              "R: Remove",
689
                                              "s: Start new checker",
690
                                              "S: Stop checker",
691
                                              "C: Checker OK",
692
                                              "a: Approve",
693
                                              "d: Deny",
694
                                   ))))
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
695
                self.refresh()
463.1.2 by teddy at bsnet
* mandos-monitor: Use unicode string literals. Update __future__
696
            elif key == "tab":
405 by Teddy Hogeborn
* mandos-monitor (MandosClientPropertyCache): Remove conversion of
697
                if self.topwidget.get_focus() is self.logbox:
698
                    self.topwidget.set_focus(0)
699
                else:
700
                    self.topwidget.set_focus(self.logbox)
701
                self.refresh()
713 by Teddy Hogeborn
mandos-monitor: New "verbose" mode to see less important log messages.
702
            elif key == "v":
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
703
                if log.level < logging.INFO:
704
                    log.setLevel(logging.INFO)
705
                    log.info("Verbose mode: Off")
713 by Teddy Hogeborn
mandos-monitor: New "verbose" mode to see less important log messages.
706
                else:
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
707
                    log.setLevel(logging.NOTSET)
708
                    log.info("Verbose mode: On")
873 by Teddy Hogeborn
PEP8 compliance: mandos-monitor
709
            # elif (key == "end" or key == "meta >" or key == "G"
710
            #       or key == ">"):
711
            #     pass            # xxx end-of-buffer
712
            # elif (key == "home" or key == "meta <" or key == "g"
713
            #       or key == "<"):
714
            #     pass            # xxx beginning-of-buffer
715
            # elif key == "ctrl e" or key == "$":
716
            #     pass            # xxx move-end-of-line
717
            # elif key == "ctrl a" or key == "^":
718
            #     pass            # xxx move-beginning-of-line
719
            # elif key == "ctrl b" or key == "meta (" or key == "h":
720
            #     pass            # xxx left
721
            # elif key == "ctrl f" or key == "meta )" or key == "l":
722
            #     pass            # xxx right
723
            # elif key == "a":
724
            #     pass            # scroll up log
725
            # elif key == "z":
726
            #     pass            # scroll down log
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
727
            elif self.topwidget.selectable():
728
                self.topwidget.keypress(self.size, key)
729
                self.refresh()
730
        return True
731
888 by Teddy Hogeborn
Add more PEP8 compliance (as per the "pycodestyle" tool).
732
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
733
class UILogHandler(logging.Handler):
734
    def __init__(self, ui, *args, **kwargs):
735
        self.ui = ui
736
        super(UILogHandler, self).__init__(*args, **kwargs)
737
        self.setFormatter(
738
            logging.Formatter("%(asctime)s: %(message)s"))
739
    def emit(self, record):
740
        msg = self.format(record)
741
        if record.levelno > logging.INFO:
742
            msg = ("bold", msg)
743
        self.ui.add_log_line(msg)
744
745
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
746
ui = UserInterface()
747
try:
748
    ui.run()
24.1.159 by Björn Påhlsson
added approval to mandos-ctl
749
except KeyboardInterrupt:
1183 by Teddy Hogeborn
mandos-monitor: Use Python's standard loggging module
750
    with warnings.catch_warnings():
751
        warnings.filterwarnings("ignore", "", BytesWarning)
752
        ui.screen.stop()
753
except Exception:
754
    with warnings.catch_warnings():
755
        warnings.filterwarnings("ignore", "", BytesWarning)
756
        ui.screen.stop()
404 by Teddy Hogeborn
* mandos-monitor: New prototype version of interactive server
757
    raise