=== modified file 'mandos' --- mandos 2010-09-07 16:48:58 +0000 +++ mandos 2010-09-07 18:41:35 +0000 @@ -194,7 +194,7 @@ self.group.Commit() def entry_group_state_changed(self, state, error): """Derived from the Avahi example code""" - logger.debug(u"Avahi state change: %i", state) + logger.debug(u"Avahi entry group state change: %i", state) if state == avahi.ENTRY_GROUP_ESTABLISHED: logger.debug(u"Zeroconf service established.") @@ -213,6 +213,7 @@ self.group = None def server_state_changed(self, state): """Derived from the Avahi example code""" + logger.debug(u"Avahi server state change: %i", state) if state == avahi.SERVER_COLLISION: logger.error(u"Zeroconf server name collision") self.remove() === modified file 'mandos-monitor' --- mandos-monitor 2010-09-07 16:48:58 +0000 +++ mandos-monitor 2010-09-07 18:41:35 +0000 @@ -40,6 +40,22 @@ urwid.curses_display.curses.A_UNDERLINE |= ( urwid.curses_display.curses.A_BLINK) +def isoformat_to_datetime(iso): + "Parse an ISO 8601 date string to a datetime.datetime()" + if not iso: + return None + d, t = iso.split(u"T", 1) + year, month, day = d.split(u"-", 2) + hour, minute, second = t.split(u":", 2) + second, fraction = divmod(float(second), 1) + return datetime.datetime(int(year), + int(month), + int(day), + int(hour), + int(minute), + int(second), # Whole seconds + int(fraction*1000000)) # Microseconds + class MandosClientPropertyCache(object): """This wraps a Mandos Client D-Bus proxy object, caches the properties and calls a hook function when any of them are @@ -85,6 +101,9 @@ # Logger self.logger = logger + self._update_timer_callback_tag = None + self.last_checker_failed = False + # The widget shown normally self._text_widget = urwid.Text(u"") # The widget shown when we have focus @@ -114,30 +133,55 @@ self.rejected, client_interface, byte_arrays=True) + last_checked_ok = isoformat_to_datetime(self.properties + ["last_checked_ok"]) + if last_checked_ok is None: + self.last_checker_failed = True + else: + self.last_checker_failed = ((datetime.datetime.utcnow() + - last_checked_ok) + > datetime.timedelta + (milliseconds= + self.properties["interval"])) + if self.last_checker_failed: + self._update_timer_callback_tag = (gobject.timeout_add + (1000, + self.update_timer)) def checker_completed(self, exitstatus, condition, command): if exitstatus == 0: - #self.logger(u'Checker for client %s (command "%s")' - # u' was successful' - # % (self.properties[u"name"], command)) + if self.last_checker_failed: + self.last_checker_failed = False + gobject.source_remove(self._update_timer_callback_tag) + self._update_timer_callback_tag = None + self.logger(u'Checker for client %s (command "%s")' + u' was successful' + % (self.properties[u"name"], command)) + self.update() return + # Checker failed + if not self.last_checker_failed: + self.last_checker_failed = True + self._update_timer_callback_tag = (gobject.timeout_add + (1000, + self.update_timer)) if os.WIFEXITED(condition): self.logger(u'Checker for client %s (command "%s")' u' failed with exit code %s' % (self.properties[u"name"], command, os.WEXITSTATUS(condition))) - return - if os.WIFSIGNALED(condition): + elif os.WIFSIGNALED(condition): self.logger(u'Checker for client %s (command "%s")' u' was killed by signal %s' % (self.properties[u"name"], command, os.WTERMSIG(condition))) - return - if os.WCOREDUMP(condition): + elif os.WCOREDUMP(condition): self.logger(u'Checker for client %s (command "%s")' u' dumped core' % (self.properties[u"name"], command)) - self.logger(u'Checker for client %s completed mysteriously') + else: + self.logger(u'Checker for client %s completed mysteriously') + self.update() def checker_started(self, command): #self.logger(u'Client %s started checker "%s"' @@ -190,20 +234,30 @@ # Rebuild focus and non-focus widgets using current properties # Base part of a client. Name! - self._text = (u'%(name)s: ' + base = (u'%(name)s: ' % {u"name": self.properties[u"name"]}) + if not self.properties[u"enabled"]: + message = u"DISABLED" + elif self.last_checker_failed: + timeout = datetime.timedelta(milliseconds + = self.properties[u"timeout"]) + last_ok = isoformat_to_datetime( + max((self.properties["last_checked_ok"] + or self.properties["created"]), + self.properties[u"last_enabled"])) + timer = timeout - (datetime.datetime.utcnow() - last_ok) - if self.properties[u"approved_pending"]: + message = (u'A checker has failed! Time until client gets diabled: %s' + % unicode(timer)) + elif self.properties[u"approved_pending"]: if self.properties[u"approved_by_default"]: - self._text += u"Connection established to client. (d)eny?" + message = u"Connection established to client. (d)eny?" else: - self._text += u"Seeks approval to send secret. (a)pprove?" + message = u"Seeks approval to send secret. (a)pprove?" else: - self._text += (u'%(enabled)s' - % {u"enabled": - (u"enabled" - if self.properties[u"enabled"] - else u"DISABLED")}) + message = u"enabled" + self._text = "%s%s" % (base, message) + if not urwid.supports_unicode(): self._text = self._text.encode("ascii", "replace") textlist = [(u"normal", self._text)] @@ -220,7 +274,15 @@ if self.update_hook is not None: self.update_hook() + def update_timer(self): + "called by gobject" + self.update() + return True # Keep calling this + def delete(self): + if self._update_timer_callback_tag is not None: + gobject.source_remove(self._update_timer_callback_tag) + self._update_timer_callback_tag = None if self.delete_hook is not None: self.delete_hook(self)