def __init__(self): self._my_router_status_entry = None self._my_router_status_entry_time = 0 # Stem's get_network_statuses() is slow, and overkill for what we need # here. Just parsing the raw GETINFO response to cut startup time down. # # Only fetching this if our cache is at least an hour old (and hence a new # consensus available). cache_age = time.time() - nyx.cache().relays_updated_at() controller = tor_controller() if cache_age < 3600: stem.util.log.info('Cache is only %s old, no need to refresh it.' % str_tools.time_label(cache_age, is_long=True)) else: stem.util.log.info( 'Cache is %s old, refreshing relay information.' % str_tools.time_label(cache_age, is_long=True)) ns_response = controller.get_info('ns/all', None) if ns_response: self._update(ns_response) controller.add_event_listener( lambda event: self._update(event.consensus_content), stem.control.EventType.NEWCONSENSUS)
def __init__(self, clone = None): GraphCategory.__init__(self, clone) self._title_last_updated = None if not clone: # fill in past bandwidth information controller = tor_controller() bw_entries, is_successful = controller.get_info('bw-event-cache', None), True if bw_entries: for entry in bw_entries.split(): entry_comp = entry.split(',') if len(entry_comp) != 2 or not entry_comp[0].isdigit() or not entry_comp[1].isdigit(): log.warn("Tor's 'GETINFO bw-event-cache' provided malformed output: %s" % bw_entries) is_successful = False break self.primary.update(int(entry_comp[0])) self.secondary.update(int(entry_comp[1])) if is_successful: log.info('Bandwidth graph has information for the last %s' % str_tools.time_label(len(bw_entries.split()), is_long = True)) read_total = controller.get_info('traffic/read', None) write_total = controller.get_info('traffic/written', None) start_time = system.start_time(controller.get_pid(None)) if read_total and write_total and start_time: self.primary.total = int(read_total) self.secondary.total = int(write_total) self.start_time = start_time
def _x_axis_labels(interval, columns): """ Provides the labels for the x-axis. We include the units for only its first value, then bump the precision for subsequent units. For example... 10s, 20, 30, 40, 50, 1m, 1.1, 1.3, 1.5 """ x_axis_labels = {} interval_sec = INTERVAL_SECONDS[interval] interval_spacing = 10 if columns >= WIDE_LABELING_GRAPH_COL else 5 previous_units, decimal_precision = None, 0 for i in range((columns - 4) // interval_spacing): x = (i + 1) * interval_spacing time_label = str_tools.time_label(x * interval_sec, decimal_precision) if not previous_units: previous_units = time_label[-1] elif previous_units != time_label[-1]: previous_units = time_label[-1] decimal_precision = 1 # raised precision for future measurements else: time_label = time_label[:-1] # strip units since already provided x_axis_labels[x] = time_label return x_axis_labels
def _x_axis_labels(interval, columns): """ Provides the labels for the x-axis. We include the units for only its first value, then bump the precision for subsequent units. For example... 10s, 20, 30, 40, 50, 1m, 1.1, 1.3, 1.5 """ x_axis_labels = {} interval_sec = INTERVAL_SECONDS[interval] interval_spacing = 10 if columns >= WIDE_LABELING_GRAPH_COL else 5 previous_units, decimal_precision = None, 0 for i in range((columns - 4) / interval_spacing): x = (i + 1) * interval_spacing time_label = str_tools.time_label(x * interval_sec, decimal_precision) if not previous_units: previous_units = time_label[-1] elif previous_units != time_label[-1]: previous_units = time_label[-1] decimal_precision = 1 # raised precision for future measurements else: time_label = time_label[:-1] # strip units since already provided x_axis_labels[x] = time_label return x_axis_labels
def _get_x_axis_labels(self, attr, subgraph_columns): """ Provides the labels for the x-axis. We include the units for only its first value, then bump the precision for subsequent units. For example... 10s, 20, 30, 40, 50, 1m, 1.1, 1.3, 1.5 """ x_axis_labels = {} interval_sec = INTERVAL_SECONDS[attr.interval] interval_spacing = 10 if subgraph_columns >= WIDE_LABELING_GRAPH_COL else 5 units_label, decimal_precision = None, 0 for i in range((subgraph_columns - 4) / interval_spacing): x = (i + 1) * interval_spacing time_label = str_tools.time_label(x * interval_sec, decimal_precision) if not units_label: units_label = time_label[-1] elif units_label != time_label[-1]: # upped scale so also up precision of future measurements units_label = time_label[-1] decimal_precision += 1 else: # if constrained on space then strips labeling since already provided time_label = time_label[:-1] x_axis_labels[x] = time_label return x_axis_labels
def __init__(self, clone = None): GraphCategory.__init__(self, clone) if not clone: # fill in past bandwidth information controller = tor_controller() bw_entries, is_successful = controller.get_info('bw-event-cache', None), True if bw_entries: for entry in bw_entries.split(): entry_comp = entry.split(',') if len(entry_comp) != 2 or not entry_comp[0].isdigit() or not entry_comp[1].isdigit(): log.warn(msg('panel.graphing.bw_event_cache_malformed', response = bw_entries)) is_successful = False break self.primary.update(int(entry_comp[0])) self.secondary.update(int(entry_comp[1])) if is_successful: log.info(msg('panel.graphing.prepopulation_successful', duration = str_tools.time_label(len(bw_entries.split()), is_long = True))) read_total = controller.get_info('traffic/read', None) write_total = controller.get_info('traffic/written', None) start_time = system.start_time(controller.get_pid(None)) if read_total and write_total and start_time: self.primary.total = int(read_total) self.secondary.total = int(write_total) self.start_time = start_time
def test_time_label(self): """ Checks the time_label() function. """ # test the pydoc examples self.assertEqual('2h', str_tools.time_label(10000)) self.assertEqual('1.0 minute', str_tools.time_label(61, 1, True)) self.assertEqual('1.01 minutes', str_tools.time_label(61, 2, True)) self.assertEqual('0s', str_tools.time_label(0)) self.assertEqual('0s', str_tools.time_label(0.1)) self.assertEqual('0 seconds', str_tools.time_label(0, is_long = True)) self.assertEqual('0.00s', str_tools.time_label(0, 2)) self.assertEqual('0.10s', str_tools.time_label(0.1, 2)) self.assertEqual('-10s', str_tools.time_label(-10)) self.assertRaises(TypeError, str_tools.time_label, None) self.assertRaises(TypeError, str_tools.time_label, 'hello world')
def test_time_label(self): """ Checks the time_label() function. """ # test the pydoc examples self.assertEqual('2h', str_tools.time_label(10000)) self.assertEqual('1.0 minute', str_tools.time_label(61, 1, True)) self.assertEqual('1.01 minutes', str_tools.time_label(61, 2, True)) self.assertEqual('0s', str_tools.time_label(0)) self.assertEqual('0 seconds', str_tools.time_label(0, is_long = True)) self.assertEqual('0.00s', str_tools.time_label(0, 2)) self.assertEqual('-10s', str_tools.time_label(-10)) self.assertRaises(TypeError, str_tools.time_label, None) self.assertRaises(TypeError, str_tools.time_label, 'hello world')
def value(self): """ Provides the value of this configuration option. :returns: **str** representation of the current config value """ values = tor_controller().get_conf(self.name, [], True) if not values: return '<none>' elif self.value_type == 'Boolean' and values[0] in ('0', '1'): return 'False' if values[0] == '0' else 'True' elif self.value_type == 'DataSize' and values[0].isdigit(): return str_tools.size_label(int(values[0])) elif self.value_type == 'TimeInterval' and values[0].isdigit(): return str_tools.time_label(int(values[0]), is_long=True) else: return ', '.join(values)
def _draw_right_column(subwindow, x, y, line, current_time, attr): if line.line_type == LineType.CIRCUIT: circ_path = [fp for fp, _ in line.circuit.path] circ_index = circ_path.index(line.fingerprint) if circ_index == len(circ_path) - 1: placement_type = 'Exit' if line.circuit.status == 'BUILT' else 'Extending' elif circ_index == 0: placement_type = 'Guard' else: placement_type = 'Middle' subwindow.addstr(x + 4, y, '%i / %s' % (circ_index + 1, placement_type), *attr) else: x = subwindow.addstr(x, y, '+' if line.connection.is_legacy else ' ', *attr) x = subwindow.addstr(x, y, '%5s' % str_tools.time_label(current_time - line.connection.start_time, 1), *attr) x = subwindow.addstr(x, y, ' (', *attr) x = subwindow.addstr(x, y, line.entry.get_type().upper(), BOLD, *attr) x = subwindow.addstr(x, y, ')', *attr)
def value(self): """ Provides the value of this configuration option. :returns: **str** representation of the current config value """ values = tor_controller().get_conf(self.name, [], True) if not values: return '<none>' elif self.value_type == 'Boolean' and values[0] in ('0', '1'): return 'False' if values[0] == '0' else 'True' elif self.value_type == 'DataSize' and values[0].isdigit(): return str_tools.size_label(int(values[0])) elif self.value_type == 'TimeInterval' and values[0].isdigit(): return str_tools.time_label(int(values[0]), is_long = True) else: return ', '.join(values)
def _draw_right_column(subwindow, x, y, line, current_time, attr): if line.line_type == LineType.CIRCUIT: circ_path = [fp for fp, _ in line.circuit.path] circ_index = circ_path.index(line.fingerprint) if circ_index == len(circ_path) - 1: placement_type = 'End' if line.circuit.status == 'BUILT' else 'Extending' elif circ_index == 0: placement_type = 'Guard' else: placement_type = 'Middle' subwindow.addstr(x + 4, y, '%i / %s' % (circ_index + 1, placement_type), *attr) else: x = subwindow.addstr(x, y, '+' if line.connection.is_legacy else ' ', *attr) x = subwindow.addstr(x, y, '%5s' % str_tools.time_label(current_time - line.connection.start_time, 1), *attr) x = subwindow.addstr(x, y, ' (', *attr) x = subwindow.addstr(x, y, line.entry.get_type().upper(), BOLD, *attr) x = subwindow.addstr(x, y, ')', *attr)
def _get_value(self): """ Provides the current value of the configuration entry, taking advantage of the tor_tools caching to effectively query the accurate value. This uses the value's type to provide a user friendly representation if able. """ conf_value = ', '.join(tor_controller().get_conf(self.get(Field.OPTION), [], True)) # provides nicer values for recognized types if not conf_value: conf_value = '<none>' elif self.get(Field.TYPE) == 'Boolean' and conf_value in ('0', '1'): conf_value = 'False' if conf_value == '0' else 'True' elif self.get(Field.TYPE) == 'DataSize' and conf_value.isdigit(): conf_value = str_tools.size_label(int(conf_value)) elif self.get(Field.TYPE) == 'TimeInterval' and conf_value.isdigit(): conf_value = str_tools.time_label(int(conf_value), is_long = True) return conf_value
def __init__(self, clone=None): GraphCategory.__init__(self, clone) if not clone: # fill in past bandwidth information controller = tor_controller() bw_entries, is_successful = controller.get_info( 'bw-event-cache', None), True if bw_entries: for entry in bw_entries.split(): entry_comp = entry.split(',') if len(entry_comp) != 2 or not entry_comp[0].isdigit( ) or not entry_comp[1].isdigit(): log.warn( msg('panel.graphing.bw_event_cache_malformed', response=bw_entries)) is_successful = False break self.primary.update(int(entry_comp[0])) self.secondary.update(int(entry_comp[1])) if is_successful: log.info( msg('panel.graphing.prepopulation_successful', duration=str_tools.time_label(len( bw_entries.split()), is_long=True))) read_total = controller.get_info('traffic/read', None) write_total = controller.get_info('traffic/written', None) start_time = system.start_time(controller.get_pid(None)) if read_total and write_total and start_time: self.primary.total = int(read_total) self.secondary.total = int(write_total) self.start_time = start_time
def _Config_DropDown(self): for line in self.controller.get_info('config/names').splitlines(): line_comp = line.split() name, value_type = line_comp[0], line_comp[1] values = self.controller.get_conf(name, [], True) if not values: Config = '<none>' elif value_type == 'Boolean' and values[0] in ('0', '1'): if values[0] == '0': Config = 'False' else: Config = 'True' elif value_type == 'DataSize' and values[0].isdigit(): Config = str_tools.size_label(int(values[0])) elif value_type == 'TimeInterval' and values[0].isdigit(): Config = str_tools.time_label(int(values[0]), is_long=True) else: Config = values[0] Value_Type.update({name: value_type}) Configurations.update({name: Config}) self.ui.Config_Options.addItem(name) self._Config_CurrentVal()
def validate(contents = None): """ Performs validation on the given torrc contents, providing back a listing of (line number, issue, msg) tuples for issues found. If the issue occures on a multiline torrc entry then the line number is for the last line of the entry. Arguments: contents - torrc contents """ controller = tor_controller() custom_options = get_custom_options() issues_found, seen_options = [], [] # Strips comments and collapses multiline multi-line entries, for more # information see: # https://trac.torproject.org/projects/tor/ticket/1929 stripped_contents, multiline_buffer = [], '' for line in _strip_comments(contents): if not line: stripped_contents.append('') else: line = multiline_buffer + line multiline_buffer = '' if line.endswith('\\'): multiline_buffer = line[:-1] stripped_contents.append('') else: stripped_contents.append(line.strip()) for line_number in range(len(stripped_contents) - 1, -1, -1): line_text = stripped_contents[line_number] if not line_text: continue line_comp = line_text.split(None, 1) if len(line_comp) == 2: option, value = line_comp else: option, value = line_text, '' # Tor is case insensetive when parsing its torrc. This poses a bit of an # issue for us because we want all of our checks to be case insensetive # too but also want messages to match the normal camel-case conventions. # # Using the custom_options to account for this. It contains the tor reported # options (camel case) and is either a matching set or the following defaut # value check will fail. Hence using that hash to correct the case. # # TODO: when refactoring for stem make this less confusing... for custom_opt in custom_options: if custom_opt.lower() == option.lower(): option = custom_opt break # if an aliased option then use its real name if option in CONFIG['torrc.alias']: option = CONFIG['torrc.alias'][option] # most parameters are overwritten if defined multiple times if option in seen_options and option not in get_multiline_parameters(): issues_found.append((line_number, ValidationError.DUPLICATE, option)) continue else: seen_options.append(option) # checks if the value isn't necessary due to matching the defaults if option not in custom_options: issues_found.append((line_number, ValidationError.IS_DEFAULT, option)) # replace aliases with their recognized representation if option in CONFIG['torrc.alias']: option = CONFIG['torrc.alias'][option] # tor appears to replace tabs with a space, for instance: # "accept\t*:563" is read back as "accept *:563" value = value.replace('\t', ' ') # parse value if it's a size or time, expanding the units value, value_type = _parse_conf_value(value) # issues GETCONF to get the values tor's currently configured to use tor_values = controller.get_conf(option, [], True) # multiline entries can be comma separated values (for both tor and conf) value_list = [value] if option in get_multiline_parameters(): value_list = [val.strip() for val in value.split(',')] fetched_values, tor_values = tor_values, [] for fetched_value in fetched_values: for fetched_entry in fetched_value.split(','): fetched_entry = fetched_entry.strip() if fetched_entry not in tor_values: tor_values.append(fetched_entry) for val in value_list: # checks if both the argument and tor's value are empty is_blank_match = not val and not tor_values if not is_blank_match and val not in tor_values: # converts corrections to reader friedly size values display_values = tor_values if value_type == ValueType.SIZE: display_values = [str_tools.size_label(int(val)) for val in tor_values] elif value_type == ValueType.TIME: display_values = [str_tools.time_label(int(val)) for val in tor_values] issues_found.append((line_number, ValidationError.MISMATCH, ', '.join(display_values))) # checks if any custom options are missing from the torrc for option in custom_options: # In new versions the 'DirReqStatistics' option is true by default and # disabled on startup if geoip lookups are unavailable. If this option is # missing then that's most likely the reason. # # https://trac.torproject.org/projects/tor/ticket/4237 if option == 'DirReqStatistics': continue if option not in seen_options: issues_found.append((None, ValidationError.MISSING, option)) return issues_found