/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to mandos-monitor

* mandos (MandosServer.handle_ipc): Better log message.
  (main/MandosDBusService.ClientNotFound): Add "address" argument.
                                           All callers changed.
* mandos-monitor (MandosClientWidget.__init__): Add "logger" argument.
  (MandosClientWidget.checker_completed,
  MandosClientWidget.checker_started, MandosClientWidget.got_secret,
  MandosClientWidget.rejected): New methods, connected to signals.
  (MandosClientWidget.update): Improve display.
  (UserInterface.client_not_found): New method, conneced to signal.
  (UserInterface.log_message): New; log with timestamp.
  (UserInterface.log_message_raw): Same as old "log_message".  Bug
                                  fix; always do "refresh()".

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
import UserList
21
21
 
22
 
import locale
23
 
 
24
 
locale.setlocale(locale.LC_ALL, u'')
25
 
 
26
22
# Some useful constants
27
23
domain = 'se.bsnet.fukt'
28
24
server_interface = domain + '.Mandos'
37
33
urwid.curses_display.curses.A_UNDERLINE |= (
38
34
    urwid.curses_display.curses.A_BLINK)
39
35
 
40
 
def isoformat_to_datetime(iso):
41
 
    "Parse an ISO 8601 date string to a datetime.datetime()"
42
 
    if not iso:
43
 
        return None
44
 
    d, t = iso.split(u"T", 1)
45
 
    year, month, day = d.split(u"-", 2)
46
 
    hour, minute, second = t.split(u":", 2)
47
 
    second, fraction = divmod(float(second), 1)
48
 
    return datetime.datetime(int(year),
49
 
                             int(month),
50
 
                             int(day),
51
 
                             int(hour),
52
 
                             int(minute),
53
 
                             int(second),           # Whole seconds
54
 
                             int(fraction*1000000)) # Microseconds
55
 
 
56
36
class MandosClientPropertyCache(object):
57
37
    """This wraps a Mandos Client D-Bus proxy object, caches the
58
38
    properties and calls a hook function when any of them are
59
39
    changed.
60
40
    """
61
 
    def __init__(self, proxy_object=None, *args, **kwargs):
 
41
    def __init__(self, proxy_object=None, properties=None, *args,
 
42
                 **kwargs):
62
43
        self.proxy = proxy_object # Mandos Client proxy object
63
44
        
64
 
        self.properties = dict()
 
45
        if properties is None:
 
46
            self.properties = dict()
 
47
        else:
 
48
            self.properties = properties
65
49
        self.proxy.connect_to_signal(u"PropertyChanged",
66
50
                                     self.property_changed,
67
51
                                     client_interface,
68
52
                                     byte_arrays=True)
69
53
        
70
 
        self.properties.update(
71
 
            self.proxy.GetAll(client_interface,
72
 
                              dbus_interface = dbus.PROPERTIES_IFACE))
 
54
        if properties is None:
 
55
            self.properties.update(self.proxy.GetAll(client_interface,
 
56
                                                     dbus_interface =
 
57
                                                     dbus.PROPERTIES_IFACE))
73
58
        super(MandosClientPropertyCache, self).__init__(
74
 
            proxy_object=proxy_object, *args, **kwargs)
 
59
            proxy_object=proxy_object,
 
60
            properties=properties, *args, **kwargs)
75
61
    
76
62
    def property_changed(self, property=None, value=None):
