[or-cvs] r23933: {arm} added: testing script for checking connection resolution per (in arm/trunk/src: . interface interface/graphing util)
Damian Johnson
atagar1 at gmail.com
Mon Dec 13 17:56:27 UTC 2010
Author: atagar
Date: 2010-12-13 17:56:26 +0000 (Mon, 13 Dec 2010)
New Revision: 23933
Added:
arm/trunk/src/test.py
Modified:
arm/trunk/src/interface/connPanel.py
arm/trunk/src/interface/controller.py
arm/trunk/src/interface/graphing/bandwidthStats.py
arm/trunk/src/interface/logPanel.py
arm/trunk/src/uninstall
arm/trunk/src/util/connections.py
arm/trunk/src/util/sysTools.py
arm/trunk/src/util/torConfig.py
arm/trunk/src/util/torTools.py
Log:
added: testing script for checking connection resolution performance, connection dumps, and the glyph demo
change: reordered resolvers by order of performance
fix: bringing all linux connection resolvers into parity (established tcp connections only)
fix: altering lsof calls to work on FreeBSD (thanks to Fabian Keil)
fix: timing issue caused the first connection resolution to not have the pid
fix: when the pid was unavailable some resolvers failed to work
fix: commands with quoted pipes were being misparsed by the sysTools' call function
fix: including udp connection results (needed since exits proxy dns traffic)
fix: skipping internal -> external address translation when the external address is private (caught by Fabian Keil)
fix: labeling connections to our socks port as being client connections
Modified: arm/trunk/src/interface/connPanel.py
===================================================================
--- arm/trunk/src/interface/connPanel.py 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/interface/connPanel.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -156,6 +156,7 @@
self.orPort = "0"
self.dirPort = "0"
self.controlPort = "0"
+ self.socksPort = "0"
self.family = [] # fingerpints of family entries
self.isBridge = False # true if BridgeRelay is set
self.exitPolicy = ""
@@ -192,6 +193,8 @@
self.listenPort = listenAddr[listenAddr.find(":") + 1:]
else: self.listenPort = self.orPort
+ self.socksPort = torTools.getConn().getOption("SocksPort", "0")
+
# entry is None if not set, otherwise of the format "$<fingerprint>,$<fingerprint>"
familyEntry = self.conn.get_option("MyFamily")[0][1]
if familyEntry: self.family = familyEntry.split(",")
@@ -216,6 +219,7 @@
self.orPort = "0"
self.dirPort = "0"
self.controlPort = "0"
+ self.socksPort = "0"
self.family = []
self.isBridge = False
self.exitPolicy = ""
@@ -325,6 +329,9 @@
type = "inbound"
connectionCountTmp[0] += 1
if SCRUB_PRIVATE_DATA and fIp not in self.fingerprintMappings.keys(): isPrivate = isGuard or self.isBridge
+ elif lPort == self.socksPort:
+ type = "client"
+ connectionCountTmp[2] += 1
elif lPort == self.controlPort:
type = "control"
connectionCountTmp[4] += 1
@@ -348,8 +355,13 @@
connectionCountTmp[1] += 1
if SCRUB_PRIVATE_DATA and fIp not in self.fingerprintMappings.keys(): isPrivate = isExitAllowed(fIp, fPort, self.exitPolicy, self.exitRejectPrivate)
- # replace nat address with external version if available
- if self.address and type != "control": lIp = self.address
+ # replace nat address with external version if available and the
+ # external address isn't a private IP
+ # TODO: range should restrict to the following address ranges:
+ # 10.*, 172.16.* - 172.31.*, 192.168.*
+ # being lazy right now - fix the 172.* range when rewriting
+ isPrivateIp = fIp.startswith("10.") or fIp.startswith("192.168.") or fIp.startswith("172.")
+ if self.address and type != "control" and not isPrivateIp: lIp = self.address
try:
countryCodeQuery = "ip-to-country/%s" % fIp
Modified: arm/trunk/src/interface/controller.py
===================================================================
--- arm/trunk/src/interface/controller.py 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/interface/controller.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -529,8 +529,11 @@
# minor refinements for connection resolver
if not isBlindMode:
- resolver = connections.getResolver("tor")
- if torPid: resolver.processPid = torPid # helps narrow connection results
+ if torPid:
+ # use the tor pid to help narrow connection results
+ resolver = connections.getResolver("tor", torPid)
+ else:
+ resolver = connections.getResolver("tor")
# hack to display a better (arm specific) notice if all resolvers fail
connections.RESOLVER_FINAL_FAILURE_MSG += " (connection related portions of the monitor won't function)"
@@ -1423,7 +1426,7 @@
panels["conn"].sortConnections()
elif page == 1 and (key == ord('u') or key == ord('U')):
# provides menu to pick identification resolving utility
- optionTypes = [None, connections.CMD_NETSTAT, connections.CMD_SS, connections.CMD_LSOF, connections.CMD_SOCKSTAT, connections.CMD_BSD_SOCKSTAT, connections.CMD_BSD_PROCSTAT]
+ optionTypes = [None, connections.CMD_NETSTAT, connections.CMD_SOCKSTAT, connections.CMD_LSOF, connections.CMD_SS, connections.CMD_BSD_SOCKSTAT, connections.CMD_BSD_PROCSTAT]
options = ["auto"] + [connections.CMD_STR[util] for util in optionTypes[1:]]
initialSelection = connections.getResolver("tor").overwriteResolver # enums correspond to indices
Modified: arm/trunk/src/interface/graphing/bandwidthStats.py
===================================================================
--- arm/trunk/src/interface/graphing/bandwidthStats.py 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/interface/graphing/bandwidthStats.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -99,7 +99,7 @@
return False
# attempt to open the state file
- try: stateFile = open("%s%s/state" % (torTools.getPathPrefix(), dataDir), "r")
+ try: stateFile = open("%s%s/state" % (conn.getPathPrefix(), dataDir), "r")
except IOError:
msg = PREPOPULATE_FAILURE_MSG % "unable to read the state file"
log.log(self._config["log.graph.bw.prepopulateFailure"], msg)
Modified: arm/trunk/src/interface/logPanel.py
===================================================================
--- arm/trunk/src/interface/logPanel.py 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/interface/logPanel.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -206,7 +206,7 @@
if not loggingLocation: return []
# includes the prefix for tor paths
- loggingLocation = torTools.getPathPrefix() + loggingLocation
+ loggingLocation = torTools.getConn().getPathPrefix() + loggingLocation
# if the runlevels argument is a superset of the log file then we can
# limit the read contents to the addLimit
Added: arm/trunk/src/test.py
===================================================================
--- arm/trunk/src/test.py (rev 0)
+++ arm/trunk/src/test.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+
+"""
+Handler for arm tests and demos.
+"""
+
+import time
+from util import connections, torTools, uiTools
+
+MENU = """Arm Test Options:
+ 1. Resolver Performance Test
+ 2. Resolver Dump
+ 3. Glyph Demo
+ q. Quit
+
+Selection: """
+
+def printDivider():
+ print("\n" + "-" * 40 + "\n")
+
+conn = None
+while True:
+ userInput = raw_input(MENU)
+
+ # initiate the TorCtl connection if the test needs it
+ if userInput in ("1", "2") and not conn:
+ conn = torTools.getConn()
+ conn.init()
+
+ # prefetch pid so extra system calls don't effect the timing of tests
+ conn.getMyPid()
+
+ if userInput == "q":
+ break # quit test script
+ elif userInput == "1":
+ systemResolvers = connections.getSystemResolvers()
+ printDivider()
+
+ allConnectionResults = []
+ for resolver in systemResolvers:
+ startTime = time.time()
+ connectionResults = connections.getConnections(resolver, "tor", conn.getMyPid())
+ connectionResults.sort()
+ allConnectionResults.append(connectionResults)
+
+ resolverLabel = "%-10s" % connections.CMD_STR[resolver]
+ countLabel = "%4i results" % len(connectionResults)
+ timeLabel = "%0.4f seconds" % (time.time() - startTime)
+ print "%s %s %s" % (resolverLabel, countLabel, timeLabel)
+
+ allResolversMatch = True
+ firstResult = allConnectionResults.pop()
+ while allConnectionResults:
+ if allConnectionResults.pop() != firstResult:
+ allResolversMatch = False
+ break
+
+ if allResolversMatch:
+ print("\nThe results of all the connection resolvers match")
+ else:
+ print("\nWarning: Connection resolver results differ")
+
+ printDivider()
+ elif userInput == "2":
+ # use the given resolver to fetch tor's connections
+ while True:
+ # provide the selection options
+ printDivider()
+ print("Select a resolver:")
+ for i in range(1, 7):
+ print(" %i. %s" % (i, connections.CMD_STR[i]))
+ print(" q. Go back to the main menu")
+
+ userSelection = raw_input("\nSelection: ")
+ if userSelection == "q":
+ printDivider()
+ break
+
+ if userSelection.isdigit() and int(userSelection) in range(1, 7):
+ try:
+ resolver = int(userSelection)
+ startTime = time.time()
+
+ print(connections.getResolverCommand(resolver, "tor", conn.getMyPid()))
+ connectionResults = connections.getConnections(resolver, "tor", conn.getMyPid())
+ connectionResults.sort()
+
+ # prints results
+ printDivider()
+ for lIp, lPort, fIp, fPort in connectionResults:
+ print(" %s:%s -> %s:%s" % (lIp, lPort, fIp, fPort))
+
+ print("\n Runtime: %0.4f seconds" % (time.time() - startTime))
+ except IOError, exc:
+ print exc
+ else:
+ print("'%s' isn't a valid selection\n" % userSelection)
+ elif userInput == "3":
+ uiTools.demoGlyphs()
+
+ # Switching to a curses context and back repetedy seems to screw up the
+ # terminal. Just to be safe this ends the process after the demo.
+ break
+ else:
+ print("'%s' isn't a valid selection\n" % userInput)
+
Modified: arm/trunk/src/uninstall
===================================================================
--- arm/trunk/src/uninstall 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/uninstall 2010-12-13 17:56:26 UTC (rev 23933)
@@ -1,7 +1,7 @@
#!/bin/sh
files="/usr/bin/arm /usr/share/man/man1/arm.1.gz /usr/share/arm"
-for i in $files
+for i in $files
do
if [ -f $i -o -d $i ]; then
rm -rf $i
Modified: arm/trunk/src/util/connections.py
===================================================================
--- arm/trunk/src/util/connections.py 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/util/connections.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -2,16 +2,17 @@
Fetches connection data (IP addresses and ports) associated with a given
process. This sort of data can be retrieved via a variety of common *nix
utilities:
-- netstat netstat -npt | grep <pid>/<process>
-- ss ss -p | grep "\"<process>\",<pid>"
-- lsof lsof -nPi | grep "<process>\s*<pid>.*(ESTABLISHED)"
+- netstat netstat -np | grep "ESTABLISHED <pid>/<process>"
- sockstat sockstat | egrep "<process>\s*<pid>.*ESTABLISHED"
+- lsof lsof -nPi | egrep "^<process>\s*<pid>.*((UDP.*)|(\(ESTABLISHED\)))"
+- ss ss -nptu | grep "ESTAB.*\"<process>\",<pid>"
-all queries dump its stderr (directing it to /dev/null).
+all queries dump its stderr (directing it to /dev/null). Results include UDP
+and established TCP connections.
FreeBSD lacks support for the needed netstat flags and has a completely
-different program for 'ss'. However, there's a couple other options (thanks to
-Fabian Keil and Hans Schnehl):
+different program for 'ss'. However, lsof works and there's a couple other
+options that perform even better (thanks to Fabian Keil and Hans Schnehl):
- sockstat sockstat -4c | grep '<process> *<pid>'
- procstat procstat -f <pid> | grep TCP | grep -v 0.0.0.0:0
"""
@@ -24,7 +25,7 @@
from util import log, sysTools
# enums for connection resolution utilities
-CMD_NETSTAT, CMD_SS, CMD_LSOF, CMD_SOCKSTAT, CMD_BSD_SOCKSTAT, CMD_BSD_PROCSTAT = range(1, 7)
+CMD_NETSTAT, CMD_SOCKSTAT, CMD_LSOF, CMD_SS, CMD_BSD_SOCKSTAT, CMD_BSD_PROCSTAT = range(1, 7)
CMD_STR = {CMD_NETSTAT: "netstat",
CMD_SS: "ss",
CMD_LSOF: "lsof",
@@ -39,23 +40,27 @@
# formatted strings for the commands to be executed with the various resolvers
# options are:
-# n = prevents dns lookups, p = include process, t = tcp only
+# n = prevents dns lookups, p = include process
# output:
# tcp 0 0 127.0.0.1:9051 127.0.0.1:53308 ESTABLISHED 9912/tor
# *note: bsd uses a different variant ('-t' => '-p tcp', but worse an
# equivilant -p doesn't exist so this can't function)
-RUN_NETSTAT = "netstat -npt | grep %s/%s"
+RUN_NETSTAT = "netstat -np | grep \"ESTABLISHED %s/%s\""
-# n = numeric ports, p = include process
+# n = numeric ports, p = include process, t = tcp sockets, u = udp sockets
# output:
# ESTAB 0 0 127.0.0.1:9051 127.0.0.1:53308 users:(("tor",9912,20))
# *note: under freebsd this command belongs to a spreadsheet program
-RUN_SS = "ss -np | grep \"\\\"%s\\\",%s\""
+RUN_SS = "ss -nptu | grep \"ESTAB.*\\\"%s\\\",%s\""
# n = prevent dns lookups, P = show port numbers (not names), i = ip only
# output:
-# tor 9912 atagar 20u IPv4 33453 TCP 127.0.0.1:9051->127.0.0.1:53308
-RUN_LSOF = "lsof -nPi | grep \"%s\s*%s.*(ESTABLISHED)\""
+# tor 3873 atagar 45u IPv4 40994 0t0 TCP 10.243.55.20:45724->194.154.227.109:9001 (ESTABLISHED)
+#
+# oddly, using the -p flag via:
+# lsof lsof -nPi -p <pid> | grep "^<process>.*(ESTABLISHED)"
+# is much slower (11-28% in tests I ran)
+RUN_LSOF = "lsof -nPi | egrep \"^%s\\s*%s.*((UDP.*)|(\\(ESTABLISHED\\)))\""
# output:
# atagar tor 3475 tcp4 127.0.0.1:9051 127.0.0.1:38942 ESTABLISHED
@@ -79,6 +84,34 @@
def loadConfig(config):
config.update(CONFIG)
+def getResolverCommand(resolutionCmd, processName, processPid = ""):
+ """
+ Provides the command that would be processed for the given resolver type.
+ This raises a ValueError if either the resolutionCmd isn't recognized or a
+ pid was requited but not provided.
+
+ Arguments:
+ resolutionCmd - command to use in resolving the address
+ processName - name of the process for which connections are fetched
+ processPid - process ID (this helps improve accuracy)
+ """
+
+ if not processPid:
+ # the pid is required for procstat resolution
+ if resolutionCmd == CMD_BSD_PROCSTAT:
+ raise ValueError("procstat resolution requires a pid")
+
+ # if the pid was undefined then match any in that field
+ processPid = "[0-9]*"
+
+ if resolutionCmd == CMD_NETSTAT: return RUN_NETSTAT % (processPid, processName)
+ elif resolutionCmd == CMD_SS: return RUN_SS % (processName, processPid)
+ elif resolutionCmd == CMD_LSOF: return RUN_LSOF % (processName, processPid)
+ elif resolutionCmd == CMD_SOCKSTAT: return RUN_SOCKSTAT % (processName, processPid)
+ elif resolutionCmd == CMD_BSD_SOCKSTAT: return RUN_BSD_SOCKSTAT % (processName, processPid)
+ elif resolutionCmd == CMD_BSD_PROCSTAT: return RUN_BSD_PROCSTAT % processPid
+ else: raise ValueError("Unrecognized resolution type: %s" % resolutionCmd)
+
def getConnections(resolutionCmd, processName, processPid = ""):
"""
Retrieves a list of the current connections for a given process, providing a
@@ -96,15 +129,9 @@
processPid - process ID (this helps improve accuracy)
"""
- if resolutionCmd == CMD_NETSTAT: cmd = RUN_NETSTAT % (processPid, processName)
- elif resolutionCmd == CMD_SS: cmd = RUN_SS % (processName, processPid)
- elif resolutionCmd == CMD_LSOF: cmd = RUN_LSOF % (processName, processPid)
- elif resolutionCmd == CMD_SOCKSTAT: cmd = RUN_SOCKSTAT % (processName, processPid)
- elif resolutionCmd == CMD_BSD_SOCKSTAT: cmd = RUN_BSD_SOCKSTAT % (processName, processPid)
- elif resolutionCmd == CMD_BSD_PROCSTAT: cmd = RUN_BSD_PROCSTAT % processPid
- else: raise ValueError("Unrecognized resolution type: %s" % resolutionCmd)
# raises an IOError if the command fails or isn't available
+ cmd = getResolverCommand(resolutionCmd, processName, processPid)
results = sysTools.call(cmd)
if not results: raise IOError("No results found using: %s" % cmd)
@@ -114,9 +141,12 @@
for line in results:
comp = line.split()
- if resolutionCmd == CMD_NETSTAT or resolutionCmd == CMD_SS:
+ if resolutionCmd == CMD_NETSTAT:
localIp, localPort = comp[3].split(":")
foreignIp, foreignPort = comp[4].split(":")
+ elif resolutionCmd == CMD_SS:
+ localIp, localPort = comp[4].split(":")
+ foreignIp, foreignPort = comp[5].split(":")
elif resolutionCmd == CMD_LSOF:
local, foreign = comp[8].split("->")
localIp, localPort = local.split(":")
@@ -180,38 +210,18 @@
else: RESOLVERS[haltedIndex] = r
return r
-def test():
- # quick method for testing connection resolution
- userInput = raw_input("Enter query (<ss, netstat, lsof, sockstat> PROCESS_NAME [PID]): ").split()
+def getSystemResolvers(osType = None):
+ """
+ Provides the types of connection resolvers available on this operating
+ system.
- # checks if there's enough arguments
- if len(userInput) == 0: sys.exit(0)
- elif len(userInput) == 1:
- print "no process name provided"
- sys.exit(1)
+ Arguments:
+ osType - operating system type, fetched from the os module if undefined
+ """
- # translates resolver string to enum
- userInput[0] = userInput[0].lower()
- if userInput[0] == "ss": userInput[0] = CMD_SS
- elif userInput[0] == "netstat": userInput[0] = CMD_NETSTAT
- elif userInput[0] == "lsof": userInput[0] = CMD_LSOF
- elif userInput[0] == "sockstat": userInput[0] = CMD_SOCKSTAT
- else:
- print "unrecognized type of resolver: %s" % userInput[2]
- sys.exit(1)
-
- # resolves connections
- try:
- if len(userInput) == 2: connections = getConnections(userInput[0], userInput[1])
- else: connections = getConnections(userInput[0], userInput[1], userInput[2])
- except IOError, exc:
- print exc
- sys.exit(1)
-
- # prints results
- print "-" * 40
- for lIp, lPort, fIp, fPort in connections:
- print "%s:%s -> %s:%s" % (lIp, lPort, fIp, fPort)
+ if osType == None: osType = os.uname()[0]
+ if osType == "FreeBSD": return [CMD_BSD_SOCKSTAT, CMD_BSD_PROCSTAT, CMD_LSOF]
+ else: return [CMD_NETSTAT, CMD_SOCKSTAT, CMD_LSOF, CMD_SS]
class ConnectionResolver(threading.Thread):
"""
@@ -279,8 +289,7 @@
self.defaultResolver = CMD_NETSTAT
osType = os.uname()[0]
- if osType == "FreeBSD": self.resolverOptions = [CMD_BSD_SOCKSTAT, CMD_BSD_PROCSTAT]
- else: self.resolverOptions = [CMD_NETSTAT, CMD_SS, CMD_LSOF, CMD_SOCKSTAT]
+ self.resolverOptions = getSystemResolvers(osType)
resolverLabels = ", ".join([CMD_STR[option] for option in self.resolverOptions])
log.log(CONFIG["log.connResolverOptions"], "Operating System: %s, Connection Resolvers: %s" % (osType, resolverLabels))
Modified: arm/trunk/src/util/sysTools.py
===================================================================
--- arm/trunk/src/util/sysTools.py 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/util/sysTools.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -149,9 +149,19 @@
return cachedResults
startTime = time.time()
- commandComp = command.split("|")
commandCall, results, errorExc = None, None, None
+ # Gets all the commands involved, taking piping into consideration. If the
+ # pipe is quoted (ie, echo "an | example") then it's ignored.
+
+ commandComp = []
+ for component in command.split("|"):
+ if not commandComp or component.count("\"") % 2 == 0:
+ commandComp.append(component)
+ else:
+ # pipe is within quotes
+ commandComp[-1] += "|" + component
+
# preprocessing for the commands to prevent anything going to stdout
for i in range(len(commandComp)):
subcommand = commandComp[i].strip()
Modified: arm/trunk/src/util/torConfig.py
===================================================================
--- arm/trunk/src/util/torConfig.py 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/util/torConfig.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -313,7 +313,7 @@
except IOError, exc:
raise IOError(failureMsg % ("the pwdx call failed: " + str(exc)))
- return torTools.getPathPrefix() + configLocation
+ return conn.getPathPrefix() + configLocation
def getMultilineParameters():
"""
Modified: arm/trunk/src/util/torTools.py
===================================================================
--- arm/trunk/src/util/torTools.py 2010-12-13 02:44:56 UTC (rev 23932)
+++ arm/trunk/src/util/torTools.py 2010-12-13 17:56:26 UTC (rev 23933)
@@ -42,7 +42,7 @@
CACHE_ARGS = ("version", "config-file", "exit-policy/default", "fingerprint",
"config/names", "info/names", "features/names", "events/names",
"nsEntry", "descEntry", "bwRate", "bwBurst", "bwObserved",
- "bwMeasured", "flags", "pid")
+ "bwMeasured", "flags", "pid", "pathPrefix")
TOR_CTL_CLOSE_MSG = "Tor closed control connection. Exiting event thread."
UNKNOWN = "UNKNOWN" # value used by cached information if undefined
@@ -52,7 +52,9 @@
"log.torGetInfo": log.DEBUG,
"log.torGetConf": log.DEBUG,
"log.torSetConf": log.INFO,
- "log.torPrefixPathInvalid": log.NOTICE}
+ "log.torPrefixPathInvalid": log.NOTICE,
+ "log.bsdJailFound": log.INFO,
+ "log.unknownBsdJailId": log.WARN}
# events used for controller functionality:
# NOTICE - used to detect when tor is shut down
@@ -67,27 +69,7 @@
def loadConfig(config):
config.update(CONFIG)
-
- # make sure the path prefix is valid and exists (providing a notice if not)
- prefixPath = CONFIG["features.pathPrefix"].strip()
-
- if prefixPath:
- if prefixPath.endswith("/"): prefixPath = prefixPath[:-1]
-
- if prefixPath and not os.path.exists(prefixPath):
- msg = "The prefix path set in your config (%s) doesn't exist." % prefixPath
- log.log(CONFIG["log.torPrefixPathInvalid"], msg)
- prefixPath = ""
-
- CONFIG["features.pathPrefix"] = prefixPath
-def getPathPrefix():
- """
- Provides the path prefix that should be used for fetching tor resources.
- """
-
- return CONFIG["features.pathPrefix"]
-
def getPid(controlPort=9051, pidFilePath=None):
"""
Attempts to determine the process id for a running tor process, using the
@@ -179,6 +161,29 @@
return None
+def getBsdJailId():
+ """
+ Get the FreeBSD jail id for the monitored Tor process.
+ """
+
+ # Output when called from a FreeBSD jail or when Tor isn't jailed:
+ # JID
+ # 0
+ #
+ # Otherwise it's something like:
+ # JID
+ # 1
+
+ torPid = getConn().getMyPid()
+ psOutput = sysTools.call("ps -p %s -o jid" % torPid)
+
+ if len(psOutput) == 2 and len(psOutput[1].split()) == 1:
+ jid = psOutput[1].strip()
+ if jid.isdigit(): return int(jid)
+
+ log.log(CONFIG["log.unknownBsdJailId"], "Failed to figure out the FreeBSD jail id. Assuming 0.")
+ return 0
+
def getConn():
"""
Singleton constructor for a Controller. Be aware that this starts as being
@@ -209,6 +214,11 @@
self._statusTime = 0 # unix time-stamp for the duration of the status
self.lastHeartbeat = 0 # time of the last tor event
+ # Logs issues and notices when fetching the path prefix if true. This is
+ # only done once for the duration of the application to avoid pointless
+ # messages.
+ self._pathPrefixLogging = True
+
# cached GETINFO parameters (None if unset or possibly changed)
self._cachedParam = dict([(arg, "") for arg in CACHE_ARGS])
@@ -609,6 +619,18 @@
return self._getRelayAttr("pid", None)
+ def getPathPrefix(self):
+ """
+ Provides the path prefix that should be used for fetching tor resources.
+ If undefined and Tor is inside a jail under FreeBsd then this provides the
+ jail's path.
+ """
+
+ result = self._getRelayAttr("pathPrefix", "")
+
+ if result == UNKNOWN: return ""
+ else: return result
+
def getStatus(self):
"""
Provides a tuple consisting of the control port's current status and unix
@@ -1008,6 +1030,39 @@
break
elif key == "pid":
result = getPid(int(self.getOption("ControlPort", 9051)), self.getOption("PidFile"))
+ elif key == "pathPrefix":
+ # make sure the path prefix is valid and exists (providing a notice if not)
+ prefixPath = CONFIG["features.pathPrefix"].strip()
+
+ # adjusts the prefix path to account for jails under FreeBSD (many
+ # thanks to Fabian Keil!)
+ if not prefixPath and os.uname()[0] == "FreeBSD":
+ jid = getBsdJailId()
+ if jid != 0:
+ # Output should be something like:
+ # JID IP Address Hostname Path
+ # 1 10.0.0.2 tor-jail /usr/jails/tor-jail
+ jlsOutput = sysTools.call("jls -j %s" % jid)
+
+ if len(jlsOutput) == 2 and len(jlsOutput[1].split()) == 4:
+ prefixPath = jlsOutput[1].split()[3]
+
+ if self._pathPrefixLogging:
+ msg = "Adjusting paths to account for Tor running in a jail at: %s" % prefixPath
+ log.log(CONFIG["log.bsdJailFound"], msg)
+
+ if prefixPath:
+ # strips off ending slash from the path
+ if prefixPath.endswith("/"): prefixPath = prefixPath[:-1]
+
+ # avoid using paths that don't exist
+ if self._pathPrefixLogging and prefixPath and not os.path.exists(prefixPath):
+ msg = "The prefix path set in your config (%s) doesn't exist." % prefixPath
+ log.log(CONFIG["log.torPrefixPathInvalid"], msg)
+ prefixPath = ""
+
+ self._pathPrefixLogging = False # prevents logging if fetched again
+ result = prefixPath
# cache value
if result: self._cachedParam[key] = result
More information about the tor-commits
mailing list