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-de... + # 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()
tor-commits@lists.torproject.org