77
63
        """This is called whenever we get a PropertyChanged signal
96
82
        # Logger
97
83
        self.logger = logger
98
84
        
99
 
        self._update_timer_callback_tag = None
100
 
        self.last_checker_failed = False
101
 
        
102
85
        # The widget shown normally
103
86
        self._text_widget = urwid.Text(u"")
104
87
        # The widget shown when we have focus
124
107
                                     self.rejected,
125
108
                                     client_interface,
126
109
                                     byte_arrays=True)
127
 
        last_checked_ok = isoformat_to_datetime(self.properties
128
 
                                                ["last_checked_ok"])
129
 
        if last_checked_ok is None:
130
 
            self.last_checker_failed = True
131
 
        else:
132
 
            self.last_checker_failed = ((datetime.datetime.utcnow()
133
 
                                         - last_checked_ok)
134
 
                                        > datetime.timedelta
135
 
                                        (milliseconds=
136
 
                                         self.properties["interval"]))
137
 
        if self.last_checker_failed:
138
 
            self._update_timer_callback_tag = (gobject.timeout_add
139
 
                                               (1000,
140
 
                                                self.update_timer))
141
110
    
142
111
    def checker_completed(self, exitstatus, condition, command):
143
112
        if exitstatus == 0:
144
 
            if self.last_checker_failed:
145
 
                self.last_checker_failed = False
146
 
                gobject.source_remove(self._update_timer_callback_tag)
147
 
                self._update_timer_callback_tag = None
148
113
            self.logger(u'Checker for client %s (command "%s")'
149
114
                        u' was successful'
150
115
                        % (self.properties[u"name"], command))
151
 
            self.update()
152
116
            return
153
 
        # Checker failed
154
 
        if not self.last_checker_failed:
155
 
            self.last_checker_failed = True
156
 
            self._update_timer_callback_tag = (gobject.timeout_add
157
 
                                               (1000,
158
 
                                                self.update_timer))
159
117
        if os.WIFEXITED(condition):
160
118
            self.logger(u'Checker for client %s (command "%s")'
161
119
                        u' failed with exit code %s'
162
120
                        % (self.properties[u"name"], command,
163
121
                           os.WEXITSTATUS(condition)))
164
 
        elif os.WIFSIGNALED(condition):
 
122
            return
 
123
        if os.WIFSIGNALED(condition):
165
124
            self.logger(u'Checker for client %s (command "%s")'
166
125
                        u' was killed by signal %s'
167
126
                        % (self.properties[u"name"], command,
168
127
                           os.WTERMSIG(condition)))
169
 
        elif os.WCOREDUMP(condition):
 
128
            return
 
129
        if os.WCOREDUMP(condition):
170
130
            self.logger(u'Checker for client %s (command "%s")'
171
131
                        u' dumped core'
172
132
                        % (self.properties[u"name"], command))
173
 
        else:
174
 
            self.logger(u'Checker for client %s completed mysteriously')
175
 
        self.update()
 
133
        self.logger(u'Checker for client %s completed mysteriously')
176
134
    
177
135
    def checker_started(self, command):
178
136
        self.logger(u'Client %s started checker "%s"'
214
172
                          }
215
173
        
216
174
        # Rebuild focus and non-focus widgets using current properties
217
 
        self._text = (u'%(name)s: %(enabled)s%(timer)s'
 
175
        self._text = (u'%(name)s: %(enabled)s'
218
176
                      % { u"name": self.properties[u"name"],
219
177
                          u"enabled":
220
178
                              (u"enabled"
221
179
                               if self.properties[u"enabled"]
222
 
                               else u"DISABLED"),
223
 
                          u"timer": (unicode(datetime.timedelta
224
 
                                             (milliseconds =
225
 
                                              self.properties
226
 
                                              [u"timeout"])
227
 
                                             - (datetime.datetime
228
 
                                                .utcnow()
229
 
                                                - isoformat_to_datetime
230
 
                                                (max((self.properties
231
 
                                                 ["last_checked_ok"]
232
 
                                                 or
233
 
                                                 self.properties
234
 
                                                 ["created"]),
235
 
                                                    self.properties[u"last_enabled"]))))
236
 
                                     if (self.last_checker_failed
237
 
                                         and self.properties
238
 
                                         [u"enabled"])
239
 
                                     else u"")})
 
180
                               else u"DISABLED")})
240
181
        if not urwid.supports_unicode():
241
182
            self._text = self._text.encode("ascii", "replace")
242
183
        textlist = [(u"normal", self._text)]
253
194
        if self.update_hook is not None:
254
195
            self.update_hook()
255
196
    
256
 
    def update_timer(self):
257
 
        "called by gobject"
258
 
        self.update()
259
 
        return True             # Keep calling this
260
 
    
261
197
    def delete(self):
262
 
        if self._update_timer_callback_tag is not None:
263
 
            gobject.source_remove(self._update_timer_callback_tag)
264
 
            self._update_timer_callback_tag = None
265
198
        if self.delete_hook is not None:
266
199
            self.delete_hook(self)
267
200
    
491
424
            return
492
425
        self.remove_client(client, path)
493
426
    
494
 
    def add_new_client(self, path):
 
427
    def add_new_client(self, path, properties):
495
428
        client_proxy_object = self.bus.get_object(self.busname, path)
496
429
        self.add_client(MandosClientWidget(server_proxy_object
497
430
                                           =self.mandos_serv,
498
431
                                           proxy_object
499
432
                                           =client_proxy_object,
 
433
                                           properties=properties,
500
434
                                           update_hook
501
435
                                           =self.refresh,
502
436
                                           delete_hook
503
 
                                           =self.remove_client,
504
 
                                           logger
505
 
                                           =self.log_message),
 
437
                                           =self.remove_client),
506
438
                        path=path)
507
439
    
508
440
    def add_client(self, client, path=None):
630
562
ui = UserInterface()
631
563
try:
632
564
    ui.run()
633
 
except Exception, e:
634
 
    ui.log_message(unicode(e))
 
565
except:
635
566
    ui.screen.stop()
636
567
    raise