[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r21062: {arm} Weekend bugfix bundle. added: most commands can be immediate (in arm/trunk: . interface)
Author: atagar
Date: 2009-11-29 23:12:57 -0500 (Sun, 29 Nov 2009)
New Revision: 21062
Modified:
arm/trunk/ChangeLog
arm/trunk/README
arm/trunk/TODO
arm/trunk/arm.py
arm/trunk/interface/connPanel.py
arm/trunk/interface/controller.py
arm/trunk/interface/fileDescriptorPopup.py
arm/trunk/interface/headerPanel.py
Log:
Weekend bugfix bundle.
added: most commands can be immediately executed from the help page (feature request by arma)
fix: truncating header's version fields if too long (caught by hexa)
fix: file descriptor dialog now provides a wider variety of error messages in case of failure
fix: offset issue in connections listing when scroll bar was visible
fix: removing family connections from listing when control port is closed
fix: preventing TorCtl startup issues from going to stdout (duplicates warnings)
Modified: arm/trunk/ChangeLog
===================================================================
--- arm/trunk/ChangeLog 2009-11-29 21:02:19 UTC (rev 21061)
+++ arm/trunk/ChangeLog 2009-11-30 04:12:57 UTC (rev 21062)
@@ -1,6 +1,16 @@
CHANGE LOG
-11/8/09 - version 1.2.2
+11/29/09 - version 1.3.0
+Weekend bugfix bundle.
+
+ * added: most commands can be immediately executed from the help page (feature request by arma)
+ * fix: truncating header's version fields if too long (caught by hexa)
+ * fix: file descriptor dialog now provides a wider variety of error messages in case of failure
+ * fix: offset issue in connections listing when scroll bar was visible
+ * fix: removing family connections from listing when control port is closed
+ * fix: preventing TorCtl startup issues from going to stdout (duplicates warnings)
+
+11/8/09 - version 1.2.2 (r20927)
This will be the last update for a while since I'm about to start a new job.
* added: including family relays on connections listing
Modified: arm/trunk/README
===================================================================
--- arm/trunk/README 2009-11-29 21:02:19 UTC (rev 21061)
+++ arm/trunk/README 2009-11-30 04:12:57 UTC (rev 21062)
@@ -20,13 +20,17 @@
Requirements:
Python 2.5
-TorCtl (retrieved in svn checkout)
+TorCtl (this is included with arm)
Tor is running with an available control port. This means either...
... starting Tor with '--controlport <PORT>'
... or including 'ControlPort <PORT>' in your torrc
-For full functionality this requires common *nix commands including: ps, pidof,
- tail, pwdx, host, netstat, lsof, and ulimit
+For full functionality this also needs:
+Common *nix commands including: ps, pidof, tail, pwdx, host, netstat, lsof, and
+ ulimit
+To be ran with the same user as tor to avoid permission issues with netstat,
+ lsof, and reading the torrc
+
This is started via 'arm' (use the '--help' argument for usage).
FAQ:
Modified: arm/trunk/TODO
===================================================================
--- arm/trunk/TODO 2009-11-29 21:02:19 UTC (rev 21061)
+++ arm/trunk/TODO 2009-11-30 04:12:57 UTC (rev 21062)
@@ -10,14 +10,11 @@
Not sure how to address this - problem is that the calls to 'host' can
take a while to time out. Might need another thread to kill the calls?
Or forcefully terminate thread if it's taking too long (might be noisy)?
- * version labels provided on Debian are longer than expected
- caught by hexa
- Features / Site
* abstract away netstat calls
In preparation for drop in replacement of lsof or calls to tor's
GETINFO.
- * file descriptor stats
* add page that allows raw control port access
Piggyback on the arm connection, providing something like an interactive
prompt. In addition, provide:
@@ -36,8 +33,6 @@
requested by arma
* show advertised bandwidth
if set and there's extra room available show 'MaxAdvertisedBandwidth'
- * when help popup is showing options let them be directly opened
- requested by arma
* check family connections to see if they're alive (VERSION cell handshake?)
* update site's screenshots (pretty out of date...)
Modified: arm/trunk/arm.py
===================================================================
--- arm/trunk/arm.py 2009-11-29 21:02:19 UTC (rev 21061)
+++ arm/trunk/arm.py 2009-11-30 04:12:57 UTC (rev 21062)
@@ -19,8 +19,8 @@
from interface import controller
from interface import logPanel
-VERSION = "1.2.2"
-LAST_MODIFIED = "Nov 8, 2009"
+VERSION = "1.3.0"
+LAST_MODIFIED = "Nov 29, 2009"
DEFAULT_CONTROL_ADDR = "127.0.0.1"
DEFAULT_CONTROL_PORT = 9051
@@ -186,6 +186,9 @@
print "Unrecognized event flag: %s" % flag
sys.exit()
+ # temporarily disables TorCtl logging to prevent issues from going to stdout when starting
+ TorUtil.loglevel = "NONE"
+
# attempts to open a socket to the tor server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
Modified: arm/trunk/interface/connPanel.py
===================================================================
--- arm/trunk/interface/connPanel.py 2009-11-29 21:02:19 UTC (rev 21061)
+++ arm/trunk/interface/connPanel.py 2009-11-30 04:12:57 UTC (rev 21062)
@@ -340,12 +340,14 @@
familyResolutionsTmp[(familyAddress, familyPort)] = fingerprint
connectionsTmp.append(("family", familyAddress, familyPort, familyAddress, familyPort, familyCountryCode, connTime))
- except (socket.error, TorCtl.ErrorReply, TorCtl.TorCtlClosed):
+ except (socket.error, TorCtl.ErrorReply):
# use dummy entry for sorting - the redraw function notes that entries are unknown
portIdentifier = str(65536 + tmpCounter)
familyResolutionsTmp[("256.255.255.255", portIdentifier)] = fingerprint
connectionsTmp.append(("family", "256.255.255.255", portIdentifier, "256.255.255.255", portIdentifier, "??", time.time()))
tmpCounter += 1
+ except TorCtl.TorCtlClosed:
+ pass # connections aren't shown when control port is unavailable
self.lastUpdate = time.time()
@@ -568,7 +570,7 @@
ipStart = etc.find("256")
if ipStart > -1: etc = etc[:ipStart] + ("%%-%is" % len(etc[ipStart:])) % "UNKNOWN"
- padding = self.maxX - (len(src) + len(dst) + len(etc) + 27) # padding needed to fill full line
+ padding = self.maxX - (len(src) + len(dst) + len(etc) + 27) - xOffset # padding needed to fill full line
lineEntry = "<%s>%s --> %s %s%s%5s (<b>%s</b>)%s</%s>" % (color, src, dst, etc, " " * padding, timeLabel, type.upper(), " " * (9 - len(type)), color)
if self.isCursorEnabled and entry == self.cursorSelection:
Modified: arm/trunk/interface/controller.py
===================================================================
--- arm/trunk/interface/controller.py 2009-11-29 21:02:19 UTC (rev 21061)
+++ arm/trunk/interface/controller.py 2009-11-30 04:12:57 UTC (rev 21062)
@@ -361,6 +361,7 @@
isUnresponsive = False # true if it's been over ten seconds since the last BW event (probably due to Tor closing)
isPaused = False # if true updates are frozen
+ overrideKey = None # immediately runs with this input rather than waiting for the user if set
page = 0
regexFilters = [] # previously used log regex filters
@@ -422,7 +423,13 @@
finally:
cursesLock.release()
- key = stdscr.getch()
+ # wait for user keyboard input until timeout (unless an override was set)
+ if overrideKey:
+ key = overrideKey
+ overrideKey = None
+ else:
+ key = stdscr.getch()
+
if key == ord('q') or key == ord('Q'):
quitConfirmed = not CONFIRM_QUIT
@@ -498,39 +505,45 @@
popup.win.box()
popup.addstr(0, 0, "Page %i Commands:" % (page + 1), util.LABEL_ATTR)
+ pageOverrideKeys = ()
+
if page == 0:
graphedStats = panels["graph"].currentDisplay
if not graphedStats: graphedStats = "none"
- popup.addfstr(1, 2, "s: graphed stats (<b>%s</b>)" % graphedStats)
- popup.addfstr(1, 41, "i: graph update interval (<b>%s</b>)" % panels["graph"].updateInterval)
+ popup.addfstr(1, 2, "<b>s</b>: graphed stats (<b>%s</b>)" % graphedStats)
+ popup.addfstr(1, 41, "<b>i</b>: graph update interval (<b>%s</b>)" % panels["graph"].updateInterval)
popup.addfstr(2, 2, "b: graph bounds (<b>%s</b>)" % graphPanel.BOUND_LABELS[panels["graph"].bounds])
- popup.addstr(2, 41, "d: file descriptors")
- popup.addstr(3, 2, "e: change logged events")
+ popup.addfstr(2, 41, "<b>d</b>: file descriptors")
+ popup.addfstr(3, 2, "<b>e</b>: change logged events")
runlevelEventsLabel = "arm and tor"
if panels["log"].runlevelTypes == logPanel.RUNLEVEL_TOR_ONLY: runlevelEventsLabel = "tor only"
elif panels["log"].runlevelTypes == logPanel.RUNLEVEL_ARM_ONLY: runlevelEventsLabel = "arm only"
- popup.addfstr(3, 41, "r: logged runlevels (<b>%s</b>)" % runlevelEventsLabel)
+ popup.addfstr(3, 41, "<b>r</b>: logged runlevels (<b>%s</b>)" % runlevelEventsLabel)
regexLabel = "enabled" if panels["log"].regexFilter else "disabled"
- popup.addfstr(4, 2, "f: log regex filter (<b>%s</b>)" % regexLabel)
+ popup.addfstr(4, 2, "<b>f</b>: log regex filter (<b>%s</b>)" % regexLabel)
+
+ pageOverrideKeys = (ord('s'), ord('i'), ord('d'), ord('e'), ord('r'), ord('f'))
if page == 1:
popup.addstr(1, 2, "up arrow: scroll up a line")
popup.addstr(1, 41, "down arrow: scroll down a line")
popup.addstr(2, 2, "page up: scroll up a page")
popup.addstr(2, 41, "page down: scroll down a page")
popup.addstr(3, 2, "enter: connection details")
- popup.addstr(3, 41, "d: raw consensus descriptor")
+ popup.addfstr(3, 41, "<b>d</b>: raw consensus descriptor")
listingType = connPanel.LIST_LABEL[panels["conn"].listingType].lower()
- popup.addfstr(4, 2, "l: listed identity (<b>%s</b>)" % listingType)
+ popup.addfstr(4, 2, "<b>l</b>: listed identity (<b>%s</b>)" % listingType)
allowDnsLabel = "allow" if panels["conn"].allowDNS else "disallow"
popup.addfstr(4, 41, "r: permit DNS resolution (<b>%s</b>)" % allowDnsLabel)
- popup.addstr(5, 2, "s: sort ordering")
- popup.addstr(5, 41, "c: client circuits")
+ popup.addfstr(5, 2, "<b>s</b>: sort ordering")
+ popup.addfstr(5, 41, "<b>c</b>: client circuits")
#popup.addfstr(5, 41, "c: toggle cursor (<b>%s</b>)" % ("on" if panels["conn"].isCursorEnabled else "off"))
+
+ pageOverrideKeys = (ord('d'), ord('l'), ord('s'), ord('c'))
elif page == 2:
popup.addstr(1, 2, "up arrow: scroll up a line")
popup.addstr(1, 41, "down arrow: scroll down a line")
@@ -546,8 +559,10 @@
popup.addstr(7, 2, "Press any key...")
popup.refresh()
+ # waits for user to hit a key, if it belongs to a command then executes it
curses.cbreak()
- stdscr.getch()
+ helpExitKey = stdscr.getch()
+ if helpExitKey in pageOverrideKeys: overrideKey = helpExitKey
curses.halfdelay(REFRESH_RATE * 10)
setPauseState(panels, isPaused, page)
Modified: arm/trunk/interface/fileDescriptorPopup.py
===================================================================
--- arm/trunk/interface/fileDescriptorPopup.py 2009-11-29 21:02:19 UTC (rev 21061)
+++ arm/trunk/interface/fileDescriptorPopup.py 2009-11-30 04:12:57 UTC (rev 21062)
@@ -27,16 +27,34 @@
# retrieves list of open files, options are:
# n = no dns lookups, p = by pid, -F = show fields (L = login name, n = opened files)
- lsofCall = os.popen("lsof -np %s -F Ln 2> /dev/null" % torPid)
- results = lsofCall.readlines()
- if len(results) == 0: raise Exception("lsof is unavailable")
+ lsofCall = os.popen3("lsof -np %s -F Ln" % torPid)
+ results = lsofCall[1].readlines()
+ errResults = lsofCall[2].readlines()
+
+ # checks if lsof was unavailable
+ if "not found" in "".join(errResults):
+ raise Exception("error: lsof is unavailable")
+
+ # if we didn't get any results then tor's probably closed (keep defaults)
+ if len(results) == 0: return
+
torUser = results[1][1:]
results = results[2:] # skip first couple lines (pid listing and user)
# splits descriptors into buckets according to their type
descriptors = [entry[1:].strip() for entry in results] # strips off first character (always an 'n')
+ # checks if read failed due to permission issues
+ isPermissionDenied = True
for desc in descriptors:
+ if "Permission denied" not in desc:
+ isPermissionDenied = False
+ break
+
+ if isPermissionDenied:
+ raise Exception("lsof error: Permission denied")
+
+ for desc in descriptors:
if os.path.exists(desc): self.fdFile.append(desc)
elif desc[0] != "/" and ":" in desc: self.fdConn.append(desc)
else: self.fdMisc.append(desc)
@@ -57,13 +75,15 @@
# uses ulimit to estimate (-H is for hard limit, which is what tor uses)
ulimitCall = os.popen("ulimit -Hn 2> /dev/null")
results = ulimitCall.readlines()
- if len(results) == 0: raise Exception("ulimit is unavailable")
+ if len(results) == 0: raise Exception("error: ulimit is unavailable")
self.fdLimit = int(results[0])
except Exception, exc:
# problem arose in calling or parsing lsof or ulimit calls
- self.errorMsg = "error: " + str(exc)
+ self.errorMsg = str(exc)
finally:
- lsofCall.close()
+ lsofCall[0].close()
+ lsofCall[1].close()
+ lsofCall[2].close()
if ulimitCall: ulimitCall.close()
def handleKey(self, key, height):
@@ -94,7 +114,7 @@
for entry in properties.fdFile + properties.fdConn + properties.fdMisc:
popupWidth = max(popupWidth, len(entry) + 4)
- popupHeight = len(properties.fdFile) + len(properties.fdConn) + len(properties.fdMisc)
+ popupHeight = len(properties.fdFile) + len(properties.fdConn) + len(properties.fdMisc) + 4
popup._resetBounds()
popup.height = popupHeight
@@ -128,7 +148,7 @@
else:
# text with file descriptor count and limit
fdCount = len(properties.fdFile) + len(properties.fdConn) + len(properties.fdMisc)
- fdCountPer = 100 * fdCount / properties.fdLimit
+ fdCountPer = 100 * fdCount / max(properties.fdLimit, 1)
statsColor = "green"
if fdCountPer >= 90: statsColor = "red"
@@ -139,7 +159,8 @@
# provides a progress bar reflecting the stats
barWidth = popup.maxX - len(countMsg) - 6 # space between "[ ]" in progress bar
- barProgress = max(1, barWidth * fdCountPer / 100) # filled cells
+ barProgress = barWidth * fdCountPer / 100 # filled cells
+ if fdCount > 0: barProgress = max(1, barProgress) # ensures one cell is filled unless really zero
popup.addstr(1, len(countMsg) + 3, "[", curses.A_BOLD)
popup.addstr(1, len(countMsg) + 4, " " * barProgress, curses.A_STANDOUT | util.getColor(statsColor))
popup.addstr(1, len(countMsg) + 4 + barWidth, "]", curses.A_BOLD)
Modified: arm/trunk/interface/headerPanel.py
===================================================================
--- arm/trunk/interface/headerPanel.py 2009-11-29 21:02:19 UTC (rev 21061)
+++ arm/trunk/interface/headerPanel.py 2009-11-30 04:12:57 UTC (rev 21062)
@@ -43,6 +43,7 @@
self.conn = conn # Tor control port connection
self.isPaused = False
self.isWide = False # doubles up parameters to shorten section if room's available
+ self.rightParamX = 0 # offset used for doubled up parameters
self.lastUpdate = -1 # time last stats was retrived
self._updateParams()
@@ -52,6 +53,7 @@
self._resetBounds()
self.isWide = self.maxX >= MIN_DUAL_ROW_WIDTH
+ self.rightParamX = max(self.maxX / 2, 75) if self.isWide else 0
self.height = 4 if self.isWide else 6
util.Panel.recreate(self, stdscr, startY, maxX)
@@ -70,13 +72,36 @@
self.clear()
- # Line 1
- self.addstr(0, 0, "arm - %s (%s %s)" % (self.vals["sys-name"], self.vals["sys-os"], self.vals["sys-version"]))
+ # Line 1 (system and tor version information)
+ systemNameLabel = "arm - %s " % self.vals["sys-name"]
+ systemVersionLabel = "%s %s" % (self.vals["sys-os"], self.vals["sys-version"])
+ # wraps systemVersionLabel in parentheses and truncates if too long
+ versionLabelMaxWidth = 40 - len(systemNameLabel)
+ if len(systemNameLabel) > 40:
+ # we only have room for the system name label
+ systemNameLabel = systemNameLabel[:39] + "..."
+ systemVersionLabel = ""
+ elif len(systemVersionLabel) > versionLabelMaxWidth:
+ # not enough room to show full version
+ systemVersionLabel = "(%s...)" % systemVersionLabel[:versionLabelMaxWidth - 3].strip()
+ else:
+ # enough room for everything
+ systemVersionLabel = "(%s)" % systemVersionLabel
+
+ self.addstr(0, 0, "%s%s" % (systemNameLabel, systemVersionLabel))
+
versionStatus = self.vals["status/version/current"]
versionColor = VERSION_STATUS_COLORS[versionStatus] if versionStatus in VERSION_STATUS_COLORS else "white"
- self.addfstr(0, 43, "Tor %s (<%s>%s</%s>)" % (self.vals["version"], versionColor, versionStatus, versionColor))
+ # truncates torVersionLabel if too long
+ torVersionLabel = self.vals["version"]
+ versionLabelMaxWidth = (self.rightParamX if self.isWide else self.maxX) - 51 - len(versionStatus)
+ if len(torVersionLabel) > versionLabelMaxWidth:
+ torVersionLabel = torVersionLabel[:versionLabelMaxWidth - 1].strip() + "-"
+
+ self.addfstr(0, 43, "Tor %s (<%s>%s</%s>)" % (torVersionLabel, versionColor, versionStatus, versionColor))
+
# Line 2 (authentication label red if open, green if credentials required)
dirPortLabel = "Dir Port: %s, " % self.vals["DirPort"] if self.vals["DirPort"] != "0" else ""
@@ -89,14 +114,14 @@
self.addfstr(1, 0, "%s<%s>%s</%s>): %s" % (labelStart, controlPortAuthColor, controlPortAuthLabel, controlPortAuthColor, self.vals["ControlPort"]))
# Line 3 (system usage info) - line 1 right if wide
- y, x = 0 if self.isWide else 2, 75 if self.isWide else 0
+ y, x = (0, self.rightParamX) if self.isWide else (2, 0)
self.addstr(y, x, "cpu: %s%%" % self.vals["%cpu"])
self.addstr(y, x + 13, "mem: %s (%s%%)" % (util.getSizeLabel(int(self.vals["rss"]) * 1024), self.vals["%mem"]))
self.addstr(y, x + 34, "pid: %s" % (self.vals["pid"] if self.vals["etime"] else ""))
self.addstr(y, x + 47, "uptime: %s" % self.vals["etime"])
# Line 4 (fingerprint) - line 2 right if wide
- y, x = 1 if self.isWide else 3, 75 if self.isWide else 0
+ y, x = (1, self.rightParamX) if self.isWide else (3, 0)
self.addstr(y, x, "fingerprint: %s" % self.vals["fingerprint"])
# Line 5 (flags) - line 3 left if wide
@@ -108,7 +133,7 @@
if len(self.vals["flags"]) > 0: flagLine = flagLine[:-2]
self.addfstr(2 if self.isWide else 4, 0, flagLine)
- # Line 3 right (exit policy) - not present if not wide
+ # Line 3 right (exit policy) - only present if wide
if self.isWide:
exitPolicy = self.vals["ExitPolicy"]
@@ -130,7 +155,7 @@
policies[i] = policy
exitPolicy = ", ".join(policies)
- self.addfstr(2, 75, "exit policy: %s" % exitPolicy)
+ self.addfstr(2, self.rightParamX, "exit policy: %s" % exitPolicy)
self.refresh()
finally: