[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] r20674: {arm} Few issues discussed on irc. added: changelog and cleaned up (in arm/trunk: . interface)
Author: atagar
Date: 2009-09-27 04:50:41 -0400 (Sun, 27 Sep 2009)
New Revision: 20674
Added:
arm/trunk/ChangeLog
arm/trunk/README
arm/trunk/TODO
Removed:
arm/trunk/readme.txt
Modified:
arm/trunk/arm.py
arm/trunk/interface/bandwidthMonitor.py
arm/trunk/interface/connPanel.py
arm/trunk/interface/controller.py
arm/trunk/interface/cpuMemMonitor.py
arm/trunk/interface/headerPanel.py
arm/trunk/interface/hostnameResolver.py
Log:
Few issues discussed on irc.
added: changelog and cleaned up todo documents (requested by arma)
added: option in controller.py to disable connection panel (feature request by Sebastian)
fix: failed to work on osx and bsd due to crashes after failed system calls (caught by Sebastian and Christopher Davis)
fix: reloading static data in bandwidth panel after HUP (caught by hexa)
fix: couple alignment issues with the connection listings
Added: arm/trunk/ChangeLog
===================================================================
--- arm/trunk/ChangeLog (rev 0)
+++ arm/trunk/ChangeLog 2009-09-27 08:50:41 UTC (rev 20674)
@@ -0,0 +1,226 @@
+CHANGE LOG
+
+9/27/09 - version 1.1.2
+Few issues discussed on irc.
+
+ * added: changelog and cleaned up todo documents (requested by arma)
+ * added: option in controller.py to disable connection panel (feature request by Sebastian)
+ * fix: failed to work on osx and bsd due to crashes after failed system calls (caught by Sebastian and Christopher Davis)
+ * fix: reloading static data in bandwidth panel after HUP (caught by hexa)
+ * fix: couple alignment issues with the connection listings
+
+9/23/09 - version 1.1.1 (r20655)
+Bundle of semi-low hanging fruit, including a few issues discussed on irc.
+
+ * added: showing extra parameters in connection listings if room's available
+ * added: identifying directory server connections
+ * change: providing an error message if running an incompatible python version (issue spotted by arma)
+ * change: giving arm a version to help in bug reports
+ * change: minor tweak to the wording of a faq entry (requested by Sebastian)
+ * fix: wasn't accounting for RelayBandwidthRate/Burst in effective bandwidth (caught by hexa and arma)
+ * fix: timing issue when shutting down (caught by arma)
+ * fix: couple issues with connection time being tracked when paused
+ * fix: preserving old results when netstat fails
+
+9/6/09 - r20493
+Several substantial features (last tasks for arm's todo list).
+
+ * added: scroll bars for connections listing and event log
+ * added: made log scrollable (feature request by StrangeCharm)
+ * added: regular expression filtering for log (feature request by StrangeCharm)
+ * added: connection uptimes (time since connection was first made)
+ * added: identifying client from server connections and providing popup for client circuits
+ * added: graph for system resource usage (cpu/memory)
+ * change: removed cursor toggling option for connection page
+ * fix: minor display issue when changing event types
+
+8/22/09 - r20354
+Several fixes and changes, mostly concerning the graph panel and making better use of screen real estate.
+
+ * added: labeled the graph's x-axis and reordered the information with changes omitted for small (tty sized) terminals (feature request by StrangeCharm)
+ * added: doubling up contents of header panel in case of wide screens to take advantage of added space
+ * added: exit policy to header if a wide display
+ * change: added precision for bandwidth measurements
+ * change: using "orconn-status" info to eliminated ambiguity in identifying inbound connection fingerprints (clever idea, but had very little impact)
+ * fix: when sighup signal is received reloads torrc and internal state (caught by StrangeCharm)
+ * fix: probable resolution of nasty concurrent bug concerning access to connection cache
+ * fix: minor issues concerning connection panel including graph widths and miscalculating local maxima
+ * fix: short circuits fingerprint cache when looking up localhost descriptor (preventing lookup failures)
+ * fix: minor issues with connection panel and description popups when no connections are available
+ * fix: descriptor popup wasn't determining if the first visible line belonged to an encryption block
+ * fix: made interface more resilient against arbitrary resizing (such as during popups)
+
+8/17/09 - r20331
+Work done over this last week.
+
+ * added: popup for raw consensus description
+ * added: total bandwidth measurement (feature request by StrangeCharm)
+ * added: connection entry for lookup of local consensus data
+ * change: widened graphs to utilize full screen width (clever idea by StrangeCharm)
+ * change: preserving runtime and pid when shutting down
+ * change: few tweaks to the readme
+ * fix: joining on worker daemon threads to exit gracefully (had a noisy race condition)
+ * fix: using BW events to keep connection count graph in sync with bandwidth graph
+ * fix: can now support graphs of multiple sizes
+
+8/8/09 - r20233
+Rewrote graph panel so it can handle any real time statistics.
+
+ * added: option to graph connection counts (feature request by phobos)
+ * added: custom graph bounds (global or local maxima)
+
+8/4/09
+Announced the project on the or-talk mailing list today which spurred an interview with Brenno Winter (who works on the cleverly named Little Sister project). The interview is available here.
+
+8/3/09 - r20210
+Added start of a faq to the readme in preparation for announcement on or-talk.
+
+7/30/09 - r20198
+Work done over the trip.
+
+ * added: customizable update interval for bandwidth graph (feature request by StrangeCharm)
+ * change: noted new project page in the readme (www.atagar.com/arm)
+ * change: added word wrapping to conf panel
+ * change: added function for custom popup menus
+ * change: logs error message when required event types are unsupported rather than throwing an exception
+ * change: using different screenshot images
+ * fix: resolved issue that caused monitor to think tor was resumed when quit
+ * fix: bug with panel utility's resize detection
+ * fix: resorts connections after NEWDESC and NEWCONSENSUS events
+ * fix: forgetting to to resume monitor at multiple points after a temporary pause
+ * fix: minor refactoring based on suggestions from pylint (unused imports and such)
+
+7/22/09 - r20115
+Another small grab bag update.
+
+ * added: version status to header panel
+ * change: noted "Common *nix commands including: ps, pidof, host, and netstat" among requirements in readme
+ * change: took some tricks from Mike's ConsensusTracker to further improve match rate
+ * fix: type mismatch that greatly diminished fingerprint matching
+ * fix: accidentally used idhash rather than idhex for fingerprints when updating cache with the contents of a NEWDESC event
+
+7/21/09 - r20100
+Quick fixes based on discussion on irc.
+
+ * change: provides warning when geoip database is unavailable (thanks to SwissTorExit and karsten)
+ * fix: missing import for the socket module
+
+7/20/09 - r20096, r20097, r20098
+Couple fixes so arm plays nicely in the case of multiple running tor instances.
+
+ * fix: can now deal with multiple tor instances: checks pid of process with the open control port
+ * fix: if only one tor process is running use that pid (netstat fails if running as a different user
+
+7/19/09 - r20087, r20090
+Last substantial feature on my to-do list.
+
+ * added: connections can be selected to view consensus details (very spiffy!)
+ * added: listing selection is by menu rather than cycling
+ * fix: couple bugs, the most interesting being when netstat can't resolve a connections listing (spotted by phobos)
+
+7/18/09 - r20078, r20079
+Miscellaneous fix and feature batch.
+
+ * added: relay's flags to the header
+ * added: listing by relay nickname
+ * added: additional event aliases and option for NEWCONSENSUS
+ * added (phobos): screenshot of arm in action so people can see what it looks like
+ * change: use constant "Listing" label for sorting rather than current view
+ * change: removed 'reload torrc' option (deceptive and useless)
+ * fix: updates cached consensus mappings with NEWDESC and NEWCONSENSUS events
+
+7/14/09 - r20016
+Resolved a few quick bugs:
+
+ * fix: added fingerprint lookup cache to resolve substantial performance issue
+ * fix: hostname resolution progress accounts for newly added entries (no more negative progress)
+ * fix: resolved bug that prevented arm from starting if too small
+ * fix: ordering issue when sorting unresolved ip addresses
+
+7/11/09 - r19975
+Connections panel can now list by IP, hostname, or fingerprint: reverse resolution was easy, but comparing three different implementations and making it non-blocking with a pausable thread-pool backend? Not so much.
+
+7/8/09 - r19953, r19957
+Just got back from Toorcamp. Preliminary connection page and miscellaneous additions.
+
+ * added: basic connection listing page (using netstat results)
+ * added: connection listing now has user configurable sort functionality (it's actually pretty spiffy: supports secondary and tertiary sub-keys)
+ * added: 'addfstr' to util which allows for embedded formatting tags (VERY helpful)
+ * added: help shows page's current settings
+ * added: made bandwidth panel toggleable
+ * added: avg bandwidth to bottom of panel
+ * fix: prevented header from being paused on page change
+ * fix: prevented bandwidth accounting events from being lost when paused
+
+6/14/09 - r19716
+Decently big batch of feature additions and bug fixes.
+
+ * added: second page that presents torrc with syntax highlighting, optional comment stripping, etc
+ * added: ps sampling (cpu/memory usage, pid, and uptime)
+ * added: help popup with page controls
+ * fix: corrected issue that caused periodic refreshing to fail
+ * fix: accounting reset time takes into account DST
+ * fix: make accounting input and header pausable
+
+6/10/09 - r19708, r19709
+Couple quick changes.
+
+ * change: removed '--path-to-torctl' startup option
+ * fix: accounting 'time to reset' now includes gmt to local conversion
+
+6/7/09 - r19646, r19655, r19656
+Couple features.
+
+ * added: svn external inclusion of TorCtl
+ * added: bandwidth panel now displays accounting data if set
+
+6/6/09 - r19636, r19637
+Quick change based on discussion on irc.
+
+ * added: command line argument to specify location of TorCtl without changing Python path first (feature request by phobos)
+
+6/5/09 - r19629
+Substantial refactoring changes.
+
+ * change: switched from a functional to an OO implementation which further simplified the controller: as an added plus this should make adding additional 'pages' trivial
+ * change: offloaded resizing to the curses wrapper
+ * fix: dealt with another curses wtf bug where panels wouldn't repaint unless done in a specific order
+
+6/4/09 - r19626
+Tested and corrected formatting for all event types except STREAM and STREAM_BW (not sure how to make those occur...).
+
+6/2/09 - r19615, r19619, r19620
+Introduced layer of abstraction from curses, simplifying its use and greatly improving reliability.
+
+ * added: introduced wrapper to hide curses ugliness which greatly simplified interface code
+ * added: notice when relay's been silent for five seconds (based on BW events so probably due to Tor being closed), another idea by karsten
+ * changed: unchecked events have stubs to present information and provide debugging information in case of type mismatch
+ * fix: all problems with resizing: it's now rock solid
+
+5/29/09 - r19580, r19594
+Fixes for several rather sinister reliability problems:
+
+ * added: allows logged events to be changed while running (suggested feature by karsten) and experimenting with a more modular design
+ * fix: added non-blocking reentrant locks to fix concurrency errors that caused chaotic terminal glitches (such as switching to a Chinese character set)
+ * fix: now fully handles resizing (including vertical)
+ * fix: using new capabilities in TorCtl including cookie authentication and disabling logging
+ * fix: bandwidth graph bug when paused
+ * fix: occasionally refreshes static content in case of graphical hiccups
+ * fix: added workaround for obscure curses caching bug that prevented portions of the screen from being redrawn
+ * fix: bug preventing initialization if too small
+
+5/25/09 - r19567
+Few small tweaks including:
+
+ * added: tiny shell script to alias starting
+ * added: more informative error message if TorCtl isn't available
+ * change: defaultly logged events
+ * change: make inclusion of 'unknown' events toggleable
+
+5/24/09 - r19548, r19549, r19550, r19551
+Initial version of arm (terminal relay status monitor). Repository set up by arma.
+
+ * fix: bug concerning undefined exit policy
+ * fix: resolved issue that prevented monitor from functioning in terminals without curs_set support
+
+
Copied: arm/trunk/README (from rev 20655, arm/trunk/readme.txt)
===================================================================
--- arm/trunk/README (rev 0)
+++ arm/trunk/README 2009-09-27 08:50:41 UTC (rev 20674)
@@ -0,0 +1,84 @@
+arm (anonymizing relay monitor) - Terminal status monitor for Tor relays.
+Developed by Damian Johnson (www.atagar.com - atagar1@xxxxxxxxx)
+All code under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
+Project page: www.atagar.com/arm
+
+Description:
+Command line application for monitoring Tor relays, providing real time status
+information such as the current configuration, bandwidth usage, message log,
+connections, etc. This uses a curses interface much like 'top' does for system
+usage. The application is intended for command-line aficionados, ssh
+connections, and anyone stuck with a tty terminal for checking their relay's
+status. Releases should be stable so if you manage to make it crash (or have a
+feature request) then please let me know!
+
+The project was originally proposed in 2008 by Jacob and Karsten:
+ http://archives.seul.org/or/dev/Jan-2008/msg00005.html
+
+An interview by Brenno Winter discussing the project is available at:
+ http://www.atagar.com/arm/HFM_INT_0001.mp3
+
+Requirements:
+Python 2.5
+TorCtl (retrieved in svn checkout)
+Common *nix commands including: ps, pidof, host, and netstat
+Tor is running with an available control port. This means either...
+ ... starting Tor with '--controlport <PORT>'
+ ... or including 'ControlPort <PORT>' in your torrc
+
+This is started via 'arm' (use the '--help' argument for usage).
+
+FAQ:
+> Why is it called 'arm'?
+
+Simple - because it makes the command short and memorable. Terminal
+applications need to be easy to type (like 'top', 'ssh', etc), and anything
+longer is just begging command-line aficionados to alias it down. I chose the
+meaning of the acronym ('anonymizing relay monitor') afterward.
+
+> If you're listing connections then what about exit nodes? Won't this include
+people's traffic?
+
+While arm isn't intended to be a sniffer it does provide real time connection
+data which, for exit nodes, includes the endpoints being visited through you.
+Unfortunately this is pretty unavoidable. The control port doesn't provide a
+means of distinguishing those connections and trying to figure it out by
+correlating against consensus data has proved pretty inaccurate.
+
+That said, this really isn't much of a concern. For Tor users the real threats
+come from things like Wireshark and MITM attacks on their unencrypted traffic.
+Simply seeing an unknown individual's endpoints is no great feat in itself.
+Just attach netstat to a cron job and voilà! You've got a sniffer that's just
+as mighty as arm.
+
+> Is it harmful to share the information provided by arm?
+
+Not really, but it's discouraged. The original plan for arm included a special
+emphasis that it wouldn't log any data. The reason is that if a large number
+of relay operators published the details of their connections then correlation
+attacks could break Tor user's anonymity. Just show some moderation in what
+you share and it should be fine.
+
+> Is there any chance that arm will leak data?
+
+Yes - arm is a passive listener with one exception. The second page
+(connections) provides the hostnames of Tor relays you're connected to. This
+means reverse DNS lookups which, if monitored, could leak your current
+connections to an eavesdropper. However, lookups are only made upon request
+(when showing connection details or listing connections by hostname) and you
+can disable lookups entirely with 'r' - see the page's help for the current
+status.
+
+That said, this is not a terribly big whoop. ISPs and anyone sniffing your
+connection already has this data - the only difference is that instead of
+saying "I am talking to x" you're saying "I'm talking to x, who's x?", meaning
+the resolver's also aware of who they are.
+
+> When arm starts it gives "Unable to resolve tor pid, abandoning connection
+listing"... why?
+
+If you're running multiple instances of tor then arm needs to figure out which
+pid belongs to the open control port. If it's running as a different user
+(such as being in a chroot jail) then it's probably failing due to permission
+issues. Arm still runs, just no connection listing or ps stats.
+
Added: arm/trunk/TODO
===================================================================
--- arm/trunk/TODO (rev 0)
+++ arm/trunk/TODO 2009-09-27 08:50:41 UTC (rev 20674)
@@ -0,0 +1,122 @@
+TODO
+
+- Bugs
+ * make netstat lookups a best-effort service (separate from draw thread)
+ Call appears to be heavier than expected and causing display to be
+ unusable on especially active relays (like directory servers).
+ caught by arma and StrangeCharm, notify coderman for testing
+ * Mac OSX and BSD may have issues with netstat options
+ Reported that they aren't cross platform. Possibly use lsof as a
+ fallback if an issue's detected.
+ caught by Christopher Davis
+ * quitting can hang several seconds when there's hostnames left to resolve
+ 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)?
+ * connection details covers right side
+ * version labels provided on Debian are longer than expected
+ caught by hexa
+ * unable to load torrc if it was loaded via a relative path
+ When tor's started via "tor -f <relative path>" we don't know what it's
+ relative of - check to see if there's a way of finding the pwd of
+ another process.
+ caught by arma
+ * new connections don't have uptime tracked when not visible
+ Previous fix attempted to resolve, but evidently didn't work.
+
+- Features / Site
+ * provide observed bandwidth
+ Newer relays have a 'w' entry that states the bandwidth and old versions
+ have client side measurements (third argument in 'Bandwidth' of
+ descriptor, note that it's in KB/s). Label the former (server side) as
+ 'Measured' and later (client side) as 'Observed' to differentiate.
+ 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
+ * update site's screenshots (pretty out of date...)
+ * add arm to listings of support programs
+ https://wiki.torproject.org/noreply/TheOnionRouter/SupportPrograms
+ https://www.torproject.org/projects/
+
+- Ideas (low priority)
+ * provide performance ARM-DEBUG events
+ Might help with debugging bottlenecks. This requires that there's more
+ refined controls for selecting logged arm runlevel.
+ * show qos stats
+ Take a look at 'linux-tor-prio.sh' to see if any of the stats are
+ available and interesting.
+ * get a test environment for Mac OSX or BSD
+ Set up a vm for FreeBSD but found working in it to be... painful (wasted
+ five hours and gave up when even asking for a working copy of vim was
+ too much to ask). As for OSX seems that getting a test environment would
+ cost quite a bit. Hence mothballing this - someone that actually uses
+ these platforms will need to resolve portability issues if they arise.
+ * localization
+ Abstract strings from code and provide on translation portal. Thus far
+ there hasn't been any requests for this.
+ * provide option for a consensus page
+ Shows full consensus with an interface similar to the connection panel.
+ For this Mike's ConsensusTracker would be helpful (though boost the
+ startup time by several seconds)
+ * provide Debian repository for arm
+ Look into debian packaging, note system call dependencies, and mail
+ submit@xxxxxxxxxxxxxxx with subject "RFP: arm" and starting with a line
+ "Package: wnpp".
+ requested by helmut
+
+- Control Protocol Wishlist (low priority)
+ * listing of tor's current connections (netstat / lsof replacement)
+ Keeping the netstat available would be good for auditing (external view
+ of tor and more likely monitored by host based IDS) but tor's listing
+ would probably be more effecient, accurate, and could contain additional
+ details making it a preferable default.
+ * bandwidth usage per connection
+ This would need to be rounded and averaged over time to avoid
+ correlation problems. Probably the most interesting stat arm currently
+ doesn't have since for most purposes (like security threats) especially
+ active connections are of most interest.
+ * identification of hop type
+ Identification if the first, middle or last hop. When this is available
+ I'll hide exit connections by default. Another interesting distinction
+ would be when we're serving directory data verses acting as a relay.
+ * associate connections to circuits
+ Currently listing is connection based rather than circuit, ie it lists:
+ previous hop -> localhost
+ previous hop -> localhost
+ localhost -> next hop
+
+ rather than:
+ previous hop -> localhost -> next hop
+ previous hop -> localhost -> *unestablished*
+
+ From a debugging and secuirty standpoint this could highlight potential
+ issues, for instance relays really shouldn't have any non-client
+ connections like:
+ *unestablished* -> localhost -> next hop
+
+ and entries like:
+ previous hop -> localhost -> *extension failed (error X)*
+
+ might indicate a firewall blocking tor outbound connections. This would
+ be especially helpful if paired with server related circuit status
+ events (which would note attempted extensions, failures, etc). We could
+ also note other circuit based stats like the amount of buffered data.
+ * mapping of ip/port to fingerprint
+ Currently inferring the mappings but this only has around a 90% success
+ rate (not sure why it fails...). Tor has an internal connection
+ identifier so what would probably be best is bidirectional translation
+ functions with that, ie getting fingerprint would be done via:
+ ip/port -> connection id -> fingerprint
+
+ In theory this should be able to tell us if the connection is the first
+ or last hop (since in those cases the foreign address doesn't have a
+ fingerprint).
+ * additional get_info data
+ effective relay bandwidth / burst - currently internally mimicing the
+ logic of tor (which is RelayBandwidthRate/Burst if set, otherwise
+ BandwidthRate/Burst)
+ list of directory authorities recognized by that instance of tor
+ total data relayed by tor - this is already kinda tracked for accounting
+
Modified: arm/trunk/arm.py
===================================================================
--- arm/trunk/arm.py 2009-09-27 07:58:53 UTC (rev 20673)
+++ arm/trunk/arm.py 2009-09-27 08:50:41 UTC (rev 20674)
@@ -19,8 +19,8 @@
from interface import controller
from interface import logPanel
-VERSION = "1.1.1"
-LAST_MODIFIED = "Sep 23, 2009"
+VERSION = "1.1.2"
+LAST_MODIFIED = "Sep 27, 2009"
DEFAULT_CONTROL_ADDR = "127.0.0.1"
DEFAULT_CONTROL_PORT = 9051
Modified: arm/trunk/interface/bandwidthMonitor.py
===================================================================
--- arm/trunk/interface/bandwidthMonitor.py 2009-09-27 07:58:53 UTC (rev 20673)
+++ arm/trunk/interface/bandwidthMonitor.py 2009-09-27 08:50:41 UTC (rev 20674)
@@ -27,18 +27,29 @@
self.conn = conn # Tor control port connection
self.accountingInfo = None # accounting data (set by _updateAccountingInfo method)
- if conn:
- self.isAccounting = conn.get_info('accounting/enabled')['accounting/enabled'] == '1'
+ # dummy values for static data
+ self.isAccounting = False
+ self.bwRate, self.bwBurst = -1, -1
+ self.resetStaticData()
+
+ def resetStaticData(self):
+ """
+ Checks with tor for static bandwidth parameters (rates, accounting
+ information, etc).
+ """
+
+ try:
+ if not self.conn: raise ValueError
+ self.isAccounting = self.conn.get_info('accounting/enabled')['accounting/enabled'] == '1'
# static limit stats for label, uses relay stats if defined (internal behavior of tor)
- bwStats = conn.get_option(['BandwidthRate', 'BandwidthBurst'])
- relayStats = conn.get_option(['RelayBandwidthRate', 'RelayBandwidthBurst'])
+ bwStats = self.conn.get_option(['BandwidthRate', 'BandwidthBurst'])
+ relayStats = self.conn.get_option(['RelayBandwidthRate', 'RelayBandwidthBurst'])
self.bwRate = util.getSizeLabel(int(bwStats[0][1] if relayStats[0][1] == "0" else relayStats[0][1]))
self.bwBurst = util.getSizeLabel(int(bwStats[1][1] if relayStats[1][1] == "0" else relayStats[1][1]))
- else:
- self.isAccounting = False
- self.bwRate, self.bwBurst = -1, -1
+ except (ValueError, TorCtl.TorCtlClosed):
+ pass # keep old values
# this doesn't track accounting stats when paused so doesn't need a custom pauseBuffer
contentHeight = 13 if self.isAccounting else 10
Modified: arm/trunk/interface/connPanel.py
===================================================================
--- arm/trunk/interface/connPanel.py 2009-09-27 07:58:53 UTC (rev 20673)
+++ arm/trunk/interface/connPanel.py 2009-09-27 08:50:41 UTC (rev 20674)
@@ -122,6 +122,7 @@
self.orconnStatusCacheValid = False # indicates if cache has been invalidated
self.clientConnectionCache = None # listing of nicknames for our client connections
self.clientConnectionLock = RLock() # lock for clientConnectionCache
+ self.isDisabled = False # prevent panel from updating entirely
self.isCursorEnabled = True
self.cursorSelection = None
@@ -205,7 +206,7 @@
Reloads netstat results.
"""
- if not self.pid: return
+ if not self.pid or self.isDisabled: return
self.connectionsLock.acquire()
self.clientConnectionLock.acquire()
@@ -432,7 +433,7 @@
src = "localhost:%-5s" % entry[CONN_L_PORT]
# space available for foreign hostname (stretched to claim any free space)
- foreignHostnameSpace = self.maxX - len(self.nickname) - 38
+ foreignHostnameSpace = self.maxX - 42 - xOffset
etc = ""
if self.maxX > 102 + xOffset:
@@ -448,7 +449,7 @@
if self.maxX > 151 + xOffset:
# show nickname (column width: min 17 characters, uses half of the remainder)
nickname = self.getNickname(entry[CONN_F_IP], entry[CONN_F_PORT])
- nicknameSpace = 15 + (self.maxX - 151) / 2
+ nicknameSpace = 15 + (self.maxX - xOffset - 151) / 2
foreignHostnameSpace -= (nicknameSpace + 2)
if len(nickname) > nicknameSpace: nickname = "%s..." % nickname[:nicknameSpace - 3]
@@ -488,7 +489,7 @@
else: dst = self.getNickname(entry[CONN_F_IP], entry[CONN_F_PORT])
# space available for foreign nickname
- foreignNicknameSpace = self.maxX - len(self.nickname) - 27
+ foreignNicknameSpace = self.maxX - len(self.nickname) - 27 - xOffset
etc = ""
if self.maxX > 92 + xOffset:
Modified: arm/trunk/interface/controller.py
===================================================================
--- arm/trunk/interface/controller.py 2009-09-27 07:58:53 UTC (rev 20673)
+++ arm/trunk/interface/controller.py 2009-09-27 08:50:41 UTC (rev 20674)
@@ -25,6 +25,7 @@
import cpuMemMonitor
import connCountMonitor
+DISABLE_CONNECTIONS_PAGE = False
REFRESH_RATE = 5 # seconds between redrawing screen
cursesLock = RLock() # global curses lock (curses isn't thread safe and
# concurrency bugs produce especially sinister glitches)
@@ -89,7 +90,14 @@
msgText = "Resolving hostnames (%i / %i, %i%%) - press esc %sto cancel" % (entryCount, batchSize, progress, additive)
if self.resolvingCounter == -1:
- msgText = "page %i / %i - q: quit, p: pause, h: page help" % (self.page, len(PAGES))
+ currentPage = self.page
+ pageCount = len(PAGES)
+
+ if DISABLE_CONNECTIONS_PAGE:
+ if currentPage >= 2: currentPage -= 1
+ pageCount -= 1
+
+ msgText = "page %i / %i - q: quit, p: pause, h: page help" % (currentPage, pageCount)
elif msgText == CTL_PAUSED:
msgText = "Paused"
msgAttr = curses.A_STANDOUT
@@ -233,14 +241,16 @@
# gets pid of tor instance with control port open
torPid = None # None if couldn't be resolved (provides error later)
- pidOfCall = os.popen("pidof tor")
- netstatCall = None
+ pidOfCall = os.popen("pidof tor 2> /dev/null")
try:
# gets pid if there's only one possability
results = pidOfCall.readlines()
-
if len(results) == 1 and len(results[0].split()) == 1: torPid = results[0].strip()
- else:
+ except IOError: pass # pid call failed
+ pidOfCall.close()
+
+ if not torPid:
+ try:
# uses netstat to identify process with open control port (might not
# work if tor's being run as a different user due to permissions)
netstatCall = os.popen("netstat -npl 2> /dev/null | grep 127.0.0.1:%s" % conn.get_option("ControlPort")[0][1])
@@ -249,11 +259,9 @@
if len(results) == 1:
results = results[0].split()[6] # process field (ex. "7184/tor")
torPid = results[:results.find("/")]
- except IOError: pass # netstat call failed
+ except IOError: pass # netstat call failed
+ netstatCall.close()
- pidOfCall.close()
- if netstatCall: netstatCall.close()
-
panels = {
"header": headerPanel.HeaderPanel(cursesLock, conn, torPid),
"popup": util.Panel(cursesLock, 9),
@@ -263,6 +271,9 @@
panels["conn"] = connPanel.ConnPanel(cursesLock, conn, torPid, panels["log"])
panels["control"] = ControlPanel(cursesLock, panels["conn"].resolver)
+ # prevents netstat calls by connPanel if not being used
+ if DISABLE_CONNECTIONS_PAGE: panels["conn"].isDisabled = True
+
# provides error if pid coulnd't be determined (hopefully shouldn't happen...)
if not torPid: panels["log"].monitor_event("WARN", "Unable to resolve tor pid, abandoning connection listing")
@@ -303,6 +314,12 @@
# if sighup received then reload related information
if sighupTracker.isReset:
panels["header"]._updateParams(True)
+ panels["graph"].stats["bandwidth"].resetStaticData()
+
+ # if bandwidth graph is being shown then height might have changed
+ if panels["graph"].currentDisplay == "bandwidth":
+ panels["graph"].height = panels["graph"].stats["bandwidth"].height
+
panels["torrc"].reset()
sighupTracker.isReset = False
@@ -364,6 +381,11 @@
if key == curses.KEY_LEFT: page = (page - 1) % len(PAGES)
else: page = (page + 1) % len(PAGES)
+ # skip connections listing if it's disabled
+ if page == 1 and DISABLE_CONNECTIONS_PAGE:
+ if key == curses.KEY_LEFT: page = (page - 1) % len(PAGES)
+ else: page = (page + 1) % len(PAGES)
+
# pauses panels that aren't visible to prevent events from accumilating
# (otherwise they'll wait on the curses lock which might get demanding)
setPauseState(panels, isPaused, page)
Modified: arm/trunk/interface/cpuMemMonitor.py
===================================================================
--- arm/trunk/interface/cpuMemMonitor.py 2009-09-27 07:58:53 UTC (rev 20673)
+++ arm/trunk/interface/cpuMemMonitor.py 2009-09-27 08:50:41 UTC (rev 20674)
@@ -30,7 +30,7 @@
else:
# cached results stale - requery ps
inbound, outbound, control = 0, 0, 0
- psCall = os.popen('ps -p %s -o %s' % (self.headerPanel.vals["pid"], "%cpu,rss"))
+ psCall = os.popen('ps -p %s -o %s 2> /dev/null' % (self.headerPanel.vals["pid"], "%cpu,rss"))
try:
sampling = psCall.read().strip().split()[2:]
psCall.close()
@@ -41,8 +41,9 @@
else:
self._processEvent(float(sampling[0]), float(sampling[1]) / 1024.0)
except IOError:
- # ps call failed
- self.connectionPanel.monitor_event("WARN", "Unable to query ps for resource usage")
+ # ps call failed - we need to register something (otherwise timescale
+ # would be thrown off) so keep old results
+ self._processEvent(self.lastPrimary, self.lastSecondary)
def getTitle(self, width):
return "System Resources:"
Modified: arm/trunk/interface/headerPanel.py
===================================================================
--- arm/trunk/interface/headerPanel.py 2009-09-27 07:58:53 UTC (rev 20673)
+++ arm/trunk/interface/headerPanel.py 2009-09-27 08:50:41 UTC (rev 20674)
@@ -58,79 +58,83 @@
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"]))
-
- 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))
-
- # Line 2 (authentication label red if open, green if credentials required)
- dirPortLabel = "Dir Port: %s, " % self.vals["DirPort"] if self.vals["DirPort"] != "0" else ""
-
- 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.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
- 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
- self.addstr(y, x, "fingerprint: %s" % self.vals["fingerprint"])
-
- # Line 5 (flags) - line 3 left if wide
- flagLine = "flags: "
- for flag in self.vals["flags"]:
- flagColor = FLAG_COLORS[flag] if flag in FLAG_COLORS.keys() else "white"
- flagLine += "<b><%s>%s</%s></b>, " % (flagColor, flag, flagColor)
-
- 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
- if self.isWide:
- exitPolicy = self.vals["ExitPolicy"]
+ if not self.lock.acquire(False): return
+ try:
+ if not self.isPaused: self._updateParams()
- # 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>"
+ # 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()
- policies = exitPolicy.split(", ")
+ self.clear()
- # color codes accepts to be green, rejects to be red, and default marker to be cyan
- isSimple = len(policies) <= 2 # if policy is short then it's kept verbose, otherwise 'accept' and 'reject' keywords removed
- for i in range(len(policies)):
- policy = policies[i].strip()
- displayedPolicy = policy if isSimple else policy.replace("accept", "").replace("reject", "").strip()
- if policy.startswith("accept"): policy = "<green><b>%s</b></green>" % displayedPolicy
- elif policy.startswith("reject"): policy = "<red><b>%s</b></red>" % displayedPolicy
- elif policy.startswith("<default>"): policy = "<cyan><b>%s</b></cyan>" % displayedPolicy
- policies[i] = policy
- exitPolicy = ", ".join(policies)
+ # Line 1
+ self.addstr(0, 0, "arm - %s (%s %s)" % (self.vals["sys-name"], self.vals["sys-os"], self.vals["sys-version"]))
- self.addfstr(2, 75, "exit policy: %s" % exitPolicy)
-
- self.refresh()
+ 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))
+
+ # Line 2 (authentication label red if open, green if credentials required)
+ dirPortLabel = "Dir Port: %s, " % self.vals["DirPort"] if self.vals["DirPort"] != "0" else ""
+
+ 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.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
+ 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
+ self.addstr(y, x, "fingerprint: %s" % self.vals["fingerprint"])
+
+ # Line 5 (flags) - line 3 left if wide
+ flagLine = "flags: "
+ for flag in self.vals["flags"]:
+ flagColor = FLAG_COLORS[flag] if flag in FLAG_COLORS.keys() else "white"
+ flagLine += "<b><%s>%s</%s></b>, " % (flagColor, flag, flagColor)
+
+ 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
+ if self.isWide:
+ 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>"
+
+ policies = exitPolicy.split(", ")
+
+ # color codes accepts to be green, rejects to be red, and default marker to be cyan
+ isSimple = len(policies) <= 2 # if policy is short then it's kept verbose, otherwise 'accept' and 'reject' keywords removed
+ for i in range(len(policies)):
+ policy = policies[i].strip()
+ displayedPolicy = policy if isSimple else policy.replace("accept", "").replace("reject", "").strip()
+ if policy.startswith("accept"): policy = "<green><b>%s</b></green>" % displayedPolicy
+ elif policy.startswith("reject"): policy = "<red><b>%s</b></red>" % displayedPolicy
+ elif policy.startswith("<default>"): policy = "<cyan><b>%s</b></cyan>" % displayedPolicy
+ policies[i] = policy
+ exitPolicy = ", ".join(policies)
+
+ self.addfstr(2, 75, "exit policy: %s" % exitPolicy)
+
+ self.refresh()
+ finally:
+ self.lock.release()
def setPaused(self, isPause):
"""
@@ -155,6 +159,8 @@
infoFields = ["address", "fingerprint"] # keys for which get_info will be called
if len(self.vals) <= 1 or forceReload:
+ isConnClosed = False
+
# first call (only contasns 'pid' mapping) - retrieve static params
infoFields += ["version", "status/version/current"]
@@ -166,14 +172,24 @@
# 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]))
+ try: self.vals.update(dict([(key, self.conn.get_option(key)[0][1]) for key in configFields]))
+ except TorCtl.TorCtlClosed: isConnClosed = True
# 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"
+ try:
+ 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"
+ except TorCtl.TorCtlClosed: isConnClosed = True
- self.vals["IsAccountingEnabled"] = self.conn.get_info('accounting/enabled')['accounting/enabled'] == "1"
-
+ if isConnClosed:
+ # tor connection closed - keep old values if available, otherwise set to empty string / false
+ for field in configFields:
+ if field not in self.vals: self.vals[field] = ""
+
+ for field in ["IsPasswordAuthSet", "IsCookieAuthSet", "IsAccountingEnabled"]:
+ if field not in self.vals: self.vals[field] = False
+
# gets parameters that throw errors if unavailable
for param in infoFields:
try: self.vals.update(self.conn.get_info(param))
@@ -191,7 +207,7 @@
psParams = ["%cpu", "rss", "%mem", "etime"]
if self.vals["pid"]:
# ps call provides header followed by params for tor
- psCall = os.popen('ps -p %s -o %s' % (self.vals["pid"], ",".join(psParams)))
+ psCall = os.popen('ps -p %s -o %s 2> /dev/null' % (self.vals["pid"], ",".join(psParams)))
try: sampling = psCall.read().strip().split()[len(psParams):]
except IOError: sampling = [] # ps call failed
Modified: arm/trunk/interface/hostnameResolver.py
===================================================================
--- arm/trunk/interface/hostnameResolver.py 2009-09-27 07:58:53 UTC (rev 20673)
+++ arm/trunk/interface/hostnameResolver.py 2009-09-27 08:50:41 UTC (rev 20674)
@@ -118,10 +118,12 @@
except Queue.Empty: continue
resolutionFailed = False # if true don't cache results
- hostCall = os.popen("host %s" % ipAddr)
+ hostCall = os.popen("host %s 2> /dev/null" % ipAddr)
try:
- hostname = hostCall.read().split()[-1:][0]
+ hostname = hostCall.read()
+ if hostname: hostname = hostname.split()[-1:][0]
+ else: raise IOError # call failed ('host' command probably unavailable)
if hostname == "reached":
# got message: ";; connection timed out; no servers could be reached"
Deleted: arm/trunk/readme.txt
===================================================================
--- arm/trunk/readme.txt 2009-09-27 07:58:53 UTC (rev 20673)
+++ arm/trunk/readme.txt 2009-09-27 08:50:41 UTC (rev 20674)
@@ -1,84 +0,0 @@
-arm (anonymizing relay monitor) - Terminal status monitor for Tor relays.
-Developed by Damian Johnson (www.atagar.com - atagar1@xxxxxxxxx)
-All code under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
-Project page: www.atagar.com/arm
-
-Description:
-Command line application for monitoring Tor relays, providing real time status
-information such as the current configuration, bandwidth usage, message log,
-connections, etc. This uses a curses interface much like 'top' does for system
-usage. The application is intended for command-line aficionados, ssh
-connections, and anyone stuck with a tty terminal for checking their relay's
-status. Releases should be stable so if you manage to make it crash (or have a
-feature request) then please let me know!
-
-The project was originally proposed in 2008 by Jacob and Karsten:
- http://archives.seul.org/or/dev/Jan-2008/msg00005.html
-
-An interview by Brenno Winter discussing the project is available at:
- http://www.atagar.com/arm/HFM_INT_0001.mp3
-
-Requirements:
-Python 2.5
-TorCtl (retrieved in svn checkout)
-Common *nix commands including: ps, pidof, host, and netstat
-Tor is running with an available control port. This means either...
- ... starting Tor with '--controlport <PORT>'
- ... or including 'ControlPort <PORT>' in your torrc
-
-This is started via 'arm' (use the '--help' argument for usage).
-
-FAQ:
-> Why is it called 'arm'?
-
-Simple - because it makes the command short and memorable. Terminal
-applications need to be easy to type (like 'top', 'ssh', etc), and anything
-longer is just begging command-line aficionados to alias it down. I chose the
-meaning of the acronym ('anonymizing relay monitor') afterward.
-
-> If you're listing connections then what about exit nodes? Won't this include
-people's traffic?
-
-While arm isn't intended to be a sniffer it does provide real time connection
-data which, for exit nodes, includes the endpoints being visited through you.
-Unfortunately this is pretty unavoidable. The control port doesn't provide a
-means of distinguishing those connections and trying to figure it out by
-correlating against consensus data has proved pretty inaccurate.
-
-That said, this really isn't much of a concern. For Tor users the real threats
-come from things like Wireshark and MITM attacks on their unencrypted traffic.
-Simply seeing an unknown individual's endpoints is no great feat in itself.
-Just attach netstat to a cron job and voilà! You've got a sniffer that's just
-as mighty as arm.
-
-> Is it harmful to share the information provided by arm?
-
-Not really, but it's discouraged. The original plan for arm included a special
-emphasis that it wouldn't log any data. The reason is that if a large number
-of relay operators published the details of their connections then correlation
-attacks could break Tor user's anonymity. Just show some moderation in what
-you share and it should be fine.
-
-> Is there any chance that arm will leak data?
-
-Yes - arm is a passive listener with one exception. The second page
-(connections) provides the hostnames of Tor relays you're connected to. This
-means reverse DNS lookups which, if monitored, could leak your current
-connections to an eavesdropper. However, lookups are only made upon request
-(when showing connection details or listing connections by hostname) and you
-can disable lookups entirely with 'r' - see the page's help for the current
-status.
-
-That said, this is not a terribly big whoop. ISPs and anyone sniffing your
-connection already has this data - the only difference is that instead of
-saying "I am talking to x" you're saying "I'm talking to x, who's x?", meaning
-the resolver's also aware of who they are.
-
-> When arm starts it gives "Unable to resolve tor pid, abandoning connection
-listing"... why?
-
-If you're running multiple instances of tor then arm needs to figure out which
-pid belongs to the open control port. If it's running as a different user
-(such as being in a chroot jail) then it's probably failing due to permission
-issues. Arm still runs, just no connection listing or ps stats.
-