[tor-commits] r24651: {arm} Replacing the file discriptor popup (which was both unused a (in arm/trunk: . src/interface src/util)
Damian Johnson
atagar1 at gmail.com
Mon Apr 18 02:34:38 UTC 2011
Author: atagar
Date: 2011-04-18 02:34:38 +0000 (Mon, 18 Apr 2011)
New Revision: 24651
Removed:
arm/trunk/src/interface/fileDescriptorPopup.py
Modified:
arm/trunk/ChangeLog
arm/trunk/README
arm/trunk/armrc.sample
arm/trunk/src/interface/__init__.py
arm/trunk/src/interface/controller.py
arm/trunk/src/interface/headerPanel.py
arm/trunk/src/util/procTools.py
arm/trunk/src/util/sysTools.py
arm/trunk/src/util/torTools.py
Log:
Replacing the file discriptor popup (which was both unused and inaccurate) with:
- logged notice/warn when descriptors are running out
- an entry in the header panel when they're running low on descriptors
Modified: arm/trunk/ChangeLog
===================================================================
--- arm/trunk/ChangeLog 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/ChangeLog 2011-04-18 02:34:38 UTC (rev 24651)
@@ -48,6 +48,8 @@
* fix: recognizing the proper private ip ranges of the 172.* block
* fix: missing 'is default' option from config sort ordering
* fix (4/4/11, r24562): hidden service parsing issue when there's multiple spaces in the HiddenServicePort opition (caught by Nicolas Pouillard)
+ * fix (4/6/11, r24570): missing new connection components from installations (caught by Anthony Basile)
+ * fix (4/13/11, r24613): failed requests for our flags cause a syntax error (caught by qbi)
1/7/11 - version 1.4.1 (r24054)
Platform specific enhancements including BSD compatibility and vastly improved performance on Linux.
Modified: arm/trunk/README
===================================================================
--- arm/trunk/README 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/README 2011-04-18 02:34:38 UTC (rev 24651)
@@ -157,13 +157,10 @@
__init__.py
controller.py - main display loop, handling input and layout
headerPanel.py - top of all pages, providing general information
+ descriptorPopup.py - (popup) displays connection descriptor data
logPanel.py - (page 1) displays tor, arm, and torctl events
- fileDescriptorPopup.py - (popup) displays file descriptors used by tor
-
connPanel.py - (page 2) deprecated counterpart for connections/*
- descriptorPopup.py - (popup) displays connection descriptor data
-
configPanel.py - (page 3) editor panel for the tor configuration
torrcPanel.py - (page 4) displays torrc and validation
Modified: arm/trunk/armrc.sample
===================================================================
--- arm/trunk/armrc.sample 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/armrc.sample 2011-04-18 02:34:38 UTC (rev 24651)
@@ -31,6 +31,10 @@
# events.
features.logFile
+# If true, the header panel always shows the file descriptor usage. Otherwise
+# this is only displayed when we're running out.
+features.showFdUsage false
+
# Paremters for the log panel
# ---------------------------
# showDateDividers
@@ -276,4 +280,6 @@
log.stats.procResolutionFailover INFO
log.stats.failedPsResolution INFO
log.savingDebugLog NOTICE
+log.fdUsageSixtyPercent NOTICE
+log.fdUsageNinetyPercent WARN
Modified: arm/trunk/src/interface/__init__.py
===================================================================
--- arm/trunk/src/interface/__init__.py 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/src/interface/__init__.py 2011-04-18 02:34:38 UTC (rev 24651)
@@ -2,5 +2,5 @@
Panels, popups, and handlers comprising the arm user interface.
"""
-__all__ = ["configPanel", "connPanel", "controller", "descriptorPopup", "fileDescriptorPopup", "headerPanel", "logPanel", "torrcPanel"]
+__all__ = ["configPanel", "connPanel", "controller", "descriptorPopup", "headerPanel", "logPanel", "torrcPanel"]
Modified: arm/trunk/src/interface/controller.py
===================================================================
--- arm/trunk/src/interface/controller.py 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/src/interface/controller.py 2011-04-18 02:34:38 UTC (rev 24651)
@@ -22,7 +22,6 @@
import configPanel
import torrcPanel
import descriptorPopup
-import fileDescriptorPopup
import interface.connections.connPanel
import interface.connections.connEntry
@@ -551,7 +550,7 @@
connections.RESOLVER_FINAL_FAILURE_MSG += " (connection related portions of the monitor won't function)"
panels = {
- "header": headerPanel.HeaderPanel(stdscr, startTime),
+ "header": headerPanel.HeaderPanel(stdscr, startTime, config),
"popup": Popup(stdscr, 9),
"graph": graphing.graphPanel.GraphPanel(stdscr),
"log": logPanel.LogPanel(stdscr, loggedEvents, config)}
@@ -962,7 +961,7 @@
popup.addfstr(3, 2, "<b>s</b>: graphed stats (<b>%s</b>)" % graphedStats)
popup.addfstr(3, 41, "<b>i</b>: graph update interval (<b>%s</b>)" % graphing.graphPanel.UPDATE_INTERVALS[panels["graph"].updateInterval][0])
popup.addfstr(4, 2, "<b>b</b>: graph bounds (<b>%s</b>)" % panels["graph"].bounds.lower())
- popup.addfstr(4, 41, "<b>d</b>: file descriptors")
+ popup.addfstr(4, 41, "<b>a</b>: save snapshot of the log")
popup.addfstr(5, 2, "<b>e</b>: change logged events")
regexLabel = "enabled" if panels["log"].regexFilter else "disabled"
@@ -971,7 +970,6 @@
hiddenEntryLabel = "visible" if panels["log"].showDuplicates else "hidden"
popup.addfstr(6, 2, "<b>u</b>: duplicate log entries (<b>%s</b>)" % hiddenEntryLabel)
popup.addfstr(6, 41, "<b>c</b>: clear event log")
- popup.addfstr(7, 41, "<b>a</b>: save snapshot of the log")
pageOverrideKeys = (ord('m'), ord('n'), ord('s'), ord('i'), ord('d'), ord('e'), ord('r'), ord('f'), ord('x'))
if page == 1:
@@ -1121,21 +1119,6 @@
panels["graph"].bounds = graphing.graphPanel.Bounds.next(panels["graph"].bounds)
selectiveRefresh(panels, page)
- elif page == 0 and key in (ord('d'), ord('D')):
- # provides popup with file descriptors
- panel.CURSES_LOCK.acquire()
- try:
- setPauseState(panels, isPaused, page, True)
- curses.cbreak() # wait indefinitely for key presses (no timeout)
-
- fileDescriptorPopup.showFileDescriptorPopup(panels["popup"], stdscr, torPid)
-
- setPauseState(panels, isPaused, page)
- curses.halfdelay(REFRESH_RATE * 10) # reset normal pausing behavior
- finally:
- panel.CURSES_LOCK.release()
-
- panels["graph"].redraw(True)
elif page == 0 and (key == ord('a') or key == ord('A')):
# allow user to enter a path to take a snapshot - abandons if left blank
panel.CURSES_LOCK.acquire()
Deleted: arm/trunk/src/interface/fileDescriptorPopup.py
===================================================================
--- arm/trunk/src/interface/fileDescriptorPopup.py 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/src/interface/fileDescriptorPopup.py 2011-04-18 02:34:38 UTC (rev 24651)
@@ -1,189 +0,0 @@
-#!/usr/bin/env python
-# fileDescriptorPopup.py -- provides open file descriptor stats and listing
-# Released under the GPL v3 (http://www.gnu.org/licenses/gpl.html)
-
-import os
-import curses
-
-from util import panel, sysTools, uiTools
-
-class PopupProperties:
- """
- State attributes of popup window for file descriptors. Any problem in system
- calls will cause 'errorMsg' to be set (providing the notice rather than
- displaying data). Under systems other than Solaris there's no way for a
- process (other than tor itself) to know its file descriptor limit, so this
- estimates.
- """
-
- def __init__(self, torPid):
- self.fdFile, self.fdConn, self.fdMisc = [], [], []
- self.fdLimit = 0
- self.errorMsg = ""
- self.scroll = 0
-
- try:
- ulimitCall = None
-
- # retrieves list of open files, options are:
- # n = no dns lookups, p = by pid, -F = show fields (L = login name, n = opened files)
- # TODO: better rewrite to take advantage of sysTools
-
- if not sysTools.isAvailable("lsof"): raise Exception("error: lsof is unavailable")
- results = sysTools.call("lsof -np %s -F Ln" % torPid)
-
- # 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)
-
- self.fdFile.sort()
- self.fdConn.sort()
- self.fdMisc.sort()
-
- # This is guessing the open file limit. Unfortunately there's no way
- # (other than "/usr/proc/bin/pfiles pid | grep rlimit" under Solaris) to
- # get the file descriptor limit for an arbitrary process. What we need is
- # for the tor process to provide the return value of the "getrlimit"
- # function via a GET_INFO call.
- if torUser.strip() == "debian-tor":
- # probably loaded via /etc/init.d/tor which changes descriptor limit
- self.fdLimit = 8192
- else:
- # 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("error: ulimit is unavailable")
- self.fdLimit = int(results[0])
-
- # can't use sysTools for this call because ulimit isn't in the path...
- # so how the **** am I to detect if it's available!
- #if not sysTools.isAvailable("ulimit"): raise Exception("error: ulimit is unavailable")
- #results = sysTools.call("ulimit -Hn")
- #if len(results) == 0: raise Exception("error: ulimit call failed")
- #self.fdLimit = int(results[0])
- except Exception, exc:
- # problem arose in calling or parsing lsof or ulimit calls
- self.errorMsg = str(exc)
- finally:
- if ulimitCall: ulimitCall.close()
-
- def handleKey(self, key, height):
- totalEntries = len(self.fdFile) + len(self.fdConn) + len(self.fdMisc)
-
- if key == curses.KEY_UP: self.scroll = max(self.scroll - 1, 0)
- elif key == curses.KEY_DOWN: self.scroll = max(0, min(self.scroll + 1, totalEntries - height))
- elif key == curses.KEY_PPAGE: self.scroll = max(self.scroll - height, 0)
- elif key == curses.KEY_NPAGE: self.scroll = max(0, min(self.scroll + height, totalEntries - height))
-
-def showFileDescriptorPopup(popup, stdscr, torPid):
- """
- Presents open file descriptors in popup window with the following controls:
- Up, Down, Page Up, Page Down - scroll descriptors
- Any other key - close popup
- """
-
- properties = PopupProperties(torPid)
-
- if not panel.CURSES_LOCK.acquire(False): return
- try:
- if properties.errorMsg:
- popupWidth = len(properties.errorMsg) + 4
- popupHeight = 3
- else:
- # uses longest entry to determine popup width
- popupWidth = 40 # minimum width
- 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) + 4
-
- popup.setHeight(popupHeight)
- popup.recreate(stdscr, popupWidth)
-
- while True:
- draw(popup, properties)
- key = stdscr.getch()
-
- if key in (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_PPAGE, curses.KEY_NPAGE):
- # navigation - tweak properties and recreate popup
- properties.handleKey(key, popup.maxY - 4)
- else:
- # closes popup
- break
-
- popup.height = 9
- popup.recreate(stdscr, 80)
- finally:
- panel.CURSES_LOCK.release()
-
-def draw(popup, properties):
- popup.clear()
- popup.win.box()
-
- # top label
- popup.addstr(0, 0, "Open File Descriptors:", curses.A_STANDOUT)
-
- if properties.errorMsg:
- popup.addstr(1, 2, properties.errorMsg, curses.A_BOLD | uiTools.getColor("red"))
- else:
- # text with file descriptor count and limit
- fdCount = len(properties.fdFile) + len(properties.fdConn) + len(properties.fdMisc)
- fdCountPer = 100 * fdCount / max(properties.fdLimit, 1)
-
- statsColor = "green"
- if fdCountPer >= 90: statsColor = "red"
- elif fdCountPer >= 50: statsColor = "yellow"
-
- countMsg = "%i / %i (%i%%)" % (fdCount, properties.fdLimit, fdCountPer)
- popup.addstr(1, 2, countMsg, curses.A_BOLD | uiTools.getColor(statsColor))
-
- # provides a progress bar reflecting the stats
- barWidth = popup.maxX - len(countMsg) - 6 # space between "[ ]" in progress bar
- 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 | uiTools.getColor(statsColor))
- popup.addstr(1, len(countMsg) + 4 + barWidth, "]", curses.A_BOLD)
-
- popup.win.hline(2, 1, curses.ACS_HLINE, popup.maxX - 2)
-
- # scrollable file descriptor listing
- lineNum = 3
- entryNum = properties.scroll
- while lineNum <= popup.maxY - 2:
- if entryNum < len(properties.fdFile):
- line = properties.fdFile[entryNum]
- color = "green"
- elif entryNum < len(properties.fdFile) + len(properties.fdMisc):
- line = properties.fdMisc[entryNum - len(properties.fdFile)]
- color = "cyan"
- else:
- line = properties.fdConn[entryNum - len(properties.fdFile) - len(properties.fdMisc)]
- color = "blue"
-
- popup.addstr(lineNum, 2, line, curses.A_BOLD | uiTools.getColor(color))
- lineNum += 1
- entryNum += 1
-
- popup.refresh()
-
Modified: arm/trunk/src/interface/headerPanel.py
===================================================================
--- arm/trunk/src/interface/headerPanel.py 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/src/interface/headerPanel.py 2011-04-18 02:34:38 UTC (rev 24651)
@@ -16,9 +16,10 @@
import os
import time
+import curses
import threading
-from util import panel, sysTools, torTools, uiTools
+from util import log, panel, sysTools, torTools, uiTools
# minimum width for which panel attempts to double up contents (two columns to
# better use screen real estate)
@@ -32,24 +33,32 @@
VERSION_STATUS_COLORS = {"new": "blue", "new in series": "blue", "obsolete": "red", "recommended": "green",
"old": "red", "unrecommended": "red", "unknown": "cyan"}
+DEFAULT_CONFIG = {"features.showFdUsage": False,
+ "log.fdUsageSixtyPercent": log.NOTICE,
+ "log.fdUsageNinetyPercent": log.WARN}
+
class HeaderPanel(panel.Panel, threading.Thread):
"""
Top area contenting tor settings and system information. Stats are stored in
the vals mapping, keys including:
tor/ version, versionStatus, nickname, orPort, dirPort, controlPort,
exitPolicy, isAuthPassword (bool), isAuthCookie (bool),
- orListenAddr, *address, *fingerprint, *flags, pid, startTime
+ orListenAddr, *address, *fingerprint, *flags, pid, startTime,
+ *fdUsed, fdLimit, isFdLimitEstimate
sys/ hostname, os, version
stat/ *%torCpu, *%armCpu, *rss, *%mem
* volatile parameter that'll be reset on each update
"""
- def __init__(self, stdscr, startTime):
+ def __init__(self, stdscr, startTime, config = None):
panel.Panel.__init__(self, stdscr, "header", 0)
threading.Thread.__init__(self)
self.setDaemon(True)
+ self._config = dict(DEFAULT_CONFIG)
+ if config: config.update(self._config)
+
self._isTorConnected = True
self._lastUpdate = -1 # time the content was last revised
self._isPaused = False # prevents updates if true
@@ -78,6 +87,10 @@
# changes.
self._lastResourceFetch = -1
+ # flag to indicate if we've already given file descriptor warnings
+ self._isFdSixtyPercentWarned = False
+ self._isFdNinetyPercentWarned = False
+
self.vals = {}
self.valsLock = threading.RLock()
self._update(True)
@@ -177,11 +190,36 @@
else: break
if self.vals["tor/orPort"]:
- # Line 4 / Line 2 Right (fingerprint)
+ # Line 4 / Line 2 Right (fingerprint, and possibly file descriptor usage)
y, x = (1, leftWidth) if isWide else (3, 0)
+
fingerprintLabel = uiTools.cropStr("fingerprint: %s" % self.vals["tor/fingerprint"], width)
self.addstr(y, x, fingerprintLabel)
+ # if there's room and we're able to retrieve both the file descriptor
+ # usage and limit then it might be presented
+ if width - x - 59 >= 20 and self.vals["tor/fdUsed"] and self.vals["tor/fdLimit"]:
+ # display file descriptor usage if we're either configured to do so or
+ # running out
+
+ fdPercent = 100 * self.vals["tor/fdUsed"] / self.vals["tor/fdLimit"]
+
+ if fdPercent >= 60 or self._config["features.showFdUsage"]:
+ fdPercentLabel, fdPercentFormat = "%i%%" % fdPercent, curses.A_NORMAL
+ if fdPercent >= 95:
+ fdPercentFormat = curses.A_BOLD | uiTools.getColor("red")
+ elif fdPercent >= 90:
+ fdPercentFormat = uiTools.getColor("red")
+ elif fdPercent >= 60:
+ fdPercentFormat = uiTools.getColor("yellow")
+
+ estimateChar = "?" if self.vals["tor/isFdLimitEstimate"] else ""
+ baseLabel = "file desc: %i / %i%s (" % (self.vals["tor/fdUsed"], self.vals["tor/fdLimit"], estimateChar)
+
+ self.addstr(y, x + 59, baseLabel)
+ self.addstr(y, x + 59 + len(baseLabel), fdPercentLabel, fdPercentFormat)
+ self.addstr(y, x + 59 + len(baseLabel) + len(fdPercentLabel), ")")
+
# Line 5 / Line 3 Left (flags)
if self._isTorConnected:
flagLine = "flags: "
@@ -349,6 +387,12 @@
policyEntries += [policy.strip() for policy in exitPolicy.split(",")]
self.vals["tor/exitPolicy"] = ", ".join(policyEntries)
+ # file descriptor limit for the process, if this can't be determined
+ # then the limit is None
+ fdLimit, fdIsEstimate = conn.getMyFileDescriptorLimit()
+ self.vals["tor/fdLimit"] = fdLimit
+ self.vals["tor/isFdLimitEstimate"] = fdIsEstimate
+
# system information
unameVals = os.uname()
self.vals["sys/hostname"] = unameVals[1]
@@ -364,6 +408,7 @@
# reverts volatile parameters to defaults
self.vals["tor/fingerprint"] = "Unknown"
self.vals["tor/flags"] = []
+ self.vals["tor/fdUsed"] = 0
self.vals["stat/%torCpu"] = "0"
self.vals["stat/%armCpu"] = "0"
self.vals["stat/rss"] = "0"
@@ -377,6 +422,27 @@
self.vals["tor/fingerprint"] = conn.getInfo("fingerprint", self.vals["tor/fingerprint"])
self.vals["tor/flags"] = conn.getMyFlags(self.vals["tor/flags"])
+ # Updates file descriptor usage and logs if the usage is high. If we don't
+ # have a known limit or it's obviously faulty (being lower than our
+ # current usage) then omit file descriptor functionality.
+ if self.vals["tor/fdLimit"]:
+ fdUsed = conn.getMyFileDescriptorUsage()
+ if fdUsed and fdUsed <= self.vals["tor/fdLimit"]: self.vals["tor/fdUsed"] = fdUsed
+ else: self.vals["tor/fdUsed"] = 0
+
+ if self.vals["tor/fdUsed"] and self.vals["tor/fdLimit"]:
+ fdPercent = 100 * self.vals["tor/fdUsed"] / self.vals["tor/fdLimit"]
+ estimatedLabel = " estimated" if self.vals["tor/isFdLimitEstimate"] else ""
+ msg = "Tor's%s file descriptor usage is at %i%%." % (estimatedLabel, fdPercent)
+
+ if fdPercent >= 90 and not self._isFdNinetyPercentWarned:
+ self._isFdSixtyPercentWarned, self._isFdNinetyPercentWarned = True, True
+ msg += " If you run out Tor will be unable to continue functioning."
+ log.log(self._config["log.fdUsageNinetyPercent"], msg)
+ elif fdPercent >= 60 and not self._isFdSixtyPercentWarned:
+ self._isFdSixtyPercentWarned = True
+ log.log(self._config["log.fdUsageSixtyPercent"], msg)
+
# ps or proc derived resource usage stats
if self.vals["tor/pid"]:
resourceTracker = sysTools.getResourceTracker(self.vals["tor/pid"])
Modified: arm/trunk/src/util/procTools.py
===================================================================
--- arm/trunk/src/util/procTools.py 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/src/util/procTools.py 2011-04-18 02:34:38 UTC (rev 24651)
@@ -96,6 +96,31 @@
_logProcRuntime("cwd", "/proc/%s/cwd" % pid, startTime)
return cwd
+def getUid(pid):
+ """
+ Provides the user ID the given process is running under. This is None if it
+ can't be determined.
+
+ Arguments:
+ pid - queried process
+ """
+
+ startTime = time.time()
+ statusFile = open("/proc/%s/status" % pid)
+ statusFileLines = statusFile.readlines()
+ statusFile.close()
+
+ result = None
+ for line in statusFileLines:
+ if line.startswith("Uid:"):
+ lineComp = line.split()
+
+ if len(lineComp) >= 2 and lineComp[1].isdigit():
+ result = lineComp[1]
+
+ _logProcRuntime("uid", "/proc/%s/status[Uid]" % pid, startTime)
+ return result
+
def getMemoryUsage(pid):
"""
Provides the memory usage in bytes for the given process of the form:
Modified: arm/trunk/src/util/sysTools.py
===================================================================
--- arm/trunk/src/util/sysTools.py 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/src/util/sysTools.py 2011-04-18 02:34:38 UTC (rev 24651)
@@ -8,8 +8,10 @@
from util import log, procTools, uiTools
-# mapping of commands to if they're available or not
-CMD_AVAILABLE_CACHE = {}
+# Mapping of commands to if they're available or not. This isn't always
+# reliable, failing for some special commands. For these the cache is
+# prepopulated to skip lookups.
+CMD_AVAILABLE_CACHE = {"ulimit": True}
# cached system call results, mapping the command issued to the (time, results) tuple
CALL_CACHE = {}
Modified: arm/trunk/src/util/torTools.py
===================================================================
--- arm/trunk/src/util/torTools.py 2011-04-17 17:25:12 UTC (rev 24650)
+++ arm/trunk/src/util/torTools.py 2011-04-18 02:34:38 UTC (rev 24651)
@@ -10,6 +10,7 @@
"""
import os
+import pwd
import time
import socket
import thread
@@ -55,7 +56,8 @@
"config/names", "info/names", "features/names", "events/names",
"nsEntry", "descEntry", "address", "bwRate", "bwBurst",
"bwObserved", "bwMeasured", "flags", "parsedVersion", "pid",
- "pathPrefix", "startTime", "authorities", "circuits", "hsPorts")
+ "user", "fdLimit", "pathPrefix", "startTime", "authorities",
+ "circuits", "hsPorts")
CACHE_GETINFO_PREFIX_ARGS = ("ip-to-country/", )
# Tor has a couple messages (in or/router.c) for when our ip address changes:
@@ -838,6 +840,52 @@
return self._getRelayAttr("pid", None)
+ def getMyUser(self):
+ """
+ Provides the user this process is running under. If unavailable this
+ provides None.
+ """
+
+ return self._getRelayAttr("user", None)
+
+ def getMyFileDescriptorUsage(self):
+ """
+ Provides the number of file descriptors currently being used by this
+ process. This returns None if this can't be determined.
+ """
+
+ # The file descriptor usage is the size of the '/proc/<pid>/fd' contents
+ # http://linuxshellaccount.blogspot.com/2008/06/finding-number-of-open-file-descriptors.html
+ # I'm not sure about other platforms (like BSD) so erroring out there.
+
+ self.connLock.acquire()
+
+ result = None
+ if self.isAlive() and procTools.isProcAvailable():
+ myPid = self.getMyPid()
+
+ if myPid:
+ try: result = len(os.listdir("/proc/%s/fd" % myPid))
+ except: pass
+
+ self.connLock.release()
+
+ return result
+
+ def getMyFileDescriptorLimit(self):
+ """
+ Provides the maximum number of file descriptors this process can have.
+ Only the Tor process itself reliably knows this value, and the option for
+ getting this was added in Tor 0.2.3.x-final. If that's unavailable then
+ we estimate the file descriptor limit based on other factors.
+
+ The return result is a tuple of the form:
+ (fileDescLimit, isEstimate)
+ and if all methods fail then both values are None.
+ """
+
+ return self._getRelayAttr("fdLimit", (None, True))
+
def getMyDirAuthorities(self):
"""
Provides a listing of IP/port tuples for the directory authorities we've
@@ -1645,6 +1693,55 @@
result = parseVersion(self.getInfo("version", ""))
elif key == "pid":
result = getPid(int(self.getOption("ControlPort", 9051)), self.getOption("PidFile"))
+ elif key == "user":
+ # This was added in Tor 0.2.3.x-final so it's quite likely unavailable.
+ # Even if it is, it might fail and return an empty string.
+ queriedUser = self.getInfo("process/user")
+
+ if queriedUser != None and queriedUser != "":
+ result = queriedUser
+ else:
+ myPid = self.getMyPid()
+
+ if myPid:
+ # if proc contents are available then fetch the pid from there and
+ # convert it to the username
+ if procTools.isProcAvailable():
+ try:
+ myUid = procTools.getUid(myPid)
+ if myUid and myUid.isdigit():
+ result = pwd.getpwuid(int(myUid)).pw_name
+ except: pass
+
+ # fall back to querying via ps
+ if not result:
+ psResults = sysTools.call("ps -o user %s" % myPid)
+ if psResults and len(psResults) >= 2: result = psResults[1].strip()
+ elif key == "fdLimit":
+ # This was added in Tor 0.2.3.x-final so it's quite likely unavailable.
+ # Even if it is, it might fail and return -1.
+
+ queriedLimit = self.getInfo("process/descriptor-limit")
+
+ if queriedLimit != None and queriedLimit != "-1":
+ result = (int(queriedLimit), False)
+ else:
+ torUser = self.getMyUser()
+
+ # This is guessing the open file limit. Unfortunately there's no way
+ # (other than "/usr/proc/bin/pfiles pid | grep rlimit" under Solaris)
+ # to get the file descriptor limit for an arbitrary process.
+
+ if torUser == "debian-tor":
+ # probably loaded via /etc/init.d/tor which changes descriptor limit
+ result = (8192, True)
+ else:
+ # uses ulimit to estimate (-H is for hard limit, which is what tor uses)
+ ulimitResults = sysTools.call("ulimit -Hn")
+
+ if ulimitResults:
+ ulimit = ulimitResults[0].strip()
+ if ulimit.isdigit(): result = (int(ulimit), True)
elif key == "pathPrefix":
# make sure the path prefix is valid and exists (providing a notice if not)
prefixPath = CONFIG["features.pathPrefix"].strip()
More information about the tor-commits
mailing list