import nyx.popups from nyx.util import panel, tor_config, tor_controller, ui_tools from stem.control import State from stem.util import conf, enum, str_tools def conf_handler(key, value): if key == 'features.config.file.max_lines_per_entry': return max(1, value) CONFIG = conf.config_dict('nyx', { 'features.config.file.showScrollbars': True, 'features.config.file.max_lines_per_entry': 8, }, conf_handler) # TODO: The nyxrc use case is incomplete. There should be equivilant reloading # and validation capabilities to the torrc. Config = enum.Enum('TORRC', 'NYXRC') # configuration file types that can be displayed class TorrcPanel(panel.Panel): """ Renders the current torrc or nyxrc with syntax highlighting in a scrollable area. """ def __init__(self, stdscr, config_type): panel.Panel.__init__(self, stdscr, 'torrc', 0)
import time import threading from stem.util import conf, log, proc, str_tools, system PROCESS_NAME_CACHE = {} # mapping of pids to their process names RESOURCE_TRACKERS = {} # mapping of pids to their resource tracker instances # Runtimes for system calls, used to estimate cpu usage. Entries are tuples of # the form: # (time called, runtime) RUNTIMES = [] SAMPLING_PERIOD = 5 # time of the sampling period CONFIG = conf.config_dict("arm", { "queries.resourceUsage.rate": 5, }) # TODO: This was a bit of a hack, and one that won't work now that we lack our # call() method to populate RUNTIMES. def getSysCpuUsage(): """ Provides an estimate of the cpu usage for system calls made through this module, based on a sampling period of five seconds. The os.times() function, unfortunately, doesn't seem to take popen calls into account. This returns a float representing the percentage used. """ currentTime = time.time()
from stem.util import conf, enum, log, str_tools, system def conf_handler(key, value): if key == "config.important": # stores lowercase entries to drop case sensitivity return [entry.lower() for entry in value] CONFIG = conf.config_dict("arm", { "features.torrc.validate": True, "config.important": [], "torrc.alias": {}, "torrc.label.size.b": [], "torrc.label.size.kb": [], "torrc.label.size.mb": [], "torrc.label.size.gb": [], "torrc.label.size.tb": [], "torrc.label.time.sec": [], "torrc.label.time.min": [], "torrc.label.time.hour": [], "torrc.label.time.day": [], "torrc.label.time.week": [], }, conf_handler) def general_conf_handler(config, key): value = config.get(key) if key.startswith("config.summary."): # we'll look for summary keys with a lowercase config name CONFIG[key.lower()] = value elif key.startswith("torrc.label.") and value:
import nyx.panel import nyx.popups import nyx.tracker from stem.util import conf, log from nyx import msg, tor_controller from nyx.curses import RED, GREEN, YELLOW, CYAN, WHITE, BOLD, HIGHLIGHT MIN_DUAL_COL_WIDTH = 141 # minimum width where we'll show two columns SHOW_FD_THRESHOLD = 60 # show file descriptor usage if usage is over this percentage UPDATE_RATE = 5 # rate in seconds at which we refresh CONFIG = conf.config_dict('nyx', { 'attr.flag_colors': {}, 'attr.version_status_colors': {}, 'tor.chroot': '', }) class HeaderPanel(nyx.panel.DaemonPanel): """ Top area containing tor settings and system information. """ def __init__(self): nyx.panel.DaemonPanel.__init__(self, UPDATE_RATE) self._vals = Sampling.create() self._last_width = nyx.curses.screen_size().width self._reported_inactive = False
import cli.controller from cli.graphing import graphPanel from util import torTools, uiTools from stem.control import State from stem.util import conf, log, str_tools, system def conf_handler(key, value): if key == "features.graph.bw.accounting.rate": return max(1, value) CONFIG = conf.config_dict("arm", { "features.graph.bw.transferInBytes": False, "features.graph.bw.accounting.show": True, "features.graph.bw.accounting.rate": 10, "features.graph.bw.accounting.isTimeLong": False, }, conf_handler) DL_COLOR, UL_COLOR = "green", "cyan" # width at which panel abandons placing optional stats (avg and total) with # header in favor of replacing the x-axis label COLLAPSE_WIDTH = 135 # valid keys for the accountingInfo mapping ACCOUNTING_ARGS = ("status", "resetTime", "read", "written", "readLimit", "writtenLimit") PREPOPULATE_SUCCESS_MSG = "Read the last day of bandwidth history from the state file" PREPOPULATE_FAILURE_MSG = "Unable to prepopulate bandwidth information (%s)"
:var float timestamp: unix timestamp for when this information was fetched """ import collections import os import time import threading import stem.control from nyx import log, tor_controller from stem.util import conf, connection, enum, proc, str_tools, system CONFIG = conf.config_dict('nyx', { 'queries.connections.rate': 5, 'queries.resources.rate': 5, 'queries.port_usage.rate': 5, }) CONNECTION_TRACKER = None RESOURCE_TRACKER = None PORT_USAGE_TRACKER = None CONSENSUS_TRACKER = None CustomResolver = enum.Enum( ('INFERENCE', 'by inference'), ) # Extending stem's Connection tuple with attributes for the uptime of the # connection.
def conf_handler(key, value): if key == "features.redrawRate": return max(1, value) elif key == "features.refreshRate": return max(0, value) CONFIG = conf.config_dict( "arm", { "startup.events": "N3", "startup.dataDirectory": "~/.arm", "startup.blindModeEnabled": False, "features.panels.show.graph": True, "features.panels.show.log": True, "features.panels.show.connection": True, "features.panels.show.config": True, "features.panels.show.torrc": True, "features.redrawRate": 5, "features.refreshRate": 5, "features.confirmQuit": True, "features.graph.type": 1, "features.graph.bw.prepopulate": True, }, conf_handler) GraphStat = enum.Enum("BANDWIDTH", "CONNECTIONS", "SYSTEM_RESOURCES") # maps 'features.graph.type' config values to the initial types GRAPH_INIT_STATS = { 1: GraphStat.BANDWIDTH, 2: GraphStat.CONNECTIONS, 3: GraphStat.SYSTEM_RESOURCES
# listing types Listing = enum.Enum(("IP_ADDRESS", "IP Address"), "HOSTNAME", "FINGERPRINT", "NICKNAME") def conf_handler(key, value): if key == "features.connection.listingType": return conf.parse_enum(key, value, Listing) elif key == "features.connection.refreshRate": return max(1, value) elif key == "features.connection.order": return conf.parse_enum_csv(key, value[0], entries.SortAttr, 3) CONFIG = conf.config_dict("arm", { "features.connection.resolveApps": True, "features.connection.listingType": Listing.IP_ADDRESS, "features.connection.order": [ entries.SortAttr.CATEGORY, entries.SortAttr.LISTING, entries.SortAttr.UPTIME], "features.connection.refreshRate": 5, "features.connection.showIps": True, }, conf_handler) class ConnectionPanel(panel.Panel, threading.Thread): """ Listing of connections tor is making, with information correlated against the current consensus and other data sources. """ def __init__(self, stdscr): panel.Panel.__init__(self, stdscr, "connections", 0) threading.Thread.__init__(self) self.setDaemon(True)
elif key == 'features.graph.bound': if value not in Bounds: log.warn("'%s' isn't a valid graph bounds, options are: %s" % (value, ', '.join(Bounds))) return CONFIG['features.graph.bound'] # keep the default CONFIG = conf.config_dict( 'nyx', { 'attr.hibernate_color': {}, 'attr.graph.title': {}, 'attr.graph.header.primary': {}, 'attr.graph.header.secondary': {}, 'features.graph.height': 7, 'features.graph.type': GraphStat.BANDWIDTH, 'features.graph.interval': Interval.EACH_SECOND, 'features.graph.bound': Bounds.LOCAL_MAX, 'features.graph.max_width': 300, # we need some sort of max size so we know how much graph data to retain 'features.panels.show.connection': True, 'features.graph.bw.transferInBytes': False, 'features.graph.bw.accounting.show': True, }, conf_handler) class GraphData(object): """ Graphable statistical information. :var int latest_value: last value we recorded
if key == 'features.log.maxLinesPerEntry': return max(1, value) elif key == 'features.log.prepopulateReadLimit': return max(0, value) elif key == 'features.log.maxRefreshRate': return max(10, value) elif key == 'cache.log_panel.size': return max(1000, value) CONFIG = conf.config_dict('nyx', { 'features.logFile': '', 'features.log.showDuplicateEntries': False, 'features.log.maxLinesPerEntry': 6, 'features.log.prepopulate': True, 'features.log.prepopulateReadLimit': 5000, 'features.log.maxRefreshRate': 300, 'features.log.regex': [], 'cache.log_panel.size': 1000, 'msg.misc.event_types': '', 'attr.log_color': {}, }, conf_handler) # The height of the drawn content is estimated based on the last time we redrew # the panel. It's chiefly used for scrolling and the bar indicating its # position. Letting the estimate be too inaccurate results in a display bug, so # redraws the display if it's off by this threshold. CONTENT_HEIGHT_REDRAW_THRESHOLD = 3 # Log buffer so we start collecting stem/nyx events when imported. This is used # to make our LogPanel when curses initializes.
def conf_handler(key, value): if key == "features.graph.height": return max(MIN_GRAPH_HEIGHT, value) elif key == "features.graph.maxWidth": return max(1, value) elif key == "features.graph.interval": return max(0, min(len(UPDATE_INTERVALS) - 1, value)) elif key == "features.graph.bound": return max(0, min(2, value)) # used for setting defaults when initializing GraphStats and GraphPanel instances CONFIG = conf.config_dict("arm", { "features.graph.height": 7, "features.graph.interval": 0, "features.graph.bound": 1, "features.graph.maxWidth": 150, "features.graph.showIntermediateBounds": True, }, conf_handler) class GraphStats: """ Module that's expected to update dynamically and provide attributes to be graphed. Up to two graphs (a 'primary' and 'secondary') can be displayed at a time and timescale parameters use the labels defined in UPDATE_INTERVALS. """ def __init__(self): """ Initializes parameters needed to present a graph. """
# color support is unavailable COLOR_ATTR_INITIALIZED = False COLOR_ATTR = dict([(color, 0) for color in COLOR_LIST]) Ending = enum.Enum("ELLIPSE", "HYPHEN") SCROLL_KEYS = (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_PPAGE, curses.KEY_NPAGE, curses.KEY_HOME, curses.KEY_END) def conf_handler(key, value): if key == "features.colorOverride" and value != "none": try: setColorOverride(value) except ValueError, exc: log.notice(exc) CONFIG = conf.config_dict("arm", { "features.colorOverride": "none", "features.colorInterface": True, "features.acsSupport": True, "features.printUnicode": True, }, conf_handler) # Flag indicating if unicode is supported by curses. If None then this has yet # to be determined. IS_UNICODE_SUPPORTED = None def demoGlyphs(): """ Displays all ACS options with their corresponding representation. These are undocumented in the pydocs. For more information see the following man page: http://www.mkssoftware.com/docs/man5/terminfo.5.asp """ try: curses.wrapper(_showGlyphs)
return max(0, value) elif key == "features.config.state.colWidth.option": return max(5, value) elif key == "features.config.state.colWidth.value": return max(5, value) elif key == "features.config.order": return conf.parse_enum_csv(key, value[0], Field, 3) CONFIG = conf.config_dict( "arm", { "features.config.order": [Field.MAN_ENTRY, Field.OPTION, Field.IS_DEFAULT], "features.config.selectionDetails.height": 6, "features.config.prepopulateEditValues": True, "features.config.state.showPrivateOptions": False, "features.config.state.showVirtualOptions": False, "features.config.state.colWidth.option": 25, "features.config.state.colWidth.value": 15, }, conf_handler, ) def getFieldFromLabel(fieldLabel): """ Converts field labels back to their enumeration, raising a ValueError if it doesn't exist. """ for entryEnum in FIELD_ATTR:
Category.CONTROL: "red" } # static data for listing format # <src> --> <dst> <etc><padding> LABEL_FORMAT = "%s --> %s %s%s" LABEL_MIN_PADDING = 2 # min space between listing label and following data # sort value for scrubbed ip addresses SCRUBBED_IP_VAL = 255**4 CONFIG = conf.config_dict( "arm", { "features.connection.markInitialConnections": True, "features.connection.showIps": True, "features.connection.showExitPort": True, "features.connection.showColumn.fingerprint": True, "features.connection.showColumn.nickname": True, "features.connection.showColumn.destination": True, "features.connection.showColumn.expandedIp": True, }) class Endpoint: """ Collection of attributes associated with a connection endpoint. This is a thin wrapper for torUtil functions, making use of its caching for performance. """ def __init__(self, ipAddr, port): self.ipAddr = ipAddr self.port = port
DNS_ERROR_CODES = ("1(FORMERR)", "2(SERVFAIL)", "3(NXDOMAIN)", "4(NOTIMP)", "5(REFUSED)", "6(YXDOMAIN)", "7(YXRRSET)", "8(NXRRSET)", "9(NOTAUTH)", "10(NOTZONE)", "16(BADVERS)") def conf_handler(key, value): if key == "queries.hostnames.poolSize": return max(1, value) elif key == "cache.hostnames.size": return max(100, value) elif key == "cache.hostnames.trimSize": return max(10, value) elif key == "cache.hostnames.trimSize": return min(value, CONFIG["cache.hostnames.size"] / 2) CONFIG = conf.config_dict("arm", { "queries.hostnames.poolSize": 5, "queries.hostnames.useSocketModule": False, "cache.hostnames.size": 700000, "cache.hostnames.trimSize": 200000, }, conf_handler) def start(): """ Primes the service to start resolving addresses. Calling this explicitly is not necessary since resolving any address will start the service if it isn't already running. """ global RESOLVER RESOLVER_LOCK.acquire() if not isRunning(): RESOLVER = _Resolver() RESOLVER_LOCK.release()
import popups from util import panel, torConfig, torTools, uiTools from stem.control import State from stem.util import conf, enum def conf_handler(key, value): if key == "features.config.file.maxLinesPerEntry": return max(1, value) CONFIG = conf.config_dict( "arm", { "features.config.file.showScrollbars": True, "features.config.file.maxLinesPerEntry": 8, }, conf_handler) # TODO: The armrc use case is incomplete. There should be equivilant reloading # and validation capabilities to the torrc. Config = enum.Enum("TORRC", "ARMRC") # configuration file types that can be displayed class TorrcPanel(panel.Panel): """ Renders the current torrc or armrc with syntax highlighting in a scrollable area. """ def __init__(self, stdscr, configType):
NYX_CONTROLLER = None def conf_handler(key, value): if key == 'features.redrawRate': return max(1, value) elif key == 'features.refreshRate': return max(0, value) CONFIG = conf.config_dict('nyx', { 'features.acsSupport': True, 'features.panels.show.graph': True, 'features.panels.show.log': True, 'features.panels.show.connection': True, 'features.panels.show.config': True, 'features.panels.show.torrc': True, 'features.redrawRate': 5, 'features.refreshRate': 5, 'features.confirmQuit': True, 'start_time': 0, }, conf_handler) def get_controller(): """ Provides the nyx controller instance. """ return NYX_CONTROLLER
def conf_handler(key, value): if key == 'features.redrawRate': return max(1, value) elif key == 'features.refreshRate': return max(0, value) CONFIG = conf.config_dict( 'nyx', { 'features.acsSupport': True, 'features.panels.show.graph': True, 'features.panels.show.log': True, 'features.panels.show.connection': True, 'features.panels.show.config': True, 'features.panels.show.torrc': True, 'features.redrawRate': 5, 'features.refreshRate': 5, 'features.confirmQuit': True, 'start_time': 0, }, conf_handler) def get_controller(): """ Provides the nyx controller instance. """ return NYX_CONTROLLER
Generates the menu for arm, binding options with their related actions. """ import functools import cli.popups import cli.controller import cli.menu.item import cli.graphing.graphPanel from util import connections, torTools, uiTools from stem.util import conf, str_tools CONFIG = conf.config_dict("arm", { "features.log.showDuplicateEntries": False, }) def makeMenu(): """ Constructs the base menu and all of its contents. """ baseMenu = cli.menu.item.Submenu("") baseMenu.add(makeActionsMenu()) baseMenu.add(makeViewMenu()) control = cli.controller.getController() for pagePanel in control.getDisplayPanels(includeSticky = False): if pagePanel.getName() == "graph":
def conf_handler(key, value): if key == "queries.hostnames.poolSize": return max(1, value) elif key == "cache.hostnames.size": return max(100, value) elif key == "cache.hostnames.trimSize": return max(10, value) elif key == "cache.hostnames.trimSize": return min(value, CONFIG["cache.hostnames.size"] / 2) CONFIG = conf.config_dict( "arm", { "queries.hostnames.poolSize": 5, "queries.hostnames.useSocketModule": False, "cache.hostnames.size": 700000, "cache.hostnames.trimSize": 200000, }, conf_handler) def start(): """ Primes the service to start resolving addresses. Calling this explicitly is not necessary since resolving any address will start the service if it isn't already running. """ global RESOLVER RESOLVER_LOCK.acquire() if not isRunning(): RESOLVER = _Resolver()
import collections import os import time import threading import nyx import stem.control import stem.descriptor.router_status_entry import stem.util.log from nyx import tor_controller from stem.util import conf, connection, enum, proc, str_tools, system CONFIG = conf.config_dict('nyx', { 'connection_rate': 5, 'resource_rate': 5, 'port_usage_rate': 5, }) UNABLE_TO_USE_ANY_RESOLVER_MSG = """ We were unable to use any of your system's resolvers to get tor's connections. This is fine, but means that the connections page will be empty. This is usually permissions related so if you would like to fix this then run nyx with the same user as tor (ie, "sudo -u <tor user> nyx"). """.strip() CONNECTION_TRACKER = None RESOURCE_TRACKER = None PORT_USAGE_TRACKER = None CONSENSUS_TRACKER = None
import popups from util import panel, torConfig, torTools, uiTools from stem.control import State from stem.util import conf, enum def conf_handler(key, value): if key == "features.config.file.maxLinesPerEntry": return max(1, value) CONFIG = conf.config_dict( "arm", {"features.config.file.showScrollbars": True, "features.config.file.maxLinesPerEntry": 8}, conf_handler ) # TODO: The armrc use case is incomplete. There should be equivilant reloading # and validation capabilities to the torrc. Config = enum.Enum("TORRC", "ARMRC") # configuration file types that can be displayed class TorrcPanel(panel.Panel): """ Renders the current torrc or armrc with syntax highlighting in a scrollable area. """ def __init__(self, stdscr, configType): panel.Panel.__init__(self, stdscr, "torrc", 0)
'config', 'connection', 'graph', 'header', 'log', 'torrc', ] def conf_handler(key, value): if key == 'features.torrc.maxLineWrap': return max(1, value) CONFIG = conf.config_dict('nyx', { 'features.maxLineWrap': 8, }, conf_handler) HALT_ACTIVITY = False # prevents curses redraws if set class KeyHandler(collections.namedtuple('Help', ['key', 'description', 'current'])): """ Action that can be taken via a given keybinding. :var str key: key the user can press :var str description: description of what it does :var str current: optional current value :param str key: key the user can press :param str description: description of what it does
SCROLL_KEYS = (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_PPAGE, curses.KEY_NPAGE, curses.KEY_HOME, curses.KEY_END) def conf_handler(key, value): if key == "features.colorOverride" and value != "none": try: setColorOverride(value) except ValueError, exc: log.notice(exc) CONFIG = conf.config_dict( "arm", { "features.colorOverride": "none", "features.colorInterface": True, "features.acsSupport": True, "features.printUnicode": True, }, conf_handler) # Flag indicating if unicode is supported by curses. If None then this has yet # to be determined. IS_UNICODE_SUPPORTED = None def demoGlyphs(): """ Displays all ACS options with their corresponding representation. These are undocumented in the pydocs. For more information see the following man page: http://www.mkssoftware.com/docs/man5/terminfo.5.asp """
from __future__ import with_statement import re import os from stem.util import conf, system # if ran directly then run over everything one level up DEFAULT_TARGET = os.path.sep.join(__file__.split(os.path.sep)[:-1]) # mapping of files to the issues that should be ignored PYFLAKES_IGNORE = None CONFIG = conf.config_dict("test", { "pyflakes.ignore": [] }) def pep8_issues(base_path = DEFAULT_TARGET): """ Checks for stylistic issues that are an issue according to the parts of PEP8 we conform to. :param str base_path: directory to be iterated over :returns: dict of the form ``path => [(line_number, message)...]`` """ # The pep8 command give output of the form... #
from __future__ import with_statement import re import os from stem.util import conf, system # if ran directly then run over everything one level up DEFAULT_TARGET = os.path.sep.join(__file__.split(os.path.sep)[:-1]) # mapping of files to the issues that should be ignored PYFLAKES_IGNORE = None CONFIG = conf.config_dict("test", { "pyflakes.ignore": [], "integ.test_directory": "./test/data", }) def pep8_issues(base_path = DEFAULT_TARGET): """ Checks for stylistic issues that are an issue according to the parts of PEP8 we conform to. :param str base_path: directory to be iterated over :returns: dict of the form ``path => [(line_number, message)...]`` """ # The pep8 command give output of the form... #
def conf_handler(key, value): if key == 'torrc.important': # stores lowercase entries to drop case sensitivity return [entry.lower() for entry in value] CONFIG = conf.config_dict('nyx', { 'features.torrc.validate': True, 'torrc.important': [], 'torrc.alias': {}, 'torrc.units.size.b': [], 'torrc.units.size.kb': [], 'torrc.units.size.mb': [], 'torrc.units.size.gb': [], 'torrc.units.size.tb': [], 'torrc.units.time.sec': [], 'torrc.units.time.min': [], 'torrc.units.time.hour': [], 'torrc.units.time.day': [], 'torrc.units.time.week': [], 'startup.data_directory': '~/.nyx', 'features.config.descriptions.enabled': True, 'features.config.descriptions.persist': True, 'tor.chroot': '', }, conf_handler) def general_conf_handler(config, key): value = config.get(key) if key.startswith('torrc.summary.'):
Generates the menu for arm, binding options with their related actions. """ import functools import cli.popups import cli.controller import cli.menu.item import cli.graphing.graphPanel from util import connections, torTools, uiTools from stem.util import conf, str_tools CONFIG = conf.config_dict("arm", { "features.log.showDuplicateEntries": False, }) def makeMenu(): """ Constructs the base menu and all of its contents. """ baseMenu = cli.menu.item.Submenu("") baseMenu.add(makeActionsMenu()) baseMenu.add(makeViewMenu()) control = cli.controller.getController() for pagePanel in control.getDisplayPanels(includeSticky=False):
'circuit', 'fingerprint', 'nickname', 'locale', ]) def conf_handler(key, value): if key == 'features.connection.order': return conf.parse_enum_csv(key, value[0], SortAttr, 3) CONFIG = conf.config_dict('nyx', { 'attr.connection.category_color': {}, 'attr.connection.sort_color': {}, 'features.connection.resolveApps': True, 'features.connection.order': [SortAttr.CATEGORY, SortAttr.IP_ADDRESS, SortAttr.UPTIME], 'features.connection.showIps': True, }, conf_handler) class Entry(object): @staticmethod @lru_cache() def from_connection(connection): return ConnectionEntry(connection) @staticmethod @lru_cache() def from_circuit(circuit): return CircuitEntry(circuit)
log.notice(msg) else: try: # range of ports (inclusive) minPort = int(portEntry[:divIndex]) maxPort = int(portEntry[divIndex + 1:]) if minPort > maxPort: raise ValueError() for port in range(minPort, maxPort + 1): PORT_USAGE[str(port)] = value except ValueError: msg = "Unable to parse port range for entry: %s" % key log.notice(msg) CONFIG = conf.config_dict("arm", { "queries.connections.minRate": 5, }, conf_handler) PORT_USAGE = {} def isValidIpAddress(ipStr): """ Returns true if input is a valid IPv4 address, false otherwise. """ # checks if theres four period separated values if not ipStr.count(".") == 3: return False # checks that each value in the octet are decimal values between 0-255 for ipComp in ipStr.split("."): if not ipComp.isdigit() or int(ipComp) < 0 or int(ipComp) > 255:
import nyx.panel.connection import nyx.panel.graph import nyx.panel.log import nyx.panel.torrc import nyx.controller import nyx.tracker import stem import stem.util.connection from nyx import tor_controller from nyx.curses import RED, WHITE, NORMAL, BOLD, UNDERLINE from stem.util import conf, str_tools CONFIG = conf.config_dict('nyx', { 'features.log.showDuplicateEntries': False, }) def make_menu(): """ Constructs the base menu and all of its contents. """ base_menu = Submenu('') base_menu.add(make_actions_menu()) base_menu.add(make_view_menu()) control = nyx.controller.get_controller() for page_panel in control.get_display_panels():
if value not in Interval: log.warn("'%s' isn't a valid graphing interval, options are: %s" % (value, ', '.join(Interval))) return CONFIG['features.graph.interval'] # keep the default elif key == 'features.graph.bound': if value not in Bounds: log.warn("'%s' isn't a valid graph bounds, options are: %s" % (value, ', '.join(Bounds))) return CONFIG['features.graph.bound'] # keep the default CONFIG = conf.config_dict('nyx', { 'attr.hibernate_color': {}, 'attr.graph.title': {}, 'attr.graph.header.primary': {}, 'attr.graph.header.secondary': {}, 'features.graph.height': 7, 'features.graph.type': GraphStat.BANDWIDTH, 'features.graph.interval': Interval.EACH_SECOND, 'features.graph.bound': Bounds.LOCAL_MAX, 'features.graph.max_width': 300, # we need some sort of max size so we know how much graph data to retain 'features.panels.show.connection': True, 'features.graph.bw.transferInBytes': False, 'features.graph.bw.accounting.show': True, }, conf_handler) class GraphData(object): """ Graphable statistical information. :var int latest_value: last value we recorded :var int total: sum of all values we've recorded :var int tick: number of events we've processed
'nickname', 'locale', ]) def conf_handler(key, value): if key == 'features.connection.order': return conf.parse_enum_csv(key, value[0], SortAttr, 3) CONFIG = conf.config_dict( 'nyx', { 'attr.connection.category_color': {}, 'attr.connection.sort_color': {}, 'features.connection.resolveApps': True, 'features.connection.order': [SortAttr.CATEGORY, SortAttr.IP_ADDRESS, SortAttr.UPTIME], 'features.connection.showIps': True, }, conf_handler) class Entry(object): @staticmethod @lru_cache() def from_connection(connection): return ConnectionEntry(connection) @staticmethod @lru_cache()
import stem import nyx.controller import nyx.popups from stem.control import Listener from stem.util import conf, log, proc, str_tools, system from nyx.util import msg, tor_controller, panel, tracker MIN_DUAL_COL_WIDTH = 141 # minimum width where we'll show two columns SHOW_FD_THRESHOLD = 60 # show file descriptor usage if usage is over this percentage UPDATE_RATE = 5 # rate in seconds at which we refresh CONFIG = conf.config_dict('nyx', { 'attr.flag_colors': {}, 'attr.version_status_colors': {}, }) class HeaderPanel(panel.Panel, threading.Thread): """ Top area containing tor settings and system information. """ def __init__(self, stdscr, start_time): panel.Panel.__init__(self, stdscr, 'header', 0) threading.Thread.__init__(self) self.setDaemon(True) self._vals = get_sampling()
DETAILS_HEIGHT = 8 NAME_WIDTH = 25 VALUE_WIDTH = 15 def conf_handler(key, value): if key == 'features.config.order': return conf.parse_enum_csv(key, value[0], SortAttr, 3) CONFIG = conf.config_dict( 'nyx', { 'attr.config.category_color': {}, 'attr.config.sort_color': {}, 'features.config.order': [SortAttr.MAN_PAGE_ENTRY, SortAttr.NAME, SortAttr.IS_SET], 'features.config.state.showPrivateOptions': False, 'features.config.state.showVirtualOptions': False, }, conf_handler) class ConfigEntry(object): """ Configuration option presented in the panel. :var str name: name of the configuration option :var str value_type: type of value :var stem.manual.ConfigOption manual: manual information about the option """
DETAILS_HEIGHT = 8 NAME_WIDTH = 25 VALUE_WIDTH = 15 def conf_handler(key, value): if key == 'config_order': return conf.parse_enum_csv(key, value[0], SortAttr, 3) CONFIG = conf.config_dict( 'nyx', { 'attr.config.category_color': {}, 'attr.config.sort_color': {}, 'config_order': [SortAttr.MAN_PAGE_ENTRY, SortAttr.NAME, SortAttr.IS_SET], 'show_private_options': False, 'show_virtual_options': False, }, conf_handler) @lru_cache() def manual(option): result = stem.manual.query( 'SELECT category, usage, summary, description, position FROM torrc WHERE key=?', option.upper()).fetchone() if result: return ManualEntry(*result) else:
'nickname', 'locale', ]) def conf_handler(key, value): if key == 'connection_order': return conf.parse_enum_csv(key, value[0], SortAttr, 3) CONFIG = conf.config_dict( 'nyx', { 'attr.connection.category_color': {}, 'attr.connection.sort_color': {}, 'connection_order': [SortAttr.CATEGORY, SortAttr.IP_ADDRESS, SortAttr.UPTIME], 'resolve_processes': True, 'show_addresses': True, }, conf_handler) class Entry(object): @staticmethod def from_connection(connection): if connection not in ENTRY_CACHE: ENTRY_CACHE[connection] = ConnectionEntry(connection) ENTRY_CACHE_REFERENCED[connection] = time.time() return ENTRY_CACHE[connection]
Category.SOCKS: "yellow", Category.CIRCUIT: "cyan", Category.DIRECTORY: "magenta", Category.CONTROL: "red"} # static data for listing format # <src> --> <dst> <etc><padding> LABEL_FORMAT = "%s --> %s %s%s" LABEL_MIN_PADDING = 2 # min space between listing label and following data # sort value for scrubbed ip addresses SCRUBBED_IP_VAL = 255 ** 4 CONFIG = conf.config_dict("arm", { "features.connection.markInitialConnections": True, "features.connection.showIps": True, "features.connection.showExitPort": True, "features.connection.showColumn.fingerprint": True, "features.connection.showColumn.nickname": True, "features.connection.showColumn.destination": True, "features.connection.showColumn.expandedIp": True, }) class Endpoint: """ Collection of attributes associated with a connection endpoint. This is a thin wrapper for torUtil functions, making use of its caching for performance. """ def __init__(self, ipAddr, port): self.ipAddr = ipAddr self.port = port
from __future__ import with_statement import re import os from stem.util import conf, system # if ran directly then run over everything one level up DEFAULT_TARGET = os.path.sep.join(__file__.split(os.path.sep)[:-1]) # mapping of files to the issues that should be ignored PYFLAKES_IGNORE = None CONFIG = conf.config_dict("test", { "pyflakes.ignore": [], "integ.test_directory": "./test/data", }) def pep8_issues(base_path=DEFAULT_TARGET): """ Checks for stylistic issues that are an issue according to the parts of PEP8 we conform to. :param str base_path: directory to be iterated over :returns: dict of the form ``path => [(line_number, message)...]`` """ # The pep8 command give output of the form... #
from cli.graphing import graphPanel from util import torTools, uiTools from stem.control import State from stem.util import conf, log, str_tools, system def conf_handler(key, value): if key == "features.graph.bw.accounting.rate": return max(1, value) CONFIG = conf.config_dict( "arm", { "features.graph.bw.transferInBytes": False, "features.graph.bw.accounting.show": True, "features.graph.bw.accounting.rate": 10, "features.graph.bw.accounting.isTimeLong": False, }, conf_handler) DL_COLOR, UL_COLOR = "green", "cyan" # width at which panel abandons placing optional stats (avg and total) with # header in favor of replacing the x-axis label COLLAPSE_WIDTH = 135 # valid keys for the accountingInfo mapping ACCOUNTING_ARGS = ("status", "resetTime", "read", "written", "readLimit", "writtenLimit") PREPOPULATE_SUCCESS_MSG = "Read the last day of bandwidth history from the state file"
:var float timestamp: unix timestamp for when this information was fetched """ import collections import time import threading import stem.control from stem.util import conf, connection, proc, str_tools, system from nyx.util import log, tor_controller CONFIG = conf.config_dict('nyx', { 'queries.connections.rate': 5, 'queries.resources.rate': 5, 'queries.port_usage.rate': 5, }) CONNECTION_TRACKER = None RESOURCE_TRACKER = None PORT_USAGE_TRACKER = None CONSENSUS_TRACKER = None # Extending stem's Connection tuple with attributes for the uptime of the # connection. Connection = collections.namedtuple('Connection', [ 'start_time', 'is_legacy', # boolean to indicate if the connection predated us ] + list(stem.util.connection.Connection._fields))
SortAttr = enum.Enum('NAME', 'VALUE', 'VALUE_TYPE', 'CATEGORY', 'USAGE', 'SUMMARY', 'DESCRIPTION', 'MAN_PAGE_ENTRY', 'IS_SET') DETAILS_HEIGHT = 8 NAME_WIDTH = 25 VALUE_WIDTH = 15 def conf_handler(key, value): if key == 'features.config.order': return conf.parse_enum_csv(key, value[0], SortAttr, 3) CONFIG = conf.config_dict('nyx', { 'attr.config.category_color': {}, 'attr.config.sort_color': {}, 'features.config.order': [SortAttr.MAN_PAGE_ENTRY, SortAttr.NAME, SortAttr.IS_SET], 'features.config.state.showPrivateOptions': False, 'features.config.state.showVirtualOptions': False, }, conf_handler) class ConfigEntry(object): """ Configuration option presented in the panel. :var str name: name of the configuration option :var str value_type: type of value :var stem.manual.ConfigOption manual: manual information about the option """ def __init__(self, name, value_type, manual):
if key == "features.redrawRate": return max(1, value) elif key == "features.refreshRate": return max(0, value) CONFIG = conf.config_dict( "arm", { "startup.events": "N3", "startup.dataDirectory": "~/.arm", "startup.blindModeEnabled": False, "features.panels.show.graph": True, "features.panels.show.log": True, "features.panels.show.connection": True, "features.panels.show.config": True, "features.panels.show.torrc": True, "features.redrawRate": 5, "features.refreshRate": 5, "features.confirmQuit": True, "features.graph.type": 1, "features.graph.bw.prepopulate": True, }, conf_handler, ) GraphStat = enum.Enum("BANDWIDTH", "CONNECTIONS", "SYSTEM_RESOURCES") # maps 'features.graph.type' config values to the initial types GRAPH_INIT_STATS = {1: GraphStat.BANDWIDTH, 2: GraphStat.CONNECTIONS, 3: GraphStat.SYSTEM_RESOURCES}
if key == "features.connection.listingType": return conf.parse_enum(key, value, Listing) elif key == "features.connection.refreshRate": return max(1, value) elif key == "features.connection.order": return conf.parse_enum_csv(key, value[0], entries.SortAttr, 3) CONFIG = conf.config_dict( "arm", { "features.connection.resolveApps": True, "features.connection.listingType": Listing.IP_ADDRESS, "features.connection.order": [ entries.SortAttr.CATEGORY, entries.SortAttr.LISTING, entries.SortAttr.UPTIME ], "features.connection.refreshRate": 5, "features.connection.showIps": True, }, conf_handler) class ConnectionPanel(panel.Panel, threading.Thread): """ Listing of connections tor is making, with information correlated against the current consensus and other data sources. """ def __init__(self, stdscr):
def conf_handler(key, value): if key == "config.important": # stores lowercase entries to drop case sensitivity return [entry.lower() for entry in value] CONFIG = conf.config_dict( "arm", { "features.torrc.validate": True, "config.important": [], "torrc.alias": {}, "torrc.label.size.b": [], "torrc.label.size.kb": [], "torrc.label.size.mb": [], "torrc.label.size.gb": [], "torrc.label.size.tb": [], "torrc.label.time.sec": [], "torrc.label.time.min": [], "torrc.label.time.hour": [], "torrc.label.time.day": [], "torrc.label.time.week": [], }, conf_handler) def general_conf_handler(config, key): value = config.get(key) if key.startswith("config.summary."): # we'll look for summary keys with a lowercase config name CONFIG[key.lower()] = value
if value not in Interval: log.warn("'%s' isn't a valid graphing interval, options are: %s" % (value, ', '.join(Interval))) return CONFIG['graph_interval'] # keep the default elif key == 'graph_bound': if value not in Bounds: log.warn("'%s' isn't a valid graph bounds, options are: %s" % (value, ', '.join(Bounds))) return CONFIG['graph_bound'] # keep the default CONFIG = conf.config_dict('nyx', { 'attr.hibernate_color': {}, 'attr.graph.title': {}, 'attr.graph.header.primary': {}, 'attr.graph.header.secondary': {}, 'graph_bound': Bounds.LOCAL_MAX, 'graph_height': 7, 'graph_interval': Interval.EACH_SECOND, 'graph_stat': GraphStat.BANDWIDTH, 'max_graph_width': 300, # we need some sort of max size so we know how much graph data to retain 'show_accounting': True, 'show_bits': False, 'show_connections': True, }, conf_handler) def _bandwidth_title_stats(): controller = tor_controller() stats = [] bw_rate = controller.get_effective_rate(None) bw_burst = controller.get_effective_rate(None, burst = True)
'black': curses.COLOR_BLACK, 'white': curses.COLOR_WHITE, } DEFAULT_COLOR_ATTR = dict([(color, 0) for color in COLOR_LIST]) COLOR_ATTR = None def conf_handler(key, value): if key == 'features.color_override': if value not in COLOR_LIST.keys() and value != 'none': raise ValueError(msg('usage.unable_to_set_color_override', color = value)) CONFIG = conf.config_dict('nyx', { 'features.color_override': 'none', 'features.colorInterface': True, }, conf_handler) def is_color_supported(): """ Checks if curses currently supports rendering colors. :returns: **True** if colors can be rendered, **False** otherwise """ return _color_attr() != DEFAULT_COLOR_ATTR def get_color(color): """
from nyx.curses import GREEN, YELLOW, WHITE, NORMAL, BOLD, HIGHLIGHT from stem.util import conf, log def conf_handler(key, value): if key == 'features.log.prepopulateReadLimit': return max(0, value) elif key == 'cache.log_panel.size': return max(1000, value) CONFIG = conf.config_dict('nyx', { 'attr.log_color': {}, 'cache.log_panel.size': 1000, 'features.logFile': '', 'features.log.showDuplicateEntries': False, 'features.log.prepopulate': True, 'features.log.prepopulateReadLimit': 5000, 'features.log.regex': [], 'startup.events': 'NOTICE,WARN,ERR,NYX_NOTICE,NYX_WARNING,NYX_ERROR', }, conf_handler) UPDATE_RATE = 0.3 # The height of the drawn content is estimated based on the last time we redrew # the panel. It's chiefly used for scrolling and the bar indicating its # position. Letting the estimate be too inaccurate results in a display bug, so # redraws the display if it's off by this threshold. CONTENT_HEIGHT_REDRAW_THRESHOLD = 3 # Log buffer so we start collecting stem/nyx events when imported. This is used
from stem.util import conf, log def conf_handler(key, value): if key == 'prepopulate_read_limit': return max(0, value) elif key == 'max_log_size': return max(1000, value) CONFIG = conf.config_dict( 'nyx', { 'attr.log_color': {}, 'deduplicate_log': True, 'logged_events': 'NOTICE,WARN,ERR,NYX_NOTICE,NYX_WARNING,NYX_ERROR', 'logging_filter': [], 'max_log_size': 1000, 'prepopulate_log': True, 'prepopulate_read_limit': 5000, 'write_logs_to': '', }, conf_handler) # Users may understanably mix up 'WARN/WARNING' and 'ERR/ERROR' in their --log # argument or config, so fixing those. EVENT_ALIASES = { 'WARNING': 'WARN', 'ERROR': 'ERR', 'NYX_WARN': 'NYX_WARNING', 'NYX_ERR': 'NYX_ERROR', }
from nyx.curses import GREEN, YELLOW, WHITE, NORMAL, BOLD, HIGHLIGHT from stem.util import conf, log def conf_handler(key, value): if key == 'features.log.prepopulateReadLimit': return max(0, value) elif key == 'cache.log_panel.size': return max(1000, value) CONFIG = conf.config_dict('nyx', { 'attr.log_color': {}, 'cache.log_panel.size': 1000, 'features.logFile': '', 'features.log.showDuplicateEntries': False, 'features.log.prepopulate': True, 'features.log.prepopulateReadLimit': 5000, 'features.log.regex': [], 'startup.events': 'N3', }, conf_handler) UPDATE_RATE = 0.3 # The height of the drawn content is estimated based on the last time we redrew # the panel. It's chiefly used for scrolling and the bar indicating its # position. Letting the estimate be too inaccurate results in a display bug, so # redraws the display if it's off by this threshold. CONTENT_HEIGHT_REDRAW_THRESHOLD = 3 # Log buffer so we start collecting stem/nyx events when imported. This is used
def conf_handler(key, value): if key == "features.config.selectionDetails.height": return max(0, value) elif key == "features.config.state.colWidth.option": return max(5, value) elif key == "features.config.state.colWidth.value": return max(5, value) elif key == "features.config.order": return conf.parse_enum_csv(key, value[0], Field, 3) CONFIG = conf.config_dict("arm", { "features.config.order": [Field.MAN_ENTRY, Field.OPTION, Field.IS_DEFAULT], "features.config.selectionDetails.height": 6, "features.config.prepopulateEditValues": True, "features.config.state.showPrivateOptions": False, "features.config.state.showVirtualOptions": False, "features.config.state.colWidth.option": 25, "features.config.state.colWidth.value": 15, }, conf_handler) def getFieldFromLabel(fieldLabel): """ Converts field labels back to their enumeration, raising a ValueError if it doesn't exist. """ for entryEnum in FIELD_ATTR: if fieldLabel == FIELD_ATTR[entryEnum][0]: return entryEnum
} VERSION_STATUS_COLORS = { "new": "blue", "new in series": "blue", "obsolete": "red", "recommended": "green", "old": "red", "unrecommended": "red", "unknown": "cyan" } CONFIG = conf.config_dict( "arm", { "startup.interface.ipAddress": "127.0.0.1", "startup.interface.port": 9051, "startup.interface.socket": "/var/run/tor/control", "features.showFdUsage": False, }) 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, socketPath, exitPolicy, isAuthPassword (bool), isAuthCookie (bool), orListenAddr, *address, *fingerprint, *flags, pid, startTime, *fdUsed, fdLimit, isFdLimitEstimate sys/ hostname, os, version stat/ *%torCpu, *%armCpu, *rss, *%mem
def conf_handler(key, value): if key == 'features.config.selectionDetails.height': return max(0, value) elif key == 'features.config.state.colWidth.option': return max(5, value) elif key == 'features.config.state.colWidth.value': return max(5, value) elif key == 'features.config.order': return conf.parse_enum_csv(key, value[0], Field, 3) CONFIG = conf.config_dict('nyx', { 'features.config.order': [Field.MAN_ENTRY, Field.OPTION, Field.IS_DEFAULT], 'features.config.selectionDetails.height': 6, 'features.config.prepopulateEditValues': True, 'features.config.state.showPrivateOptions': False, 'features.config.state.showVirtualOptions': False, 'features.config.state.colWidth.option': 25, 'features.config.state.colWidth.value': 15, }, conf_handler) def get_field_from_label(field_label): """ Converts field labels back to their enumeration, raising a ValueError if it doesn't exist. """ for entry_enum in FIELD_ATTR: if field_label == FIELD_ATTR[entry_enum][0]: return entry_enum