[or-cvs] r19716: {arm} Decently big batch of feature additions and bug fixes. added (arm/trunk/interface)
atagar at seul.org
atagar at seul.org
Mon Jun 15 06:42:20 UTC 2009
Author: atagar
Date: 2009-06-15 02:42:19 -0400 (Mon, 15 Jun 2009)
New Revision: 19716
Added:
arm/trunk/interface/confPanel.py
arm/trunk/interface/headerPanel.py
Removed:
arm/trunk/interface/staticPanel.py
Modified:
arm/trunk/interface/bandwidthPanel.py
arm/trunk/interface/controller.py
arm/trunk/interface/util.py
Log:
Decently big batch of feature additions and bug fixes.
added: second page that presents torrc with syntax highlighing, optional comment stripping, etc
added: ps sampling (cpu/memory usage, pid, and uptime)
added: help popup with page controls
bug fix: corrected issue that caused periodic refreshing to fail
bug fix: accounting reset time takes into account DST
bug fix: make accounting input and header pausable
Modified: arm/trunk/interface/bandwidthPanel.py
===================================================================
--- arm/trunk/interface/bandwidthPanel.py 2009-06-14 02:39:33 UTC (rev 19715)
+++ arm/trunk/interface/bandwidthPanel.py 2009-06-15 06:42:19 UTC (rev 19716)
@@ -34,6 +34,7 @@
self.lastUploadRate = 0
self.maxDownloadRate = 1 # max rates seen, used to determine graph bounds
self.maxUploadRate = 1
+ self.accountingInfo = None # accounting data (set by _updateAccountingInfo method)
self.isPaused = False
self.pauseBuffer = None # mirror instance used to track updates when paused
@@ -112,35 +113,22 @@
self.addstr(7 - row, col + 40, " ", curses.A_STANDOUT | ulColor)
if self.isAccounting:
- try:
- accountingParams = self.conn.get_info(["accounting/hibernating", "accounting/bytes", "accounting/bytes-left", "accounting/interval-end"])
-
- hibernateStr = accountingParams["accounting/hibernating"]
+ if not self.isPaused: self._updateAccountingInfo()
+
+ if self.accountingInfo:
+ status = self.accountingInfo["status"]
hibernateColor = "green"
- if hibernateStr == "soft": hibernateColor = "yellow"
- elif hibernateStr == "hard": hibernateColor = "red"
+ if status == "soft": hibernateColor = "yellow"
+ elif status == "hard": hibernateColor = "red"
self.addstr(9, 0, "Accounting (", curses.A_BOLD)
- self.addstr(9, 12, hibernateStr, curses.A_BOLD | util.getColor(hibernateColor))
- self.addstr(9, 12 + len(hibernateStr), "):", curses.A_BOLD)
+ self.addstr(9, 12, status, curses.A_BOLD | util.getColor(hibernateColor))
+ self.addstr(9, 12 + len(status), "):", curses.A_BOLD)
- # timezone subtraction converts from gmt to local
- sec = time.mktime(time.strptime(accountingParams["accounting/interval-end"], "%Y-%m-%d %H:%M:%S")) - time.time() - time.timezone
- resetHours = sec / 3600
- sec %= 3600
- resetMin = sec / 60
- sec %= 60
-
- self.addstr(9, 35, "Time to reset: %i:%02i:%02i" % (resetHours, resetMin, sec))
-
- read = util.getSizeLabel(int(accountingParams["accounting/bytes"].split(" ")[0]))
- written = util.getSizeLabel(int(accountingParams["accounting/bytes"].split(" ")[1]))
- limit = util.getSizeLabel(int(accountingParams["accounting/bytes"].split(" ")[0]) + int(accountingParams["accounting/bytes-left"].split(" ")[0]))
-
- self.addstr(10, 2, "%s / %s" % (read, limit), dlColor)
- self.addstr(10, 37, "%s / %s" % (written, limit), ulColor)
-
- except TorCtl.TorCtlClosed:
+ self.addstr(9, 35, "Time to reset: %s" % self.accountingInfo["resetTime"])
+ self.addstr(10, 2, "%s / %s" % (self.accountingInfo["read"], self.accountingInfo["readLimit"]), dlColor)
+ self.addstr(10, 37, "%s / %s" % (self.accountingInfo["written"], self.accountingInfo["writtenLimit"]), ulColor)
+ else:
self.addstr(9, 0, "Accounting:", curses.A_BOLD)
self.addstr(9, 12, "Shutting Down...")
@@ -175,4 +163,38 @@
self.downloadRates = self.pauseBuffer.downloadRates
self.uploadRates = self.pauseBuffer.uploadRates
self.redraw()
+
+ def _updateAccountingInfo(self):
+ """
+ Updates mapping used for accounting info. This includes the following keys:
+ status, resetTime, read, written, readLimit, writtenLimit
+
+ Sets mapping to None if the Tor connection is closed.
+ """
+
+ try:
+ self.accountingInfo = {}
+
+ accountingParams = self.conn.get_info(["accounting/hibernating", "accounting/bytes", "accounting/bytes-left", "accounting/interval-end"])
+ self.accountingInfo["status"] = accountingParams["accounting/hibernating"]
+
+ # altzone subtraction converts from gmt to local with respect to DST
+ sec = time.mktime(time.strptime(accountingParams["accounting/interval-end"], "%Y-%m-%d %H:%M:%S")) - time.time() - time.altzone
+ resetHours = sec / 3600
+ sec %= 3600
+ resetMin = sec / 60
+ sec %= 60
+ self.accountingInfo["resetTime"] = "%i:%02i:%02i" % (resetHours, resetMin, sec)
+
+ read = int(accountingParams["accounting/bytes"].split(" ")[0])
+ written = int(accountingParams["accounting/bytes"].split(" ")[1])
+ readLeft = int(accountingParams["accounting/bytes-left"].split(" ")[0])
+ writtenLeft = int(accountingParams["accounting/bytes-left"].split(" ")[1])
+
+ self.accountingInfo["read"] = util.getSizeLabel(read)
+ self.accountingInfo["written"] = util.getSizeLabel(written)
+ self.accountingInfo["readLimit"] = util.getSizeLabel(read + readLeft)
+ self.accountingInfo["writtenLimit"] = util.getSizeLabel(written + writtenLeft)
+ except TorCtl.TorCtlClosed:
+ self.accountingInfo = None
Added: arm/trunk/interface/confPanel.py
===================================================================
--- arm/trunk/interface/confPanel.py (rev 0)
+++ arm/trunk/interface/confPanel.py 2009-06-15 06:42:19 UTC (rev 19716)
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+# confPanel.py -- Presents torrc with syntax highlighting.
+# Released under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
+
+import math
+import curses
+from TorCtl import TorCtl
+
+import util
+
+class ConfPanel(util.Panel):
+ """
+ Presents torrc with syntax highlighting in a scroll-able area.
+ """
+
+ def __init__(self, lock, confLocation):
+ util.Panel.__init__(self, lock, -1)
+ self.confLocation = confLocation
+ self.showLineNum = True
+ self.stripComments = False
+ self.reset()
+
+ def reset(self):
+ """
+ Reloads torrc contents and resets scroll height.
+ """
+ confFile = open(self.confLocation, "r")
+ self.confContents = confFile.readlines()
+ confFile.close()
+ self.scroll = 0
+
+ def handleKey(self, key):
+ self._resetBounds()
+ pageHeight = self.maxY - 1
+ if key == curses.KEY_UP: self.scroll = max(self.scroll - 1, 0)
+ elif key == curses.KEY_DOWN: self.scroll = min(self.scroll + 1, len(self.confContents) - pageHeight)
+ elif key == curses.KEY_PPAGE: self.scroll = max(self.scroll - pageHeight, 0)
+ elif key == curses.KEY_NPAGE: self.scroll = min(self.scroll + pageHeight, len(self.confContents) - pageHeight)
+ elif key == ord('n') or key == ord('N'): self.showLineNum = not self.showLineNum
+ elif key == ord('r') or key == ord('R'): self.reset()
+ elif key == ord('s') or key == ord('S'):
+ self.stripComments = not self.stripComments
+ self.scroll = 0
+ self.redraw()
+
+ def redraw(self):
+ if self.win:
+ if not self.lock.acquire(False): return
+ try:
+ self.clear()
+ self.addstr(0, 0, "Tor Config (%s):" % self.confLocation, util.LABEL_ATTR)
+
+ if self.stripComments:
+ displayText = []
+
+ for line in self.confContents:
+ commentStart = line.find("#")
+ if commentStart != -1: line = line[:commentStart]
+
+ line = line.strip()
+ if line: displayText.append(line)
+ else: displayText = self.confContents
+
+ pageHeight = self.maxY - 1
+ numFieldWidth = int(math.log10(len(displayText))) + 1
+ for i in range(self.scroll, min(len(displayText), self.scroll + pageHeight)):
+ lineText = displayText[i].strip()
+ endBreak = 0
+
+ if self.showLineNum:
+ self.addstr(i - self.scroll + 1, 0, ("%%%ii" % numFieldWidth) % (i + 1), curses.A_BOLD | util.getColor("yellow"))
+ numOffset = numFieldWidth + 1
+ else: numOffset = 0
+
+ if not lineText: continue
+ elif not lineText[0] == "#":
+ ctlBreak = lineText.find(" ")
+ endBreak = lineText.find("#")
+ if endBreak == -1: endBreak = len(lineText)
+
+ self.addstr(i - self.scroll + 1, numOffset, lineText[:ctlBreak], curses.A_BOLD | util.getColor("green"))
+ self.addstr(i - self.scroll + 1, numOffset + ctlBreak, lineText[ctlBreak:endBreak], curses.A_BOLD | util.getColor("cyan"))
+ self.addstr(i - self.scroll + 1, numOffset + endBreak, lineText[endBreak:], util.getColor("white"))
+
+ self.refresh()
+ finally:
+ self.lock.release()
+
Modified: arm/trunk/interface/controller.py
===================================================================
--- arm/trunk/interface/controller.py 2009-06-14 02:39:33 UTC (rev 19715)
+++ arm/trunk/interface/controller.py 2009-06-15 06:42:19 UTC (rev 19716)
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# controller.py -- arm interface (curses monitor for relay status).
+# controller.py -- arm interface (curses monitor for relay status)
# Released under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
"""
@@ -12,7 +12,8 @@
from TorCtl import TorCtl
import util
-import staticPanel
+import headerPanel
+import confPanel
import bandwidthPanel
import logPanel
@@ -21,10 +22,15 @@
# concurrency bugs produce especially sinister glitches)
# enums for message in control label
-CTL_HELP, CTL_PAUSED, CTL_EVENT_INPUT, CTL_EVENT_ERR = range(4)
+CTL_HELP, CTL_PAUSED = range(2)
# panel order per page
-PAGE_1 = ["summary", "control", "bandwidth", "log"]
+PAGE_S = ["header", "control", "popup"] # sticky (ie, always available) page
+PAGES = [
+ ["bandwidth", "log"],
+ ["torrc"]]
+PAUSEABLE = ["header", "bandwidth", "log"]
+PAGE_COUNT = 2 # all page numbering is internally represented as 0-indexed
# TODO: page 2: configuration information
# TODO: page 3: current connections
@@ -33,24 +39,34 @@
def __init__(self, lock):
util.Panel.__init__(self, lock, 1)
- self.msgType = CTL_HELP
- self.arg = ""
+ self.msgText = CTL_HELP # message text to be displyed
+ self.msgAttr = curses.A_NORMAL # formatting attributes
+ self.page = 1 # page number currently being displayed
- def setMsg(self, msgType, arg=""):
- self.msgType = msgType
- self.arg = arg
+ def setMsg(self, msgText, msgAttr=curses.A_NORMAL):
+ """
+ Sets the message and display attributes. If msgType matches CTL_HELP or
+ CTL_PAUSED then uses the default message for those statuses.
+ """
+
+ self.msgText = msgText
+ self.msgAttr = msgAttr
def redraw(self):
if self.win:
self.clear()
- if self.msgType == CTL_HELP: self.addstr(0, 0, "q: quit, e: change events, p: pause")
- elif self.msgType == CTL_PAUSED: self.addstr(0, 0, "Paused", curses.A_STANDOUT)
- elif self.msgType == CTL_EVENT_INPUT: self.addstr(0, 0, "Events to log: ")
- elif self.msgType == CTL_EVENT_ERR: self.addstr(0, 0, self.arg, curses.A_STANDOUT)
- else:
- assert False, "Unrecognized event type for control label: " + str(self.msgType)
+ msgText = self.msgText
+ msgAttr = self.msgAttr
+ if msgText == CTL_HELP:
+ msgText = "page %i / %i - q: quit, p: pause, h: help" % (self.page, PAGE_COUNT)
+ msgAttr = curses.A_NORMAL
+ elif msgText == CTL_PAUSED:
+ msgText = "Paused"
+ msgAttr = curses.A_STANDOUT
+
+ self.addstr(0, 0, msgText, msgAttr)
self.refresh()
def setEventListening(loggedEvents, conn, logListener):
@@ -108,12 +124,13 @@
try: curses.curs_set(0)
except curses.error: pass
- staticInfo = staticPanel.getStaticInfo(conn)
panels = {
- "summary": staticPanel.SummaryPanel(cursesLock, staticInfo),
+ "header": headerPanel.HeaderPanel(cursesLock, conn),
"control": ControlPanel(cursesLock),
+ "popup": util.Panel(cursesLock, 8),
"bandwidth": bandwidthPanel.BandwidthMonitor(cursesLock, conn),
- "log": logPanel.LogMonitor(cursesLock, loggedEvents)}
+ "log": logPanel.LogMonitor(cursesLock, loggedEvents),
+ "torrc": confPanel.ConfPanel(cursesLock, conn.get_info("config-file")["config-file"])}
# listeners that update bandwidth and log panels with Tor status
conn.add_event_listener(panels["log"])
@@ -126,6 +143,7 @@
oldY, oldX = -1, -1
isUnresponsive = False # true if it's been over five seconds since the last BW event (probably due to Tor closing)
isPaused = False # if true updates are frozen
+ page = 0
while True:
# tried only refreshing when the screen was resized but it caused a
@@ -135,12 +153,21 @@
cursesLock.acquire()
try:
y, x = stdscr.getmaxyx()
- if y > oldY:
+ if x > oldX or y > oldY:
# gives panels a chance to take advantage of the maximum bounds
startY = 0
- for panelKey in PAGE_1:
+ for panelKey in PAGE_S[:2]:
panels[panelKey].recreate(stdscr, startY)
startY += panels[panelKey].height
+
+ isChanged = panels["popup"].recreate(stdscr, startY, 80)
+
+ for panelSet in PAGES:
+ tmpStartY = startY
+
+ for panelKey in panelSet:
+ panels[panelKey].recreate(stdscr, tmpStartY)
+ tmpStartY += panels[panelKey].height
# if it's been at least five seconds since the last BW event Tor's probably done
if not isUnresponsive and panels["log"].getHeartbeat() >= 5:
@@ -152,7 +179,7 @@
panels["log"].monitor_event("WARN", "Relay resumed")
# I haven't the foggiest why, but doesn't work if redrawn out of order...
- for panelKey in PAGE_1: panels[panelKey].redraw()
+ for panelKey in (PAGE_S + PAGES[page]): panels[panelKey].redraw()
oldY, oldX = y, x
stdscr.refresh()
finally:
@@ -160,20 +187,73 @@
key = stdscr.getch()
if key == 27 or key == ord('q') or key == ord('Q'): break # quits (also on esc)
- elif key == ord('e') or key == ord('E'):
- # allow user to enter new types of events to log - unchanged if left blank
+ elif key == curses.KEY_LEFT or key == curses.KEY_RIGHT:
+ # switch page
+ if key == curses.KEY_LEFT: page = (page - 1) % PAGE_COUNT
+ else: page = (page + 1) % PAGE_COUNT
+ # pauses panels that aren't visible to prevent events from accumilating
+ # (otherwise they'll wait on the curses lock which might get demanding)
+ for key in PAUSEABLE: panels[key].setPaused(isPaused or key not in PAGES[page])
+
+ panels["control"].page = page + 1
+ panels["control"].refresh()
+ elif key == ord('p') or key == ord('P'):
+ # toggles update freezing
cursesLock.acquire()
try:
- # pauses listeners so events can still be handed (otherwise they wait
- # on curses lock which might get demanding if the user takes their time)
- isBwPaused = panels["bandwidth"].isPaused
- isLogPaused = panels["log"].isPaused
- panels["bandwidth"].setPaused(True)
- panels["log"].setPaused(True)
+ isPaused = not isPaused
+ for key in PAUSEABLE: panels[key].setPaused(isPaused or key not in PAGES[page])
+ msgType = CTL_PAUSED if isPaused else CTL_HELP
+ panels["control"].setMsg(msgType)
+ finally:
+ cursesLock.release()
+ elif key == ord('h') or key == ord('H'):
+ # displays popup for current page's controls
+ cursesLock.acquire()
+ try:
+ for key in PAUSEABLE: panels[key].setPaused(True)
+ panels["control"].setMsg("Press any key...")
+ panels["control"].redraw()
+
+ # lists commands
+ popup = panels["popup"]
+ popup.clear()
+ popup.win.box()
+ popup.addstr(0, 0, "Page %i Commands:" % (page + 1), util.LABEL_ATTR)
+
+ if page == 0:
+ popup.addstr(1, 2, "e: change logged events")
+ elif page == 1:
+ popup.addstr(1, 2, "up arrow: scroll up a line")
+ popup.addstr(1, 35, "down arrow: scroll down a line")
+ popup.addstr(2, 2, "page up: scroll up a page")
+ popup.addstr(2, 35, "page down: scroll down a page")
+ popup.addstr(3, 2, "s: toggle comment stripping")
+ popup.addstr(3, 35, "n: toggle line numbering")
+ popup.addstr(4, 2, "r: reload torrc")
+
+ popup.refresh()
+
+ curses.cbreak()
+ stdscr.getch()
+ curses.halfdelay(REFRESH_RATE * 10)
+
+ msgType = CTL_PAUSED if isPaused else CTL_HELP
+ panels["control"].setMsg(msgType)
+
+ for key in PAUSEABLE: panels[key].setPaused(isPaused or key not in PAGES[page])
+ finally:
+ cursesLock.release()
+ elif page == 0 and (key == ord('e') or key == ord('E')):
+ # allow user to enter new types of events to log - unchanged if left blank
+ cursesLock.acquire()
+ try:
+ for key in PAUSEABLE: panels[key].setPaused(True)
+
# provides prompt
- panels["control"].setMsg(CTL_EVENT_INPUT)
+ panels["control"].setMsg("Events to log: ")
panels["control"].redraw()
# makes cursor and typing visible
@@ -181,16 +261,16 @@
except curses.error: pass
curses.echo()
- # switches bandwidth area to list event types
- bwPanel = panels["bandwidth"]
- bwPanel.clear()
- bwPanel.addstr(0, 0, "Event Types:", util.LABEL_ATTR)
+ # lists event types
+ popup = panels["popup"]
+ popup.clear()
+ popup.addstr(0, 0, "Event Types:", util.LABEL_ATTR)
lineNum = 1
for line in logPanel.EVENT_LISTING.split("\n"):
line = line.strip()
- bwPanel.addstr(lineNum, 0, line[:x - 1])
+ popup.addstr(lineNum, 0, line[:x - 1])
lineNum += 1
- bwPanel.refresh()
+ popup.refresh()
# gets user input (this blocks monitor updates)
eventsInput = panels["control"].win.getstr(0, 15)
@@ -200,6 +280,7 @@
try: curses.curs_set(0)
except curses.error: pass
curses.noecho()
+ curses.halfdelay(REFRESH_RATE * 10) # evidenlty previous tweaks reset this...
# TODO: it would be nice to quit on esc, but looks like this might not be possible...
if eventsInput != "":
@@ -208,29 +289,18 @@
loggedEvents = setEventListening(expandedEvents, conn, panels["log"])
panels["log"].loggedEvents = loggedEvents
except ValueError, exc:
- panels["control"].setMsg(CTL_EVENT_ERR, "Invalid flags: %s" % str(exc))
+ panels["control"].setMsg("Invalid flags: %s" % str(exc), curses.A_STANDOUT)
panels["control"].redraw()
time.sleep(2)
msgType = CTL_PAUSED if isPaused else CTL_HELP
panels["control"].setMsg(msgType)
- # returns listeners to previous pause status
- panels["bandwidth"].setPaused(isBwPaused)
- panels["log"].setPaused(isLogPaused)
+ for key in PAUSEABLE: panels[key].setPaused(isPaused or key not in PAGES[page])
finally:
cursesLock.release()
- elif key == ord('p') or key == ord('P'):
- # toggles update freezing
- cursesLock.acquire()
- try:
- isPaused = not isPaused
- panels["log"].setPaused(isPaused)
- panels["bandwidth"].setPaused(isPaused)
- msgType = CTL_PAUSED if isPaused else CTL_HELP
- panels["control"].setMsg(msgType)
- finally:
- cursesLock.release()
+ elif page == 1:
+ panels["torrc"].handleKey(key)
def startTorMonitor(conn, loggedEvents):
curses.wrapper(drawTorMonitor, conn, loggedEvents)
Added: arm/trunk/interface/headerPanel.py
===================================================================
--- arm/trunk/interface/headerPanel.py (rev 0)
+++ arm/trunk/interface/headerPanel.py 2009-06-15 06:42:19 UTC (rev 19716)
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+# summaryPanel.py -- Static system and Tor related information.
+# Released under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
+
+import os
+import curses
+from TorCtl import TorCtl
+
+import util
+
+class HeaderPanel(util.Panel):
+ """
+ Draws top area containing static information.
+
+ arm - <System Name> (<OS> <Version>) Tor <Tor Version>
+ <Relay Nickname> - <IP Addr>:<ORPort>, [Dir Port: <DirPort>, ]Control Port (<open, password, cookie>): <ControlPort>
+ cpu: <cpu%> mem: <mem> (<mem%>) uid: <uid> uptime: <upmin>:<upsec>
+ fingerprint: <Fingerprint>
+
+ Example:
+ arm - odin (Linux 2.6.24-24-generic) Tor 0.2.1.15-rc
+ odin - 76.104.132.98:9001, Dir Port: 9030, Control Port (cookie): 9051
+ cpu: 14.6% mem: 42 MB (4.2%) pid: 20060 uptime: 48:27
+ fingerprint: BDAD31F6F318E0413833E8EBDA956F76E4D66788
+ """
+
+ def __init__(self, lock, conn):
+ util.Panel.__init__(self, lock, 5)
+ self.vals = [] # mapping of information to be presented
+ self.conn = conn # Tor control port connection
+ self.isPaused = False
+ self._updateParams()
+
+ def redraw(self):
+ if self.win:
+ if not self.isPaused: self._updateParams()
+
+ # extra erase/refresh is needed to avoid internal caching screwing up and
+ # refusing to redisplay content in the case of graphical glitches - probably
+ # an obscure curses bug...
+ self.win.erase()
+ self.win.refresh()
+
+ self.clear()
+
+ # Line 1
+ self.addstr(0, 0, "arm - %s (%s %s)" % (self.vals["sys-name"], self.vals["sys-os"], self.vals["sys-version"]))
+ self.addstr(0, 45, "Tor %s" % self.vals["version"])
+
+ # Line 2 (authentication label red if open, green if credentials required)
+ dirPortLabel = "Dir Port: %s, " % self.vals["DirPort"] if not self.vals["DirPort"] == None else ""
+
+ # TODO: if both cookie and password are set then which takes priority?
+ if self.vals["IsPasswordAuthSet"]: controlPortAuthLabel = "password"
+ elif self.vals["IsCookieAuthSet"]: controlPortAuthLabel = "cookie"
+ else: controlPortAuthLabel = "open"
+ controlPortAuthColor = "red" if controlPortAuthLabel == "open" else "green"
+
+ labelStart = "%s - %s:%s, %sControl Port (" % (self.vals["Nickname"], self.vals["address"], self.vals["ORPort"], dirPortLabel)
+ self.addstr(1, 0, labelStart)
+ xLoc = len(labelStart)
+ self.addstr(1, xLoc, controlPortAuthLabel, util.getColor(controlPortAuthColor))
+ xLoc += len(controlPortAuthLabel)
+ self.addstr(1, xLoc, "): %s" % self.vals["ControlPort"])
+
+ # Line 3 (system usage info)
+ self.addstr(2, 0, "cpu: %s%%" % self.vals["%cpu"])
+ self.addstr(2, 13, "mem: %s (%s%%)" % (util.getSizeLabel(int(self.vals["rss"]) * 1024), self.vals["%mem"]))
+ self.addstr(2, 34, "pid: %s" % self.vals["pid"])
+ self.addstr(2, 47, "uptime: %s" % self.vals["etime"])
+
+ # Line 4 (fingerprint)
+ self.addstr(3, 0, "fingerprint: %s" % self.vals["fingerprint"])
+
+
+ # Lines 3-5
+ #self.addstr(3, 0, "Config: %s" % self.vals["config-file"])
+ #exitPolicy = self.vals["ExitPolicy"]
+
+ # adds note when default exit policy is appended
+ #if exitPolicy == None: exitPolicy = "<default>"
+ #elif not exitPolicy.endswith("accept *:*") and not exitPolicy.endswith("reject *:*"):
+ # exitPolicy += ", <default>"
+ #self.addstr(4, 0, "Exit Policy: %s" % exitPolicy)
+
+ self.refresh()
+
+ def setPaused(self, isPause):
+ """
+ If true, prevents updates from being presented.
+ """
+
+ self.isPaused = isPause
+
+ def _updateParams(self):
+ """
+ Updates mapping of static Tor settings and system information to their
+ corresponding string values. Keys include:
+ info - version, config-file, *address, *fingerprint
+ sys - sys-name, sys-os, sys-version
+ ps - *%cpu, *rss, *%mem, *pid, *etime
+ config - Nickname, ORPort, DirPort, ControlPort, ExitPolicy
+ config booleans - IsPasswordAuthSet, IsCookieAuthSet, IsAccountingEnabled
+
+ * volatile parameter that'll be reset (otherwise won't be checked if
+ already set)
+ """
+
+ if not self.vals:
+ # retrieves static params
+ self.vals = self.conn.get_info(["version", "config-file"])
+
+ # populates with some basic system information
+ unameVals = os.uname()
+ self.vals["sys-name"] = unameVals[1]
+ self.vals["sys-os"] = unameVals[0]
+ self.vals["sys-version"] = unameVals[2]
+
+ # parameters from the user's torrc
+ configFields = ["Nickname", "ORPort", "DirPort", "ControlPort", "ExitPolicy"]
+ self.vals.update(dict([(key, self.conn.get_option(key)[0][1]) for key in configFields]))
+
+ # simply keeps booleans for if authentication info is set
+ self.vals["IsPasswordAuthSet"] = not self.conn.get_option("HashedControlPassword")[0][1] == None
+ self.vals["IsCookieAuthSet"] = self.conn.get_option("CookieAuthentication")[0][1] == "1"
+
+ self.vals["IsAccountingEnabled"] = self.conn.get_info('accounting/enabled')['accounting/enabled'] == "1"
+
+ # gets parameters that throw errors if unavailable
+ for param in ["address", "fingerprint"]:
+ try: self.vals.update(self.conn.get_info(param))
+ except TorCtl.ErrorReply: self.vals[param] = "Unknown"
+ except TorCtl.TorCtlClosed:
+ # Tor shut down - keep last known values
+ if not self.vals[param]: self.vals[param] = "Unknown"
+
+ # ps call provides header followed by params for tor
+ psParams = ["%cpu", "rss", "%mem", "pid", "etime"]
+ psCall = os.popen('ps -C %s -o %s' % ("tor", ",".join(psParams)))
+
+ try: sampling = psCall.read().strip().split()[len(psParams):]
+ except IOError: sampling = [] # ps call failed
+ psCall.close()
+
+ if len(sampling) < 5:
+ # either ps failed or returned no tor instance
+ sampling = [""] * len(psParams)
+
+ # %cpu, rss, and %mem are better zeroed out
+ for i in range(3): sampling[i] = "0"
+
+ for i in range(len(psParams)):
+ self.vals[psParams[i]] = sampling[i]
+
Deleted: arm/trunk/interface/staticPanel.py
===================================================================
--- arm/trunk/interface/staticPanel.py 2009-06-14 02:39:33 UTC (rev 19715)
+++ arm/trunk/interface/staticPanel.py 2009-06-15 06:42:19 UTC (rev 19716)
@@ -1,114 +0,0 @@
-#!/usr/bin/env python
-# summaryPanel.py -- Static system and Tor related information.
-# Released under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
-
-import os
-import curses
-from TorCtl import TorCtl
-
-import util
-
-def getStaticInfo(conn):
- """
- Provides mapping of static Tor settings and system information to their
- corresponding string values. Keys include:
- info - version, config-file, address, fingerprint
- sys - sys-name, sys-os, sys-version
- config - Nickname, ORPort, DirPort, ControlPort, ExitPolicy
- config booleans - IsPasswordAuthSet, IsCookieAuthSet, IsAccountingEnabled
- """
-
- vals = conn.get_info(["version", "config-file"])
-
- # gets parameters that throw errors if unavailable
- for param in ["address", "fingerprint"]:
- try:
- vals.update(conn.get_info(param))
- except TorCtl.ErrorReply:
- vals[param] = "Unknown"
-
- # populates with some basic system information
- unameVals = os.uname()
- vals["sys-name"] = unameVals[1]
- vals["sys-os"] = unameVals[0]
- vals["sys-version"] = unameVals[2]
-
- # parameters from the user's torrc
- configFields = ["Nickname", "ORPort", "DirPort", "ControlPort", "ExitPolicy"]
- vals.update(dict([(key, conn.get_option(key)[0][1]) for key in configFields]))
-
- # simply keeps booleans for if authentication info is set
- vals["IsPasswordAuthSet"] = not conn.get_option("HashedControlPassword")[0][1] == None
- vals["IsCookieAuthSet"] = conn.get_option("CookieAuthentication")[0][1] == "1"
-
- vals["IsAccountingEnabled"] = conn.get_info('accounting/enabled')['accounting/enabled'] == "1"
-
- return vals
-
-class SummaryPanel(util.Panel):
- """
- Draws top area containing static information.
-
- arm - <System Name> (<OS> <Version>) Tor <Tor Version>
- <Relay Nickname> - <IP Addr>:<ORPort>, [Dir Port: <DirPort>, ]Control Port (<open, password, cookie>): <ControlPort>
- Fingerprint: <Fingerprint>
- Config: <Config>
- Exit Policy: <ExitPolicy>
-
- Example:
- arm - odin (Linux 2.6.24-24-generic) Tor 0.2.0.34 (r18423)
- odin - 76.104.132.98:9001, Dir Port: 9030, Control Port (cookie): 9051
- Fingerprint: BDAD31F6F318E0413833E8EBDA956F76E4D66788
- Config: /home/atagar/.vidalia/torrc
- Exit Policy: reject *:*
- """
-
- def __init__(self, lock, vals):
- util.Panel.__init__(self, lock, 6)
- self.vals = vals # mapping of information to be presented
-
- def redraw(self):
- i = 1
-
- if self.win:
- # extra erase/refresh is needed to avoid internal caching screwing up and
- # refusing to redisplay content in the case of graphical glitches - probably
- # an obscure curses bug...
- self.win.erase()
- self.win.refresh()
-
- self.clear()
-
- # Line 1
- self.addstr(0, 0, "arm - %s (%s %s)" % (self.vals["sys-name"], self.vals["sys-os"], self.vals["sys-version"]))
- self.addstr(0, 45, "Tor %s" % self.vals["version"])
-
- # Line 2 (authentication label red if open, green if credentials required)
- dirPortLabel = "Dir Port: %s, " % self.vals["DirPort"] if not self.vals["DirPort"] == None else ""
-
- # TODO: if both cookie and password are set then which takes priority?
- if self.vals["IsPasswordAuthSet"]: controlPortAuthLabel = "password"
- elif self.vals["IsCookieAuthSet"]: controlPortAuthLabel = "cookie"
- else: controlPortAuthLabel = "open"
- controlPortAuthColor = "red" if controlPortAuthLabel == "open" else "green"
-
- labelStart = "%s - %s:%s, %sControl Port (" % (self.vals["Nickname"], self.vals["address"], self.vals["ORPort"], dirPortLabel)
- self.addstr(1, 0, labelStart)
- xLoc = len(labelStart)
- self.addstr(1, xLoc, controlPortAuthLabel, util.getColor(controlPortAuthColor))
- xLoc += len(controlPortAuthLabel)
- self.addstr(1, xLoc, "): %s" % self.vals["ControlPort"])
-
- # Lines 3-5
- self.addstr(2, 0, "Fingerprint: %s" % self.vals["fingerprint"])
- self.addstr(3, 0, "Config: %s" % self.vals["config-file"])
- exitPolicy = self.vals["ExitPolicy"]
-
- # adds note when default exit policy is appended
- if exitPolicy == None: exitPolicy = "<default>"
- elif not exitPolicy.endswith("accept *:*") and not exitPolicy.endswith("reject *:*"):
- exitPolicy += ", <default>"
- self.addstr(4, 0, "Exit Policy: %s" % exitPolicy)
-
- self.refresh()
-
Modified: arm/trunk/interface/util.py
===================================================================
--- arm/trunk/interface/util.py 2009-06-14 02:39:33 UTC (rev 19715)
+++ arm/trunk/interface/util.py 2009-06-15 06:42:19 UTC (rev 19716)
@@ -79,7 +79,7 @@
def redraw(self):
pass # overwritten by implementations
- def recreate(self, stdscr, startY):
+ def recreate(self, stdscr, startY, maxX=-1):
"""
Creates a new subwindow for the panel if:
- panel currently doesn't have a subwindow
@@ -105,11 +105,12 @@
newHeight = y - startY
if self.height != -1: newHeight = min(newHeight, self.height)
- if self.startY != startY or newHeight > self.maxY or self.isDisplaced:
+ if self.startY != startY or newHeight > self.maxY or self.isDisplaced or (self.maxX > maxX and maxX != -1):
# window growing or moving - recreate
self.startY = startY
startY = min(startY, y - 1) # better create a displaced window than leave it as None
-
+ if maxX != -1: x = min(x, maxX)
+
self.win = stdscr.subwin(newHeight, x, startY, 0)
return True
else: return False
More information about the tor-commits
mailing list