chiark / gitweb /
keyexch, keymgmt: Include the peer's public key in the check hash.
[tripe] / tripemon.in
index 7ab688825a8a9a3917596f941a47fcbb77b409cd..0db0775247603f6c6f9d14010d65fb6fa90615dc 100644 (file)
@@ -7,6 +7,7 @@ import socket as S
 from sys import argv, exit, stdin, stdout, stderr
 import os as OS
 from os import environ
+import math as M
 import sets as SET
 import getopt as O
 import time as T
@@ -534,6 +535,70 @@ class MyDialog (G.Dialog, MyWindowMixin, HookClient):
   def respond(me, hunoz, rid, *hukairz):
     if rid >= 0: me.rmap[rid]()
 
+def makeactiongroup(name, acts):
+  """Creates an ActionGroup called NAME.  ACTS is a list of tuples
+  containing:
+  ACT: an action name
+  LABEL: the label string for the action
+  ACCEL: accelerator string, or None
+  FUNC: thunk to call when the action is invoked"""
+  actgroup = G.ActionGroup(name)
+  for act, label, accel, func in acts:
+    a = G.Action(act, label, None, None)
+    if func: a.connect('activate', invoker(func))
+    actgroup.add_action_with_accel(a, accel)
+  return actgroup
+
+class GridPacker (G.Table):
+  """Like a Table, but with more state: makes filling in the widgets
+  easier."""
+  def __init__(me):
+    G.Table.__init__(me)
+    me.row = 0
+    me.col = 0
+    me.rows = 1
+    me.cols = 1
+    me.set_border_width(4)
+    me.set_col_spacings(4)
+    me.set_row_spacings(4)
+  def pack(me, w, width = 1, newlinep = False,
+           xopt = G.EXPAND | G.FILL | G.SHRINK, yopt = 0,
+           xpad = 0, ypad = 0):
+    """Packs a new widget.  W is the widget to add.  XOPY, YOPT, XPAD and
+    YPAD are as for Table.  WIDTH is how many cells to take up horizontally.
+    NEWLINEP is whether to start a new line for this widget.  Returns W."""
+    if newlinep:
+      me.row += 1
+      me.col = 0
+    bot = me.row + 1
+    right = me.col + width
+    if bot > me.rows or right > me.cols:
+      if bot > me.rows: me.rows = bot
+      if right > me.cols: me.cols = right
+      me.resize(me.rows, me.cols)
+    me.attach(w, me.col, me.col + width, me.row, me.row + 1,
+              xopt, yopt, xpad, ypad)
+    me.col += width
+    return w
+  def labelled(me, lab, w, newlinep = False, **kw):
+    """Packs a labelled widget.  Other arguments are as for pack.  Returns
+    W."""
+    label = G.Label(lab)
+    label.set_alignment(1.0, 0)
+    me.pack(label, newlinep = newlinep, xopt = G.FILL)
+    me.pack(w, **kw)
+    return w
+  def info(me, label, text = None, len = 18, **kw):
+    """Packs an information widget with a label.  LABEL is the label; TEXT is
+    the initial text; LEN is the estimated length in characters.  Returns the
+    entry widget."""
+    e = G.Entry()
+    if text is not None: e.set_text(text)
+    e.set_width_chars(len)
+    e.set_editable(False)
+    me.labelled(label, e, **kw)
+    return e
+
 class WindowSlot (HookClient):
   """A place to store a window.  If the window is destroyed, remember this;
   when we come to open the window, raise it if it already exists; otherwise
@@ -640,6 +705,60 @@ def moanbox(msg):
   d.run()
   d.destroy()
 
+def unimplemented(*hunoz):
+  """Indicator of laziness."""
+  moanbox("I've not written that bit yet.")
+
+class ServInfo (MyWindow):
+  def __init__(me, monitor):
+    MyWindow.__init__(me)
+    me.set_title('TrIPE server info')
+    me.mon = monitor
+    me.table = GridPacker()
+    me.add(me.table)
+    me.e = {}
+    def add(label, tag, text = None, **kw):
+      me.e[tag] = me.table.info(label, text, **kw)
+    add('Implementation', 'implementation')
+    add('Version', 'version', newlinep = True)
+    me.update()
+    me.hook(me.mon.connecthook, me.update)
+    me.show_all()
+  def update(me):
+    info = parseinfo(me.mon.simplecmd('SERVINFO'))
+    for i in me.e:
+      me.e[i].set_text(info[i])
+
+class TraceOptions (MyDialog):
+  """Tracing options window."""
+  def __init__(me, monitor):
+    MyDialog.__init__(me, title = 'Tracing options',
+                      buttons = [(G.STOCK_CLOSE, me.destroy),
+                                 (G.STOCK_OK, me.ok)])
+    me.mon = monitor
+    me.opts = []
+    for o in me.mon.simplecmd('TRACE'):
+      char = o[0]
+      onp = o[1]
+      text = o[3].upper() + o[4:]
+      if char.isupper(): continue
+      ticky = G.CheckButton(text)
+      ticky.set_active(onp != ' ')
+      me.vbox.pack_start(ticky)
+      me.opts.append((char, ticky))
+    me.show_all()
+  def ok(me):
+    on = []
+    off = []
+    for char, ticky in me.opts:
+      if ticky.get_active():
+        on.append(char)
+      else:
+        off.append(char)
+    setting = ''.join(on) + '-' + ''.join(off)
+    me.mon.simplecmd('TRACE %s' % setting)
+    me.destroy()
+
 #----- Logging windows ------------------------------------------------------
 
 class LogModel (G.ListStore):
@@ -691,107 +810,21 @@ class LogViewer (MyWindow):
     me.add(scr)
     me.show_all()
 
-def makeactiongroup(name, acts):
-  """Creates an ActionGroup called NAME.  ACTS is a list of tuples
-  containing:
-  ACT: an action name
-  LABEL: the label string for the action
-  ACCEL: accelerator string, or None
-  FUNC: thunk to call when the action is invoked"""
-  actgroup = G.ActionGroup(name)
-  for act, label, accel, func in acts:
-    a = G.Action(act, label, None, None)
-    if func: a.connect('activate', invoker(func))
-    actgroup.add_action_with_accel(a, accel)
-  return actgroup
-
-class TraceOptions (MyDialog):
-  """Tracing options window."""
-  def __init__(me, monitor):
-    MyDialog.__init__(me, title = 'Tracing options',
-                      buttons = [(G.STOCK_CLOSE, me.destroy),
-                                 (G.STOCK_OK, me.ok)])
-    me.mon = monitor
-    me.opts = []
-    for o in me.mon.simplecmd('TRACE'):
-      char = o[0]
-      onp = o[1]
-      text = o[3].upper() + o[4:]
-      if char.isupper(): continue
-      ticky = G.CheckButton(text)
-      ticky.set_active(onp != ' ')
-      me.vbox.pack_start(ticky)
-      me.opts.append((char, ticky))
-    me.show_all()
-  def ok(me):
-    on = []
-    off = []
-    for char, ticky in me.opts:
-      if ticky.get_active():
-        on.append(char)
-      else:
-        off.append(char)
-    setting = ''.join(on) + '-' + ''.join(off)
-    me.mon.simplecmd('TRACE %s' % setting)
-    me.destroy()
-
-def unimplemented(*hunoz):
-  """Indicator of laziness."""
-  moanbox("I've not written that bit yet.")
-
-class GridPacker (G.Table):
-  """Like a Table, but with more state: makes filling in the widgets
-  easier."""
-  def __init__(me):
-    G.Table.__init__(me)
-    me.row = 0
-    me.col = 0
-    me.rows = 1
-    me.cols = 1
-    me.set_border_width(4)
-    me.set_col_spacings(4)
-    me.set_row_spacings(4)
-  def pack(me, w, width = 1, newlinep = False,
-           xopt = G.EXPAND | G.FILL | G.SHRINK, yopt = 0,
-           xpad = 0, ypad = 0):
-    """Packs a new widget.  W is the widget to add.  XOPY, YOPT, XPAD and
-    YPAD are as for Table.  WIDTH is how many cells to take up horizontally.
-    NEWLINEP is whether to start a new line for this widget.  Returns W."""
-    if newlinep:
-      me.row += 1
-      me.col = 0
-    bot = me.row + 1
-    right = me.col + width
-    if bot > me.rows or right > me.cols:
-      if bot > me.rows: me.rows = bot
-      if right > me.cols: me.cols = right
-      me.resize(me.rows, me.cols)
-    me.attach(w, me.col, me.col + width, me.row, me.row + 1,
-              xopt, yopt, xpad, ypad)
-    me.col += width
-    return w
-  def labelled(me, lab, w, newlinep = False, **kw):
-    """Packs a labelled widget.  Other arguments are as for pack.  Returns
-    W."""
-    label = G.Label(lab)
-    label.set_alignment(1.0, 0)
-    me.pack(label, newlinep = newlinep, xopt = G.FILL)
-    me.pack(w, **kw)
-    return w
-  def info(me, label, text = None, len = 18, **kw):
-    e = G.Entry()
-    if text is not None: e.set_text(text)
-    e.set_width_chars(len)
-    e.set_editable(False)
-    me.labelled(label, e, **kw)
-    return e
+#----- Peer window ----------------------------------------------------------
 
 def xlate_time(t):
   """Translate a time in tripe's stats format to something a human might
   actually want to read."""
   if t == 'NEVER': return '(never)'
-  Y, M, D, h, m, s = map(int, rx_time.match(t).group(1, 2, 3, 4, 5, 6))
-  return '%04d:%02d:%02d %02d:%02d:%02d' % (Y, M, D, h, m, s)
+  YY, MM, DD, hh, mm, ss = map(int, rx_time.match(t).group(1, 2, 3, 4, 5, 6))
+  ago = T.time() - T.mktime((YY, MM, DD, hh, mm, ss, 0, 0, -1))
+  ago = M.floor(ago); unit = 's'
+  for n, u in [(60, 'min'), (60, 'hrs'), (24, 'days')]:
+    if ago < 2*n: break
+    ago /= n
+    unit = u
+  return '%04d:%02d:%02d %02d:%02d:%02d (%.1f %s ago)' % \
+         (YY, MM, DD, hh, mm, ss, ago, unit)
 def xlate_bytes(b):
   """Translate a number of bytes into something a human might want to read."""
   suff = 'B'
@@ -882,10 +915,14 @@ class PeerWindow (MyWindow):
   def ping(me):
     for ping in me.peer.ping, me.peer.eping:
       s = '%d/%d' % (ping.ngood, ping.n)
+      if ping.n:
+        s += ' (%.1f%%)' % (ping.ngood * 100.0/ping.n)
       if ping.ngood:
         s += '; %.2f ms (last %.1f ms)' % (ping.ttot/ping.ngood, ping.tlast);
       me.e[ping.cmd].set_text(s)
 
+#----- Add peer -------------------------------------------------------------
+
 class AddPeerCommand (SimpleBackgroundCommand):
   def __init__(me, conn, dlg, name, addr, port,
                keepalive = None, tunnel = None):
@@ -968,25 +1005,7 @@ class AddPeerDialog (MyDialog):
       GDK.beep()
       return
 
-class ServInfo (MyWindow):
-  def __init__(me, monitor):
-    MyWindow.__init__(me)
-    me.set_title('TrIPE server info')
-    me.mon = monitor
-    me.table = GridPacker()
-    me.add(me.table)
-    me.e = {}
-    def add(label, tag, text = None, **kw):
-      me.e[tag] = me.table.info(label, text, **kw)
-    add('Implementation', 'implementation')
-    add('Version', 'version', newlinep = True)
-    me.update()
-    me.hook(me.mon.connecthook, me.update)
-    me.show_all()
-  def update(me):
-    info = parseinfo(me.mon.simplecmd('SERVINFO'))
-    for i in me.e:
-      me.e[i].set_text(info[i])
+#----- The server monitor ---------------------------------------------------
 
 class PingCommand (SimpleBackgroundCommand):
   def __init__(me, conn, cmd, peer, func):
@@ -1026,17 +1045,17 @@ class MonitorWindow (MyWindow):
     me.add(vbox)
 
     me.ui = G.UIManager()
+    def cmd(c): return lambda: me.mon.simplecmd(c)
     actgroup = makeactiongroup('monitor',
       [('file-menu', '_File', None, None),
        ('connect', '_Connect', '<Alt>C', me.mon.connect),
        ('disconnect', '_Disconnect', '<Alt>D', me.mon.disconnect),
        ('quit', '_Quit', '<Alt>Q', me.close),
        ('server-menu', '_Server', None, None),
-       ('daemon', 'Run in _background', None,
-          lambda: me.mon.simplecmd('DAEMON')),
-       ('server-version', 'Server version', None, me.servinfo.open),
-       ('server-quit', 'Terminate server', None,
-          lambda: me.mon.simplecmd('QUIT')),
+       ('daemon', 'Run in _background', None, cmd('DAEMON')),
+       ('server-version', 'Server version', '<Alt>V', me.servinfo.open),
+       ('reload-keys', 'Reload keys', '<Alt>R', cmd('RELOAD')),
+       ('server-quit', 'Terminate server', None, cmd('QUIT')),
        ('logs-menu', '_Logs', None, None),
        ('show-warnings', 'Show _warnings', '<Alt>W', me.warnview.open),
        ('show-trace', 'Show _trace', '<Alt>T', me.traceview.open),
@@ -1053,12 +1072,13 @@ class MonitorWindow (MyWindow):
             <menuitem action="quit"/>
           </menu>
           <menu action="server-menu">
-            <menuitem action="connect"/>
+             <menuitem action="connect"/>
             <menuitem action="disconnect"/>
             <separator/>
+            <menuitem action="server-version"/>
             <menuitem action="add-peer"/>
             <menuitem action="daemon"/>
-            <menuitem action="server-version"/>
+            <menuitem action="reload-keys"/>
             <separator/>
             <menuitem action="server-quit"/>
           </menu>
@@ -1294,3 +1314,4 @@ Options supported:
 if __name__ == '__main__':
   main()
 
+#----- That's all, folks ----------------------------------------------------