tor-commits
Threads by month
- ----- 2025 -----
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
September 2015
- 15 participants
- 1411 discussions
commit d06cec4899bf0987c412afd7e8f55ad8045939d8
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Jul 4 13:10:52 2015 -0700
Consistently use 'with' for locking
As we've been rewriting components I've swapping locking to use the 'with'
keyword (much safer, avoiding deadlocks in case of exceptions). Swapping the
rest of the codebase over so we're consistent.
---
nyx/config_panel.py | 115 ++++++------
nyx/connections/conn_panel.py | 304 +++++++++++++++---------------
nyx/torrc_panel.py | 268 +++++++++++++--------------
nyx/util/tor_config.py | 408 +++++++++++++++++++----------------------
4 files changed, 524 insertions(+), 571 deletions(-)
diff --git a/nyx/config_panel.py b/nyx/config_panel.py
index 05a65e0..58f229b 100644
--- a/nyx/config_panel.py
+++ b/nyx/config_panel.py
@@ -332,14 +332,12 @@ class ConfigPanel(panel.Panel):
set ordering
"""
- self.vals_lock.acquire()
-
- if ordering:
- CONFIG['features.config.order'] = ordering
+ with self.vals_lock:
+ if ordering:
+ CONFIG['features.config.order'] = ordering
- self.conf_contents.sort(key=lambda i: (i.get_all(CONFIG['features.config.order'])))
- self.conf_important_contents.sort(key=lambda i: (i.get_all(CONFIG['features.config.order'])))
- self.vals_lock.release()
+ self.conf_contents.sort(key=lambda i: (i.get_all(CONFIG['features.config.order'])))
+ self.conf_important_contents.sort(key=lambda i: (i.get_all(CONFIG['features.config.order'])))
def show_sort_dialog(self):
"""
@@ -573,78 +571,75 @@ class ConfigPanel(panel.Panel):
]
def draw(self, width, height):
- self.vals_lock.acquire()
-
- # panel with details for the current selection
-
- detail_panel_height = CONFIG['features.config.selectionDetails.height']
- is_scrollbar_visible = False
+ with self.vals_lock:
+ # panel with details for the current selection
- if detail_panel_height == 0 or detail_panel_height + 2 >= height:
- # no detail panel
+ detail_panel_height = CONFIG['features.config.selectionDetails.height']
+ is_scrollbar_visible = False
- detail_panel_height = 0
- scroll_location = self.scroller.get_scroll_location(self._get_config_options(), height - 1)
- cursor_selection = self.get_selection()
- is_scrollbar_visible = len(self._get_config_options()) > height - 1
- else:
- # Shrink detail panel if there isn't sufficient room for the whole
- # thing. The extra line is for the bottom border.
+ if detail_panel_height == 0 or detail_panel_height + 2 >= height:
+ # no detail panel
- detail_panel_height = min(height - 1, detail_panel_height + 1)
- scroll_location = self.scroller.get_scroll_location(self._get_config_options(), height - 1 - detail_panel_height)
- cursor_selection = self.get_selection()
- is_scrollbar_visible = len(self._get_config_options()) > height - detail_panel_height - 1
+ detail_panel_height = 0
+ scroll_location = self.scroller.get_scroll_location(self._get_config_options(), height - 1)
+ cursor_selection = self.get_selection()
+ is_scrollbar_visible = len(self._get_config_options()) > height - 1
+ else:
+ # Shrink detail panel if there isn't sufficient room for the whole
+ # thing. The extra line is for the bottom border.
- if cursor_selection is not None:
- self._draw_selection_panel(cursor_selection, width, detail_panel_height, is_scrollbar_visible)
+ detail_panel_height = min(height - 1, detail_panel_height + 1)
+ scroll_location = self.scroller.get_scroll_location(self._get_config_options(), height - 1 - detail_panel_height)
+ cursor_selection = self.get_selection()
+ is_scrollbar_visible = len(self._get_config_options()) > height - detail_panel_height - 1
- # draws the top label
+ if cursor_selection is not None:
+ self._draw_selection_panel(cursor_selection, width, detail_panel_height, is_scrollbar_visible)
- if self.is_title_visible():
- config_type = 'Tor' if self.config_type == State.TOR else 'Arm'
- hidden_msg = "press 'a' to hide most options" if self.show_all else "press 'a' to show all options"
- title_label = '%s Configuration (%s):' % (config_type, hidden_msg)
- self.addstr(0, 0, title_label, curses.A_STANDOUT)
+ # draws the top label
- # draws left-hand scroll bar if content's longer than the height
+ if self.is_title_visible():
+ config_type = 'Tor' if self.config_type == State.TOR else 'Arm'
+ hidden_msg = "press 'a' to hide most options" if self.show_all else "press 'a' to show all options"
+ title_label = '%s Configuration (%s):' % (config_type, hidden_msg)
+ self.addstr(0, 0, title_label, curses.A_STANDOUT)
- scroll_offset = 1
+ # draws left-hand scroll bar if content's longer than the height
- if is_scrollbar_visible:
- scroll_offset = 3
- self.add_scroll_bar(scroll_location, scroll_location + height - detail_panel_height - 1, len(self._get_config_options()), 1 + detail_panel_height)
+ scroll_offset = 1
- option_width = CONFIG['features.config.state.colWidth.option']
- value_width = CONFIG['features.config.state.colWidth.value']
- description_width = max(0, width - scroll_offset - option_width - value_width - 2)
+ if is_scrollbar_visible:
+ scroll_offset = 3
+ self.add_scroll_bar(scroll_location, scroll_location + height - detail_panel_height - 1, len(self._get_config_options()), 1 + detail_panel_height)
- # if the description column is overly long then use its space for the
- # value instead
+ option_width = CONFIG['features.config.state.colWidth.option']
+ value_width = CONFIG['features.config.state.colWidth.value']
+ description_width = max(0, width - scroll_offset - option_width - value_width - 2)
- if description_width > 80:
- value_width += description_width - 80
- description_width = 80
+ # if the description column is overly long then use its space for the
+ # value instead
- for line_number in range(scroll_location, len(self._get_config_options())):
- entry = self._get_config_options()[line_number]
- draw_line = line_number + detail_panel_height + 1 - scroll_location
+ if description_width > 80:
+ value_width += description_width - 80
+ description_width = 80
- line_format = [curses.A_NORMAL if entry.get(Field.IS_DEFAULT) else curses.A_BOLD]
+ for line_number in range(scroll_location, len(self._get_config_options())):
+ entry = self._get_config_options()[line_number]
+ draw_line = line_number + detail_panel_height + 1 - scroll_location
- if entry.get(Field.CATEGORY):
- line_format += [CATEGORY_COLOR[entry.get(Field.CATEGORY)]]
+ line_format = [curses.A_NORMAL if entry.get(Field.IS_DEFAULT) else curses.A_BOLD]
- if entry == cursor_selection:
- line_format += [curses.A_STANDOUT]
+ if entry.get(Field.CATEGORY):
+ line_format += [CATEGORY_COLOR[entry.get(Field.CATEGORY)]]
- line_text = entry.get_label(option_width, value_width, description_width)
- self.addstr(draw_line, scroll_offset, line_text, *line_format)
+ if entry == cursor_selection:
+ line_format += [curses.A_STANDOUT]
- if draw_line >= height:
- break
+ line_text = entry.get_label(option_width, value_width, description_width)
+ self.addstr(draw_line, scroll_offset, line_text, *line_format)
- self.vals_lock.release()
+ if draw_line >= height:
+ break
def _get_config_options(self):
return self.conf_contents if self.show_all else self.conf_important_contents
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 0a27721..1402b71 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -148,10 +148,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
time we were last paused.
"""
- if self._halt_time:
- return self._halt_time
- else:
- return panel.Panel.get_pause_time(self)
+ return self._halt_time if self._halt_time else panel.Panel.get_pause_time(self)
def set_sort_order(self, ordering = None):
"""
@@ -162,22 +159,19 @@ class ConnectionPanel(panel.Panel, threading.Thread):
set ordering
"""
- self.vals_lock.acquire()
-
- if ordering:
- nyx_config = conf.get_config('nyx')
-
- ordering_keys = [entries.SortAttr.keys()[entries.SortAttr.index_of(v)] for v in ordering]
- nyx_config.set('features.connection.order', ', '.join(ordering_keys))
+ with self.vals_lock:
+ if ordering:
+ nyx_config = conf.get_config('nyx')
- self._entries.sort(key = lambda i: (i.get_sort_values(CONFIG['features.connection.order'], self.get_listing_type())))
+ ordering_keys = [entries.SortAttr.keys()[entries.SortAttr.index_of(v)] for v in ordering]
+ nyx_config.set('features.connection.order', ', '.join(ordering_keys))
- self._entry_lines = []
+ self._entries.sort(key = lambda i: (i.get_sort_values(CONFIG['features.connection.order'], self.get_listing_type())))
- for entry in self._entries:
- self._entry_lines += entry.getLines()
+ self._entry_lines = []
- self.vals_lock.release()
+ for entry in self._entries:
+ self._entry_lines += entry.getLines()
def get_listing_type(self):
"""
@@ -197,17 +191,14 @@ class ConnectionPanel(panel.Panel, threading.Thread):
if self.get_listing_type() == listing_type:
return
- self.vals_lock.acquire()
-
- nyx_config = conf.get_config('nyx')
- nyx_config.set('features.connection.listing_type', Listing.keys()[Listing.index_of(listing_type)])
-
- # if we're sorting by the listing then we need to resort
+ with self.vals_lock:
+ nyx_config = conf.get_config('nyx')
+ nyx_config.set('features.connection.listing_type', Listing.keys()[Listing.index_of(listing_type)])
- if entries.SortAttr.LISTING in CONFIG['features.connection.order']:
- self.set_sort_order()
+ # if we're sorting by the listing then we need to resort
- self.vals_lock.release()
+ if entries.SortAttr.LISTING in CONFIG['features.connection.order']:
+ self.set_sort_order()
def is_clients_allowed(self):
"""
@@ -423,86 +414,83 @@ class ConnectionPanel(panel.Panel, threading.Thread):
return self._scroller.get_cursor_selection(self._entry_lines)
def draw(self, width, height):
- self.vals_lock.acquire()
-
- # if we don't have any contents then refuse to show details
-
- if not self._entries:
- self._show_details = False
+ with self.vals_lock:
+ # if we don't have any contents then refuse to show details
- # extra line when showing the detail panel is for the bottom border
+ if not self._entries:
+ self._show_details = False
- detail_panel_offset = DETAILS_HEIGHT + 1 if self._show_details else 0
- is_scrollbar_visible = len(self._entry_lines) > height - detail_panel_offset - 1
+ # extra line when showing the detail panel is for the bottom border
- scroll_location = self._scroller.get_scroll_location(self._entry_lines, height - detail_panel_offset - 1)
- cursor_selection = self.get_selection()
+ detail_panel_offset = DETAILS_HEIGHT + 1 if self._show_details else 0
+ is_scrollbar_visible = len(self._entry_lines) > height - detail_panel_offset - 1
- # draws the detail panel if currently displaying it
+ scroll_location = self._scroller.get_scroll_location(self._entry_lines, height - detail_panel_offset - 1)
+ cursor_selection = self.get_selection()
- if self._show_details and cursor_selection:
- # This is a solid border unless the scrollbar is visible, in which case a
- # 'T' pipe connects the border to the bar.
+ # draws the detail panel if currently displaying it
- ui_tools.draw_box(self, 0, 0, width, DETAILS_HEIGHT + 2)
+ if self._show_details and cursor_selection:
+ # This is a solid border unless the scrollbar is visible, in which case a
+ # 'T' pipe connects the border to the bar.
- if is_scrollbar_visible:
- self.addch(DETAILS_HEIGHT + 1, 1, curses.ACS_TTEE)
+ ui_tools.draw_box(self, 0, 0, width, DETAILS_HEIGHT + 2)
- draw_entries = cursor_selection.get_details(width)
+ if is_scrollbar_visible:
+ self.addch(DETAILS_HEIGHT + 1, 1, curses.ACS_TTEE)
- for i in range(min(len(draw_entries), DETAILS_HEIGHT)):
- self.addstr(1 + i, 2, draw_entries[i][0], *draw_entries[i][1])
+ draw_entries = cursor_selection.get_details(width)
- # title label with connection counts
+ for i in range(min(len(draw_entries), DETAILS_HEIGHT)):
+ self.addstr(1 + i, 2, draw_entries[i][0], *draw_entries[i][1])
- if self.is_title_visible():
- title = 'Connection Details:' if self._show_details else self._title
- self.addstr(0, 0, title, curses.A_STANDOUT)
+ # title label with connection counts
- scroll_offset = 0
+ if self.is_title_visible():
+ title = 'Connection Details:' if self._show_details else self._title
+ self.addstr(0, 0, title, curses.A_STANDOUT)
- if is_scrollbar_visible:
- scroll_offset = 2
- self.add_scroll_bar(scroll_location, scroll_location + height - detail_panel_offset - 1, len(self._entry_lines), 1 + detail_panel_offset)
+ scroll_offset = 0
- if self.is_paused() or not self._is_tor_running:
- current_time = self.get_pause_time()
- else:
- current_time = time.time()
+ if is_scrollbar_visible:
+ scroll_offset = 2
+ self.add_scroll_bar(scroll_location, scroll_location + height - detail_panel_offset - 1, len(self._entry_lines), 1 + detail_panel_offset)
- for line_number in range(scroll_location, len(self._entry_lines)):
- entry_line = self._entry_lines[line_number]
+ if self.is_paused() or not self._is_tor_running:
+ current_time = self.get_pause_time()
+ else:
+ current_time = time.time()
- # if this is an unresolved SOCKS, HIDDEN, or CONTROL entry then queue up
- # resolution for the applicaitions they belong to
+ for line_number in range(scroll_location, len(self._entry_lines)):
+ entry_line = self._entry_lines[line_number]
- if isinstance(entry_line, conn_entry.ConnectionLine) and entry_line.is_unresolved_application():
- self._resolve_apps()
+ # if this is an unresolved SOCKS, HIDDEN, or CONTROL entry then queue up
+ # resolution for the applicaitions they belong to
- # hilighting if this is the selected line
+ if isinstance(entry_line, conn_entry.ConnectionLine) and entry_line.is_unresolved_application():
+ self._resolve_apps()
- extra_format = curses.A_STANDOUT if entry_line == cursor_selection else curses.A_NORMAL
+ # hilighting if this is the selected line
- draw_line = line_number + detail_panel_offset + 1 - scroll_location
+ extra_format = curses.A_STANDOUT if entry_line == cursor_selection else curses.A_NORMAL
- prefix = entry_line.get_listing_prefix()
+ draw_line = line_number + detail_panel_offset + 1 - scroll_location
- for i in range(len(prefix)):
- self.addch(draw_line, scroll_offset + i, prefix[i])
+ prefix = entry_line.get_listing_prefix()
- x_offset = scroll_offset + len(prefix)
- draw_entry = entry_line.get_listing_entry(width - scroll_offset - len(prefix), current_time, self.get_listing_type())
+ for i in range(len(prefix)):
+ self.addch(draw_line, scroll_offset + i, prefix[i])
- for msg, attr in draw_entry:
- attr |= extra_format
- self.addstr(draw_line, x_offset, msg, attr)
- x_offset += len(msg)
+ x_offset = scroll_offset + len(prefix)
+ draw_entry = entry_line.get_listing_entry(width - scroll_offset - len(prefix), current_time, self.get_listing_type())
- if draw_line >= height:
- break
+ for msg, attr in draw_entry:
+ attr |= extra_format
+ self.addstr(draw_line, x_offset, msg, attr)
+ x_offset += len(msg)
- self.vals_lock.release()
+ if draw_line >= height:
+ break
def stop(self):
"""
@@ -528,114 +516,112 @@ class ConnectionPanel(panel.Panel, threading.Thread):
conn_resolver = nyx.util.tracker.get_connection_tracker()
current_resolution_count = conn_resolver.run_counter()
- self.vals_lock.acquire()
-
- new_entries = [] # the new results we'll display
+ with self.vals_lock:
+ new_entries = [] # the new results we'll display
- # Fetches new connections and client circuits...
- # new_connections [(local ip, local port, foreign ip, foreign port)...]
- # new_circuits {circuit_id => (status, purpose, path)...}
+ # Fetches new connections and client circuits...
+ # new_connections [(local ip, local port, foreign ip, foreign port)...]
+ # new_circuits {circuit_id => (status, purpose, path)...}
- new_connections = [(conn.local_address, conn.local_port, conn.remote_address, conn.remote_port) for conn in conn_resolver.get_value()]
- new_circuits = {}
+ new_connections = [(conn.local_address, conn.local_port, conn.remote_address, conn.remote_port) for conn in conn_resolver.get_value()]
+ new_circuits = {}
- for circ in tor_controller().get_circuits([]):
- # Skips established single-hop circuits (these are for directory
- # fetches, not client circuits)
+ for circ in tor_controller().get_circuits([]):
+ # Skips established single-hop circuits (these are for directory
+ # fetches, not client circuits)
- if not (circ.status == 'BUILT' and len(circ.path) == 1):
- new_circuits[circ.id] = (circ.status, circ.purpose, [entry[0] for entry in circ.path])
+ if not (circ.status == 'BUILT' and len(circ.path) == 1):
+ new_circuits[circ.id] = (circ.status, circ.purpose, [entry[0] for entry in circ.path])
- # Populates new_entries with any of our old entries that still exist.
- # This is both for performance and to keep from resetting the uptime
- # attributes. Note that CircEntries are a ConnectionEntry subclass so
- # we need to check for them first.
+ # Populates new_entries with any of our old entries that still exist.
+ # This is both for performance and to keep from resetting the uptime
+ # attributes. Note that CircEntries are a ConnectionEntry subclass so
+ # we need to check for them first.
- for old_entry in self._entries:
- if isinstance(old_entry, circ_entry.CircEntry):
- new_entry = new_circuits.get(old_entry.circuit_id)
+ for old_entry in self._entries:
+ if isinstance(old_entry, circ_entry.CircEntry):
+ new_entry = new_circuits.get(old_entry.circuit_id)
- if new_entry:
- old_entry.update(new_entry[0], new_entry[2])
- new_entries.append(old_entry)
- del new_circuits[old_entry.circuit_id]
- elif isinstance(old_entry, conn_entry.ConnectionEntry):
- connection_line = old_entry.getLines()[0]
- conn_attr = (connection_line.local.get_address(), connection_line.local.get_port(),
- connection_line.foreign.get_address(), connection_line.foreign.get_port())
+ if new_entry:
+ old_entry.update(new_entry[0], new_entry[2])
+ new_entries.append(old_entry)
+ del new_circuits[old_entry.circuit_id]
+ elif isinstance(old_entry, conn_entry.ConnectionEntry):
+ connection_line = old_entry.getLines()[0]
+ conn_attr = (connection_line.local.get_address(), connection_line.local.get_port(),
+ connection_line.foreign.get_address(), connection_line.foreign.get_port())
- if conn_attr in new_connections:
- new_entries.append(old_entry)
- new_connections.remove(conn_attr)
+ if conn_attr in new_connections:
+ new_entries.append(old_entry)
+ new_connections.remove(conn_attr)
- # Reset any display attributes for the entries we're keeping
+ # Reset any display attributes for the entries we're keeping
- for entry in new_entries:
- entry.reset_display()
+ for entry in new_entries:
+ entry.reset_display()
- # Adds any new connection and circuit entries.
+ # Adds any new connection and circuit entries.
- for local_address, local_port, remote_address, remote_port in new_connections:
- new_conn_entry = conn_entry.ConnectionEntry(local_address, local_port, remote_address, remote_port)
- new_conn_line = new_conn_entry.getLines()[0]
+ for local_address, local_port, remote_address, remote_port in new_connections:
+ new_conn_entry = conn_entry.ConnectionEntry(local_address, local_port, remote_address, remote_port)
+ new_conn_line = new_conn_entry.getLines()[0]
- if new_conn_line.get_type() != conn_entry.Category.CIRCUIT:
- new_entries.append(new_conn_entry)
+ if new_conn_line.get_type() != conn_entry.Category.CIRCUIT:
+ new_entries.append(new_conn_entry)
- # updates exit port and client locale usage information
- if new_conn_line.is_private():
- if new_conn_line.get_type() == conn_entry.Category.INBOUND:
- # client connection, update locale information
+ # updates exit port and client locale usage information
+ if new_conn_line.is_private():
+ if new_conn_line.get_type() == conn_entry.Category.INBOUND:
+ # client connection, update locale information
- client_locale = new_conn_line.foreign.get_locale()
+ client_locale = new_conn_line.foreign.get_locale()
- if client_locale:
- self._client_locale_usage[client_locale] = self._client_locale_usage.get(client_locale, 0) + 1
- elif new_conn_line.get_type() == conn_entry.Category.EXIT:
- exit_port = new_conn_line.foreign.get_port()
- self._exit_port_usage[exit_port] = self._exit_port_usage.get(exit_port, 0) + 1
+ if client_locale:
+ self._client_locale_usage[client_locale] = self._client_locale_usage.get(client_locale, 0) + 1
+ elif new_conn_line.get_type() == conn_entry.Category.EXIT:
+ exit_port = new_conn_line.foreign.get_port()
+ self._exit_port_usage[exit_port] = self._exit_port_usage.get(exit_port, 0) + 1
- for circuit_id in new_circuits:
- status, purpose, path = new_circuits[circuit_id]
- new_entries.append(circ_entry.CircEntry(circuit_id, status, purpose, path))
+ for circuit_id in new_circuits:
+ status, purpose, path = new_circuits[circuit_id]
+ new_entries.append(circ_entry.CircEntry(circuit_id, status, purpose, path))
- # Counts the relays in each of the categories. This also flushes the
- # type cache for all of the connections (in case its changed since last
- # fetched).
+ # Counts the relays in each of the categories. This also flushes the
+ # type cache for all of the connections (in case its changed since last
+ # fetched).
- category_types = list(conn_entry.Category)
- type_counts = dict((type, 0) for type in category_types)
+ category_types = list(conn_entry.Category)
+ type_counts = dict((type, 0) for type in category_types)
- for entry in new_entries:
- if isinstance(entry, conn_entry.ConnectionEntry):
- type_counts[entry.getLines()[0].get_type()] += 1
- elif isinstance(entry, circ_entry.CircEntry):
- type_counts[conn_entry.Category.CIRCUIT] += 1
+ for entry in new_entries:
+ if isinstance(entry, conn_entry.ConnectionEntry):
+ type_counts[entry.getLines()[0].get_type()] += 1
+ elif isinstance(entry, circ_entry.CircEntry):
+ type_counts[conn_entry.Category.CIRCUIT] += 1
- # makes labels for all the categories with connections (ie,
- # "21 outbound", "1 control", etc)
+ # makes labels for all the categories with connections (ie,
+ # "21 outbound", "1 control", etc)
- count_labels = []
+ count_labels = []
- for category in category_types:
- if type_counts[category] > 0:
- count_labels.append('%i %s' % (type_counts[category], category.lower()))
+ for category in category_types:
+ if type_counts[category] > 0:
+ count_labels.append('%i %s' % (type_counts[category], category.lower()))
- if count_labels:
- self._title = 'Connections (%s):' % ', '.join(count_labels)
- else:
- self._title = 'Connections:'
+ if count_labels:
+ self._title = 'Connections (%s):' % ', '.join(count_labels)
+ else:
+ self._title = 'Connections:'
- self._entries = new_entries
+ self._entries = new_entries
- self._entry_lines = []
+ self._entry_lines = []
- for entry in self._entries:
- self._entry_lines += entry.getLines()
+ for entry in self._entries:
+ self._entry_lines += entry.getLines()
- self.set_sort_order()
- self._last_resource_fetch = current_resolution_count
- self.vals_lock.release()
+ self.set_sort_order()
+ self._last_resource_fetch = current_resolution_count
def _resolve_apps(self, flag_query = True):
"""
diff --git a/nyx/torrc_panel.py b/nyx/torrc_panel.py
index dd84fb3..0ed0b96 100644
--- a/nyx/torrc_panel.py
+++ b/nyx/torrc_panel.py
@@ -160,192 +160,188 @@ class TorrcPanel(panel.Panel):
]
def draw(self, width, height):
- self.vals_lock.acquire()
-
- # If true, we assume that the cached value in self._last_content_height is
- # still accurate, and stop drawing when there's nothing more to display.
- # Otherwise the self._last_content_height is suspect, and we'll process all
- # the content to check if it's right (and redraw again with the corrected
- # height if not).
-
- trust_last_content_height = self._last_content_height_args == (width, height)
+ with self.vals_lock:
+ # If true, we assume that the cached value in self._last_content_height is
+ # still accurate, and stop drawing when there's nothing more to display.
+ # Otherwise the self._last_content_height is suspect, and we'll process all
+ # the content to check if it's right (and redraw again with the corrected
+ # height if not).
- # restricts scroll location to valid bounds
+ trust_last_content_height = self._last_content_height_args == (width, height)
- self.scroll = max(0, min(self.scroll, self._last_content_height - height + 1))
+ # restricts scroll location to valid bounds
- rendered_contents, corrections, conf_location = None, {}, None
+ self.scroll = max(0, min(self.scroll, self._last_content_height - height + 1))
- if self.config_type == Config.TORRC:
- loaded_torrc = tor_config.get_torrc()
- loaded_torrc.get_lock().acquire()
- conf_location = loaded_torrc.get_config_location()
+ rendered_contents, corrections, conf_location = None, {}, None
- if not loaded_torrc.is_loaded():
- rendered_contents = ['### Unable to load the torrc ###']
- else:
- rendered_contents = loaded_torrc.get_display_contents(self.strip_comments)
+ if self.config_type == Config.TORRC:
+ loaded_torrc = tor_config.get_torrc()
- # constructs a mapping of line numbers to the issue on it
+ with loaded_torrc.get_lock():
+ conf_location = loaded_torrc.get_config_location()
- corrections = dict((line_number, (issue, msg)) for line_number, issue, msg in loaded_torrc.get_corrections())
+ if not loaded_torrc.is_loaded():
+ rendered_contents = ['### Unable to load the torrc ###']
+ else:
+ rendered_contents = loaded_torrc.get_display_contents(self.strip_comments)
- loaded_torrc.get_lock().release()
- else:
- loaded_nyxrc = conf.get_config('nyx')
- conf_location = loaded_nyxrc._path
- rendered_contents = list(loaded_nyxrc._raw_contents)
+ # constructs a mapping of line numbers to the issue on it
- # offset to make room for the line numbers
+ corrections = dict((line_number, (issue, msg)) for line_number, issue, msg in loaded_torrc.get_corrections())
+ else:
+ loaded_nyxrc = conf.get_config('nyx')
+ conf_location = loaded_nyxrc._path
+ rendered_contents = list(loaded_nyxrc._raw_contents)
- line_number_offset = 0
+ # offset to make room for the line numbers
- if self.show_line_num:
- if len(rendered_contents) == 0:
- line_number_offset = 2
- else:
- line_number_offset = int(math.log10(len(rendered_contents))) + 2
+ line_number_offset = 0
- # draws left-hand scroll bar if content's longer than the height
+ if self.show_line_num:
+ if len(rendered_contents) == 0:
+ line_number_offset = 2
+ else:
+ line_number_offset = int(math.log10(len(rendered_contents))) + 2
- scroll_offset = 0
+ # draws left-hand scroll bar if content's longer than the height
- if CONFIG['features.config.file.showScrollbars'] and self._last_content_height > height - 1:
- scroll_offset = 3
- self.add_scroll_bar(self.scroll, self.scroll + height - 1, self._last_content_height, 1)
+ scroll_offset = 0
- display_line = -self.scroll + 1 # line we're drawing on
+ if CONFIG['features.config.file.showScrollbars'] and self._last_content_height > height - 1:
+ scroll_offset = 3
+ self.add_scroll_bar(self.scroll, self.scroll + height - 1, self._last_content_height, 1)
- # draws the top label
+ display_line = -self.scroll + 1 # line we're drawing on
- if self.is_title_visible():
- source_label = 'Tor' if self.config_type == Config.TORRC else 'Nyx'
- location_label = ' (%s)' % conf_location if conf_location else ''
- self.addstr(0, 0, '%s Configuration File%s:' % (source_label, location_label), curses.A_STANDOUT)
+ # draws the top label
- is_multiline = False # true if we're in the middle of a multiline torrc entry
+ if self.is_title_visible():
+ source_label = 'Tor' if self.config_type == Config.TORRC else 'Nyx'
+ location_label = ' (%s)' % conf_location if conf_location else ''
+ self.addstr(0, 0, '%s Configuration File%s:' % (source_label, location_label), curses.A_STANDOUT)
- for line_number in range(0, len(rendered_contents)):
- line_text = rendered_contents[line_number]
- line_text = line_text.rstrip() # remove ending whitespace
+ is_multiline = False # true if we're in the middle of a multiline torrc entry
- # blank lines are hidden when stripping comments
+ for line_number in range(0, len(rendered_contents)):
+ line_text = rendered_contents[line_number]
+ line_text = line_text.rstrip() # remove ending whitespace
- if self.strip_comments and not line_text:
- continue
+ # blank lines are hidden when stripping comments
- # splits the line into its component (msg, format) tuples
+ if self.strip_comments and not line_text:
+ continue
- line_comp = {
- 'option': ['', (curses.A_BOLD, 'green')],
- 'argument': ['', (curses.A_BOLD, 'cyan')],
- 'correction': ['', (curses.A_BOLD, 'cyan')],
- 'comment': ['', ('white',)],
- }
+ # splits the line into its component (msg, format) tuples
- # parses the comment
+ line_comp = {
+ 'option': ['', (curses.A_BOLD, 'green')],
+ 'argument': ['', (curses.A_BOLD, 'cyan')],
+ 'correction': ['', (curses.A_BOLD, 'cyan')],
+ 'comment': ['', ('white',)],
+ }
- comment_index = line_text.find('#')
+ # parses the comment
- if comment_index != -1:
- line_comp['comment'][0] = line_text[comment_index:]
- line_text = line_text[:comment_index]
+ comment_index = line_text.find('#')
- # splits the option and argument, preserving any whitespace around them
+ if comment_index != -1:
+ line_comp['comment'][0] = line_text[comment_index:]
+ line_text = line_text[:comment_index]
- stripped_line = line_text.strip()
- option_index = stripped_line.find(' ')
+ # splits the option and argument, preserving any whitespace around them
- if is_multiline:
- # part of a multiline entry started on a previous line so everything
- # is part of the argument
- line_comp['argument'][0] = line_text
- elif option_index == -1:
- # no argument provided
- line_comp['option'][0] = line_text
- else:
- option_text = stripped_line[:option_index]
- option_end = line_text.find(option_text) + len(option_text)
- line_comp['option'][0] = line_text[:option_end]
- line_comp['argument'][0] = line_text[option_end:]
+ stripped_line = line_text.strip()
+ option_index = stripped_line.find(' ')
- # flags following lines as belonging to this multiline entry if it ends
- # with a slash
+ if is_multiline:
+ # part of a multiline entry started on a previous line so everything
+ # is part of the argument
+ line_comp['argument'][0] = line_text
+ elif option_index == -1:
+ # no argument provided
+ line_comp['option'][0] = line_text
+ else:
+ option_text = stripped_line[:option_index]
+ option_end = line_text.find(option_text) + len(option_text)
+ line_comp['option'][0] = line_text[:option_end]
+ line_comp['argument'][0] = line_text[option_end:]
- if stripped_line:
- is_multiline = stripped_line.endswith('\\')
+ # flags following lines as belonging to this multiline entry if it ends
+ # with a slash
- # gets the correction
+ if stripped_line:
+ is_multiline = stripped_line.endswith('\\')
- if line_number in corrections:
- line_issue, line_issue_msg = corrections[line_number]
+ # gets the correction
- if line_issue in (tor_config.ValidationError.DUPLICATE, tor_config.ValidationError.IS_DEFAULT):
- line_comp['option'][1] = (curses.A_BOLD, 'blue')
- line_comp['argument'][1] = (curses.A_BOLD, 'blue')
- elif line_issue == tor_config.ValidationError.MISMATCH:
- line_comp['argument'][1] = (curses.A_BOLD, 'red')
- line_comp['correction'][0] = ' (%s)' % line_issue_msg
- else:
- # For some types of configs the correction field is simply used to
- # provide extra data (for instance, the type for tor state fields).
+ if line_number in corrections:
+ line_issue, line_issue_msg = corrections[line_number]
- line_comp['correction'][0] = ' (%s)' % line_issue_msg
- line_comp['correction'][1] = (curses.A_BOLD, 'magenta')
+ if line_issue in (tor_config.ValidationError.DUPLICATE, tor_config.ValidationError.IS_DEFAULT):
+ line_comp['option'][1] = (curses.A_BOLD, 'blue')
+ line_comp['argument'][1] = (curses.A_BOLD, 'blue')
+ elif line_issue == tor_config.ValidationError.MISMATCH:
+ line_comp['argument'][1] = (curses.A_BOLD, 'red')
+ line_comp['correction'][0] = ' (%s)' % line_issue_msg
+ else:
+ # For some types of configs the correction field is simply used to
+ # provide extra data (for instance, the type for tor state fields).
- # draws the line number
+ line_comp['correction'][0] = ' (%s)' % line_issue_msg
+ line_comp['correction'][1] = (curses.A_BOLD, 'magenta')
- if self.show_line_num and display_line < height and display_line >= 1:
- line_number_str = ('%%%ii' % (line_number_offset - 1)) % (line_number + 1)
- self.addstr(display_line, scroll_offset, line_number_str, curses.A_BOLD, 'yellow')
+ # draws the line number
- # draws the rest of the components with line wrap
+ if self.show_line_num and display_line < height and display_line >= 1:
+ line_number_str = ('%%%ii' % (line_number_offset - 1)) % (line_number + 1)
+ self.addstr(display_line, scroll_offset, line_number_str, curses.A_BOLD, 'yellow')
- cursor_location, line_offset = line_number_offset + scroll_offset, 0
- max_lines_per_entry = CONFIG['features.config.file.max_lines_per_entry']
- display_queue = [line_comp[entry] for entry in ('option', 'argument', 'correction', 'comment')]
+ # draws the rest of the components with line wrap
- while display_queue:
- msg, format = display_queue.pop(0)
+ cursor_location, line_offset = line_number_offset + scroll_offset, 0
+ max_lines_per_entry = CONFIG['features.config.file.max_lines_per_entry']
+ display_queue = [line_comp[entry] for entry in ('option', 'argument', 'correction', 'comment')]
- max_msg_size, include_break = width - cursor_location, False
+ while display_queue:
+ msg, format = display_queue.pop(0)
- if len(msg) >= max_msg_size:
- # message is too long - break it up
+ max_msg_size, include_break = width - cursor_location, False
- if line_offset == max_lines_per_entry - 1:
- msg = str_tools.crop(msg, max_msg_size)
- else:
- include_break = True
- msg, remainder = str_tools.crop(msg, max_msg_size, 4, 4, str_tools.Ending.HYPHEN, True)
- display_queue.insert(0, (remainder.strip(), format))
+ if len(msg) >= max_msg_size:
+ # message is too long - break it up
- draw_line = display_line + line_offset
+ if line_offset == max_lines_per_entry - 1:
+ msg = str_tools.crop(msg, max_msg_size)
+ else:
+ include_break = True
+ msg, remainder = str_tools.crop(msg, max_msg_size, 4, 4, str_tools.Ending.HYPHEN, True)
+ display_queue.insert(0, (remainder.strip(), format))
- if msg and draw_line < height and draw_line >= 1:
- self.addstr(draw_line, cursor_location, msg, *format)
+ draw_line = display_line + line_offset
- # If we're done, and have added content to this line, then start
- # further content on the next line.
+ if msg and draw_line < height and draw_line >= 1:
+ self.addstr(draw_line, cursor_location, msg, *format)
- cursor_location += len(msg)
- include_break |= not display_queue and cursor_location != line_number_offset + scroll_offset
+ # If we're done, and have added content to this line, then start
+ # further content on the next line.
- if include_break:
- line_offset += 1
- cursor_location = line_number_offset + scroll_offset
+ cursor_location += len(msg)
+ include_break |= not display_queue and cursor_location != line_number_offset + scroll_offset
- display_line += max(line_offset, 1)
+ if include_break:
+ line_offset += 1
+ cursor_location = line_number_offset + scroll_offset
- if trust_last_content_height and display_line >= height:
- break
+ display_line += max(line_offset, 1)
- if not trust_last_content_height:
- self._last_content_height_args = (width, height)
- new_content_height = display_line + self.scroll - 1
+ if trust_last_content_height and display_line >= height:
+ break
- if self._last_content_height != new_content_height:
- self._last_content_height = new_content_height
- self.redraw(True)
+ if not trust_last_content_height:
+ self._last_content_height_args = (width, height)
+ new_content_height = display_line + self.scroll - 1
- self.vals_lock.release()
+ if self._last_content_height != new_content_height:
+ self._last_content_height = new_content_height
+ self.redraw(True)
diff --git a/nyx/util/tor_config.py b/nyx/util/tor_config.py
index 4e3f16e..35ebd24 100644
--- a/nyx/util/tor_config.py
+++ b/nyx/util/tor_config.py
@@ -149,170 +149,168 @@ def load_option_descriptions(load_path = None, check_version = True):
match the cached descriptors, otherwise accepts anyway
"""
- CONFIG_DESCRIPTIONS_LOCK.acquire()
- CONFIG_DESCRIPTIONS.clear()
+ with CONFIG_DESCRIPTIONS_LOCK:
+ CONFIG_DESCRIPTIONS.clear()
- raised_exc = None
- loaded_version = ''
+ raised_exc = None
+ loaded_version = ''
- try:
- if load_path:
- # Input file is expected to be of the form:
- # <option>
- # <arg description>
- # <description, possibly multiple lines>
- # <PERSIST_ENTRY_DIVIDER>
- input_file = open(load_path, 'r')
- input_file_contents = input_file.readlines()
- input_file.close()
+ try:
+ if load_path:
+ # Input file is expected to be of the form:
+ # <option>
+ # <arg description>
+ # <description, possibly multiple lines>
+ # <PERSIST_ENTRY_DIVIDER>
+ input_file = open(load_path, 'r')
+ input_file_contents = input_file.readlines()
+ input_file.close()
- try:
- version_line = input_file_contents.pop(0).rstrip()
+ try:
+ version_line = input_file_contents.pop(0).rstrip()
- if version_line.startswith('Tor Version '):
- file_version = version_line[12:]
- loaded_version = file_version
- tor_version = tor_controller().get_info('version', '')
+ if version_line.startswith('Tor Version '):
+ file_version = version_line[12:]
+ loaded_version = file_version
+ tor_version = tor_controller().get_info('version', '')
- if check_version and file_version != tor_version:
- msg = "wrong version, tor is %s but the file's from %s" % (tor_version, file_version)
- raise IOError(msg)
- else:
- raise IOError('unable to parse version')
+ if check_version and file_version != tor_version:
+ msg = "wrong version, tor is %s but the file's from %s" % (tor_version, file_version)
+ raise IOError(msg)
+ else:
+ raise IOError('unable to parse version')
- while input_file_contents:
- # gets category enum, failing if it doesn't exist
- category = input_file_contents.pop(0).rstrip()
+ while input_file_contents:
+ # gets category enum, failing if it doesn't exist
+ category = input_file_contents.pop(0).rstrip()
- if category not in Category:
- base_msg = "invalid category in input file: '%s'"
- raise IOError(base_msg % category)
+ if category not in Category:
+ base_msg = "invalid category in input file: '%s'"
+ raise IOError(base_msg % category)
- # gets the position in the man page
- index_arg, index_str = -1, input_file_contents.pop(0).rstrip()
+ # gets the position in the man page
+ index_arg, index_str = -1, input_file_contents.pop(0).rstrip()
- if index_str.startswith('index: '):
- index_str = index_str[7:]
+ if index_str.startswith('index: '):
+ index_str = index_str[7:]
- if index_str.isdigit():
- index_arg = int(index_str)
+ if index_str.isdigit():
+ index_arg = int(index_str)
+ else:
+ raise IOError('non-numeric index value: %s' % index_str)
else:
- raise IOError('non-numeric index value: %s' % index_str)
- else:
- raise IOError('malformed index argument: %s' % index_str)
-
- option = input_file_contents.pop(0).rstrip()
- argument = input_file_contents.pop(0).rstrip()
+ raise IOError('malformed index argument: %s' % index_str)
- description, loaded_line = '', input_file_contents.pop(0)
+ option = input_file_contents.pop(0).rstrip()
+ argument = input_file_contents.pop(0).rstrip()
- while loaded_line != PERSIST_ENTRY_DIVIDER:
- description += loaded_line
+ description, loaded_line = '', input_file_contents.pop(0)
- if input_file_contents:
- loaded_line = input_file_contents.pop(0)
- else:
- break
-
- CONFIG_DESCRIPTIONS[option.lower()] = ManPageEntry(option, index_arg, category, argument, description.rstrip())
- except IndexError:
- CONFIG_DESCRIPTIONS.clear()
- raise IOError('input file format is invalid')
- else:
- man_call_results = system.call('man tor', None)
+ while loaded_line != PERSIST_ENTRY_DIVIDER:
+ description += loaded_line
- if not man_call_results:
- raise IOError('man page not found')
+ if input_file_contents:
+ loaded_line = input_file_contents.pop(0)
+ else:
+ break
- # Fetches all options available with this tor instance. This isn't
- # vital, and the valid_options are left empty if the call fails.
+ CONFIG_DESCRIPTIONS[option.lower()] = ManPageEntry(option, index_arg, category, argument, description.rstrip())
+ except IndexError:
+ CONFIG_DESCRIPTIONS.clear()
+ raise IOError('input file format is invalid')
+ else:
+ man_call_results = system.call('man tor', None)
- controller, valid_options = tor_controller(), []
- config_option_query = controller.get_info('config/names', None)
+ if not man_call_results:
+ raise IOError('man page not found')
- if config_option_query:
- for line in config_option_query.strip().split('\n'):
- valid_options.append(line[:line.find(' ')].lower())
+ # Fetches all options available with this tor instance. This isn't
+ # vital, and the valid_options are left empty if the call fails.
- option_count, last_option, last_arg = 0, None, None
- last_category, last_description = Category.GENERAL, ''
+ controller, valid_options = tor_controller(), []
+ config_option_query = controller.get_info('config/names', None)
- for line in man_call_results:
- line = codecs.latin_1_encode(line, 'replace')[0]
- line = ui_tools.get_printable(line)
- stripped_line = line.strip()
+ if config_option_query:
+ for line in config_option_query.strip().split('\n'):
+ valid_options.append(line[:line.find(' ')].lower())
- # we have content, but an indent less than an option (ignore line)
- # if stripped_line and not line.startswith(' ' * MAN_OPT_INDENT): continue
+ option_count, last_option, last_arg = 0, None, None
+ last_category, last_description = Category.GENERAL, ''
- # line starts with an indent equivilant to a new config option
+ for line in man_call_results:
+ line = codecs.latin_1_encode(line, 'replace')[0]
+ line = ui_tools.get_printable(line)
+ stripped_line = line.strip()
- is_opt_indent = line.startswith(' ' * MAN_OPT_INDENT) and line[MAN_OPT_INDENT] != ' '
+ # we have content, but an indent less than an option (ignore line)
+ # if stripped_line and not line.startswith(' ' * MAN_OPT_INDENT): continue
- is_category_line = not line.startswith(' ') and 'OPTIONS' in line
+ # line starts with an indent equivilant to a new config option
- # if this is a category header or a new option, add an entry using the
- # buffered results
+ is_opt_indent = line.startswith(' ' * MAN_OPT_INDENT) and line[MAN_OPT_INDENT] != ' '
- if is_opt_indent or is_category_line:
- # Filters the line based on if the option is recognized by tor or
- # not. This isn't necessary for nyx, so if unable to make the check
- # then we skip filtering (no loss, the map will just have some extra
- # noise).
+ is_category_line = not line.startswith(' ') and 'OPTIONS' in line
- stripped_description = last_description.strip()
+ # if this is a category header or a new option, add an entry using the
+ # buffered results
- if last_option and (not valid_options or last_option.lower() in valid_options):
- CONFIG_DESCRIPTIONS[last_option.lower()] = ManPageEntry(last_option, option_count, last_category, last_arg, stripped_description)
- option_count += 1
+ if is_opt_indent or is_category_line:
+ # Filters the line based on if the option is recognized by tor or
+ # not. This isn't necessary for nyx, so if unable to make the check
+ # then we skip filtering (no loss, the map will just have some extra
+ # noise).
- last_description = ''
+ stripped_description = last_description.strip()
- # parses the option and argument
+ if last_option and (not valid_options or last_option.lower() in valid_options):
+ CONFIG_DESCRIPTIONS[last_option.lower()] = ManPageEntry(last_option, option_count, last_category, last_arg, stripped_description)
+ option_count += 1
- line = line.strip()
- div_index = line.find(' ')
+ last_description = ''
- if div_index != -1:
- last_option, last_arg = line[:div_index], line[div_index + 1:]
+ # parses the option and argument
- # if this is a category header then switch it
+ line = line.strip()
+ div_index = line.find(' ')
- if is_category_line:
- if line.startswith('OPTIONS'):
- last_category = Category.GENERAL
- elif line.startswith('CLIENT'):
- last_category = Category.CLIENT
- elif line.startswith('SERVER'):
- last_category = Category.RELAY
- elif line.startswith('DIRECTORY SERVER'):
- last_category = Category.DIRECTORY
- elif line.startswith('DIRECTORY AUTHORITY SERVER'):
- last_category = Category.AUTHORITY
- elif line.startswith('HIDDEN SERVICE'):
- last_category = Category.HIDDEN_SERVICE
- elif line.startswith('TESTING NETWORK'):
- last_category = Category.TESTING
- else:
- log.notice('Unrecognized category in the man page: %s' % line.strip())
- else:
- # Appends the text to the running description. Empty lines and lines
- # starting with a specific indentation are used for formatting, for
- # instance the ExitPolicy and TestingTorNetwork entries.
+ if div_index != -1:
+ last_option, last_arg = line[:div_index], line[div_index + 1:]
- if last_description and last_description[-1] != '\n':
- last_description += ' '
+ # if this is a category header then switch it
- if not stripped_line:
- last_description += '\n\n'
- elif line.startswith(' ' * MAN_EX_INDENT):
- last_description += ' %s\n' % stripped_line
+ if is_category_line:
+ if line.startswith('OPTIONS'):
+ last_category = Category.GENERAL
+ elif line.startswith('CLIENT'):
+ last_category = Category.CLIENT
+ elif line.startswith('SERVER'):
+ last_category = Category.RELAY
+ elif line.startswith('DIRECTORY SERVER'):
+ last_category = Category.DIRECTORY
+ elif line.startswith('DIRECTORY AUTHORITY SERVER'):
+ last_category = Category.AUTHORITY
+ elif line.startswith('HIDDEN SERVICE'):
+ last_category = Category.HIDDEN_SERVICE
+ elif line.startswith('TESTING NETWORK'):
+ last_category = Category.TESTING
+ else:
+ log.notice('Unrecognized category in the man page: %s' % line.strip())
else:
- last_description += stripped_line
- except IOError as exc:
- raised_exc = exc
+ # Appends the text to the running description. Empty lines and lines
+ # starting with a specific indentation are used for formatting, for
+ # instance the ExitPolicy and TestingTorNetwork entries.
- CONFIG_DESCRIPTIONS_LOCK.release()
+ if last_description and last_description[-1] != '\n':
+ last_description += ' '
+
+ if not stripped_line:
+ last_description += '\n\n'
+ elif line.startswith(' ' * MAN_EX_INDENT):
+ last_description += ' %s\n' % stripped_line
+ else:
+ last_description += stripped_line
+ except IOError as exc:
+ raised_exc = exc
if raised_exc:
raise raised_exc
@@ -338,22 +336,21 @@ def save_option_descriptions(path):
output_file = open(path, 'w')
- CONFIG_DESCRIPTIONS_LOCK.acquire()
- sorted_options = CONFIG_DESCRIPTIONS.keys()
- sorted_options.sort()
+ with CONFIG_DESCRIPTIONS_LOCK:
+ sorted_options = CONFIG_DESCRIPTIONS.keys()
+ sorted_options.sort()
- tor_version = tor_controller().get_info('version', '')
- output_file.write('Tor Version %s\n' % tor_version)
+ tor_version = tor_controller().get_info('version', '')
+ output_file.write('Tor Version %s\n' % tor_version)
- for i in range(len(sorted_options)):
- man_entry = get_config_description(sorted_options[i])
- output_file.write('%s\nindex: %i\n%s\n%s\n%s\n' % (man_entry.category, man_entry.index, man_entry.option, man_entry.arg_usage, man_entry.description))
+ for i in range(len(sorted_options)):
+ man_entry = get_config_description(sorted_options[i])
+ output_file.write('%s\nindex: %i\n%s\n%s\n%s\n' % (man_entry.category, man_entry.index, man_entry.option, man_entry.arg_usage, man_entry.description))
- if i != len(sorted_options) - 1:
- output_file.write(PERSIST_ENTRY_DIVIDER)
+ if i != len(sorted_options) - 1:
+ output_file.write(PERSIST_ENTRY_DIVIDER)
- output_file.close()
- CONFIG_DESCRIPTIONS_LOCK.release()
+ output_file.close()
def get_config_summary(option):
@@ -391,15 +388,11 @@ def get_config_description(option):
option - tor config option
"""
- CONFIG_DESCRIPTIONS_LOCK.acquire()
-
- if option.lower() in CONFIG_DESCRIPTIONS:
- return_val = CONFIG_DESCRIPTIONS[option.lower()]
- else:
- return_val = None
-
- CONFIG_DESCRIPTIONS_LOCK.release()
- return return_val
+ with CONFIG_DESCRIPTIONS_LOCK:
+ if option.lower() in CONFIG_DESCRIPTIONS:
+ return CONFIG_DESCRIPTIONS[option.lower()]
+ else:
+ return None
def get_config_options():
@@ -408,12 +401,8 @@ def get_config_options():
list if no man page has been loaded.
"""
- CONFIG_DESCRIPTIONS_LOCK.acquire()
-
- return_val = [CONFIG_DESCRIPTIONS[opt].option for opt in CONFIG_DESCRIPTIONS]
-
- CONFIG_DESCRIPTIONS_LOCK.release()
- return return_val
+ with CONFIG_DESCRIPTIONS_LOCK:
+ return [CONFIG_DESCRIPTIONS[opt].option for opt in CONFIG_DESCRIPTIONS]
def get_config_location():
@@ -853,28 +842,25 @@ class Torrc():
warning for this before then logs a warning
"""
- self.vals_lock.acquire()
-
- # clears contents and caches
- self.contents, self.config_location = None, None
- self.displayable_contents = None
- self.stripped_contents = None
- self.corrections = None
+ with self.vals_lock:
+ # clears contents and caches
+ self.contents, self.config_location = None, None
+ self.displayable_contents = None
+ self.stripped_contents = None
+ self.corrections = None
- try:
- self.config_location = get_config_location()
- config_file = open(self.config_location, 'r')
- self.contents = config_file.readlines()
- config_file.close()
- except IOError as exc:
- if log_failure and not self.is_foad_fail_warned:
- log.warn('Unable to load torrc (%s)' % exc.strerror)
- self.is_foad_fail_warned = True
-
- self.vals_lock.release()
- raise exc
+ try:
+ self.config_location = get_config_location()
+ config_file = open(self.config_location, 'r')
+ self.contents = config_file.readlines()
+ config_file.close()
+ except IOError as exc:
+ if log_failure and not self.is_foad_fail_warned:
+ log.warn('Unable to load torrc (%s)' % exc.strerror)
+ self.is_foad_fail_warned = True
- self.vals_lock.release()
+ self.vals_lock.release()
+ raise exc
def is_loaded(self):
"""
@@ -896,10 +882,8 @@ class Torrc():
Provides the contents of the configuration file.
"""
- self.vals_lock.acquire()
- return_val = list(self.contents) if self.contents else None
- self.vals_lock.release()
- return return_val
+ with self.vals_lock:
+ return list(self.contents) if self.contents else None
def get_display_contents(self, strip = False):
"""
@@ -914,31 +898,27 @@ class Torrc():
strip - removes comments and extra whitespace if true
"""
- self.vals_lock.acquire()
-
- if not self.is_loaded():
- return_val = None
- else:
- if self.displayable_contents is None:
- # restricts contents to displayable characters
- self.displayable_contents = []
-
- for line_number in range(len(self.contents)):
- line_text = self.contents[line_number]
- line_text = line_text.replace('\t', ' ')
- line_text = ui_tools.get_printable(line_text)
- self.displayable_contents.append(line_text)
+ with self.vals_lock:
+ if not self.is_loaded():
+ return None
+ else:
+ if self.displayable_contents is None:
+ # restricts contents to displayable characters
+ self.displayable_contents = []
- if strip:
- if self.stripped_contents is None:
- self.stripped_contents = _strip_comments(self.displayable_contents)
+ for line_number in range(len(self.contents)):
+ line_text = self.contents[line_number]
+ line_text = line_text.replace('\t', ' ')
+ line_text = ui_tools.get_printable(line_text)
+ self.displayable_contents.append(line_text)
- return_val = list(self.stripped_contents)
- else:
- return_val = list(self.displayable_contents)
+ if strip:
+ if self.stripped_contents is None:
+ self.stripped_contents = _strip_comments(self.displayable_contents)
- self.vals_lock.release()
- return return_val
+ return list(self.stripped_contents)
+ else:
+ return list(self.displayable_contents)
def get_corrections(self):
"""
@@ -947,26 +927,22 @@ class Torrc():
results.
"""
- self.vals_lock.acquire()
-
- if not self.is_loaded():
- return_val = None
- else:
- tor_version = tor_controller().get_version(None)
- skip_validation = not CONFIG['features.torrc.validate']
- skip_validation |= (tor_version is None or not tor_version >= stem.version.Requirement.GETINFO_CONFIG_TEXT)
-
- if skip_validation:
- log.info('Skipping torrc validation (requires tor 0.2.2.7-alpha)')
- return_val = {}
+ with self.vals_lock:
+ if not self.is_loaded():
+ return None
else:
- if self.corrections is None:
- self.corrections = validate(self.contents)
+ tor_version = tor_controller().get_version(None)
+ skip_validation = not CONFIG['features.torrc.validate']
+ skip_validation |= (tor_version is None or not tor_version >= stem.version.Requirement.GETINFO_CONFIG_TEXT)
- return_val = list(self.corrections)
+ if skip_validation:
+ log.info('Skipping torrc validation (requires tor 0.2.2.7-alpha)')
+ return {}
+ else:
+ if self.corrections is None:
+ self.corrections = validate(self.contents)
- self.vals_lock.release()
- return return_val
+ return list(self.corrections)
def get_lock(self):
"""
1
0
commit f2e6d9340280f0b670c98b3815dd74282a929cbd
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon Jul 6 08:37:28 2015 -0700
Mark 'vals_lock' as being private
Several unrevised panels use a lock for its internal state. Marking them all as
private. I'm doing this as a batch since I needed to double check they weren't
used externally anyway.
---
nyx/config_panel.py | 8 ++++----
nyx/connections/conn_panel.py | 12 ++++++------
nyx/torrc_panel.py | 6 +++---
nyx/util/tor_config.py | 13 ++++++-------
4 files changed, 19 insertions(+), 20 deletions(-)
diff --git a/nyx/config_panel.py b/nyx/config_panel.py
index 58f229b..18d76f4 100644
--- a/nyx/config_panel.py
+++ b/nyx/config_panel.py
@@ -225,7 +225,7 @@ class ConfigPanel(panel.Panel):
self.conf_contents = []
self.conf_important_contents = []
self.scroller = ui_tools.Scroller(True)
- self.vals_lock = threading.RLock()
+ self._vals_lock = threading.RLock()
# shows all configuration options if true, otherwise only the ones with
# the 'important' flag are shown
@@ -332,7 +332,7 @@ class ConfigPanel(panel.Panel):
set ordering
"""
- with self.vals_lock:
+ with self._vals_lock:
if ordering:
CONFIG['features.config.order'] = ordering
@@ -358,7 +358,7 @@ class ConfigPanel(panel.Panel):
self.set_sort_order(result_enums)
def handle_key(self, key):
- with self.vals_lock:
+ with self._vals_lock:
if key.is_scroll():
page_height = self.get_preferred_size()[0] - 1
detail_panel_height = CONFIG['features.config.selectionDetails.height']
@@ -571,7 +571,7 @@ class ConfigPanel(panel.Panel):
]
def draw(self, width, height):
- with self.vals_lock:
+ with self._vals_lock:
# panel with details for the current selection
detail_panel_height = CONFIG['features.config.selectionDetails.height']
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index a7b1009..e05a351 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -76,7 +76,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self._last_update = -1 # time the content was last revised
self._is_tor_running = True # indicates if tor is currently running or not
self._halt_time = None # time when tor was stopped
- self.vals_lock = threading.RLock()
+ self._vals_lock = threading.RLock()
self._pause_condition = threading.Condition()
self._halt = False # terminates thread if true
@@ -158,7 +158,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
set ordering
"""
- with self.vals_lock:
+ with self._vals_lock:
if ordering:
nyx_config = conf.get_config('nyx')
@@ -190,7 +190,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
if self.get_listing_type() == listing_type:
return
- with self.vals_lock:
+ with self._vals_lock:
nyx_config = conf.get_config('nyx')
nyx_config.set('features.connection.listing_type', Listing.keys()[Listing.index_of(listing_type)])
@@ -216,7 +216,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self.set_sort_order(results)
def handle_key(self, key):
- with self.vals_lock:
+ with self._vals_lock:
user_traffic_allowed = tor_controller().is_user_traffic_allowed()
if key.is_scroll():
@@ -367,7 +367,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
return self._scroller.get_cursor_selection(self._entry_lines)
def draw(self, width, height):
- with self.vals_lock:
+ with self._vals_lock:
# if we don't have any contents then refuse to show details
if not self._entries:
@@ -469,7 +469,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
conn_resolver = nyx.util.tracker.get_connection_tracker()
current_resolution_count = conn_resolver.run_counter()
- with self.vals_lock:
+ with self._vals_lock:
new_entries = [] # the new results we'll display
# Fetches new connections and client circuits...
diff --git a/nyx/torrc_panel.py b/nyx/torrc_panel.py
index 0ed0b96..b0a19da 100644
--- a/nyx/torrc_panel.py
+++ b/nyx/torrc_panel.py
@@ -38,7 +38,7 @@ class TorrcPanel(panel.Panel):
def __init__(self, stdscr, config_type):
panel.Panel.__init__(self, stdscr, 'torrc', 0)
- self.vals_lock = threading.RLock()
+ self._vals_lock = threading.RLock()
self.config_type = config_type
self.scroll = 0
self.show_line_num = True # shows left aligned line numbers
@@ -122,7 +122,7 @@ class TorrcPanel(panel.Panel):
nyx.popups.show_msg(result_msg, 1)
def handle_key(self, key):
- with self.vals_lock:
+ with self._vals_lock:
if key.is_scroll():
page_height = self.get_preferred_size()[0] - 1
new_scroll = ui_tools.get_scroll_position(key, self.scroll, page_height, self._last_content_height)
@@ -160,7 +160,7 @@ class TorrcPanel(panel.Panel):
]
def draw(self, width, height):
- with self.vals_lock:
+ with self._vals_lock:
# If true, we assume that the cached value in self._last_content_height is
# still accurate, and stop drawing when there's nothing more to display.
# Otherwise the self._last_content_height is suspect, and we'll process all
diff --git a/nyx/util/tor_config.py b/nyx/util/tor_config.py
index 35ebd24..6bb68b3 100644
--- a/nyx/util/tor_config.py
+++ b/nyx/util/tor_config.py
@@ -822,7 +822,7 @@ class Torrc():
def __init__(self):
self.contents = None
self.config_location = None
- self.vals_lock = threading.RLock()
+ self._vals_lock = threading.RLock()
# cached results for the current contents
self.displayable_contents = None
@@ -842,7 +842,7 @@ class Torrc():
warning for this before then logs a warning
"""
- with self.vals_lock:
+ with self._vals_lock:
# clears contents and caches
self.contents, self.config_location = None, None
self.displayable_contents = None
@@ -859,7 +859,6 @@ class Torrc():
log.warn('Unable to load torrc (%s)' % exc.strerror)
self.is_foad_fail_warned = True
- self.vals_lock.release()
raise exc
def is_loaded(self):
@@ -882,7 +881,7 @@ class Torrc():
Provides the contents of the configuration file.
"""
- with self.vals_lock:
+ with self._vals_lock:
return list(self.contents) if self.contents else None
def get_display_contents(self, strip = False):
@@ -898,7 +897,7 @@ class Torrc():
strip - removes comments and extra whitespace if true
"""
- with self.vals_lock:
+ with self._vals_lock:
if not self.is_loaded():
return None
else:
@@ -927,7 +926,7 @@ class Torrc():
results.
"""
- with self.vals_lock:
+ with self._vals_lock:
if not self.is_loaded():
return None
else:
@@ -949,7 +948,7 @@ class Torrc():
Provides the lock governing concurrent access to the contents.
"""
- return self.vals_lock
+ return self._vals_lock
def log_validation_issues(self):
"""
1
0

22 Sep '15
commit 8b8e97d693d57fec3f8df1f354af0360e7899ab4
Author: Damian Johnson <atagar(a)torproject.org>
Date: Tue Jul 7 08:40:22 2015 -0700
Perform app resolution as part of updates
Seems we called our _resolve_apps() from the draw loop rather than our update
thread. Guess that was so we would only do lookups when viewing the panel?
Regardless, the helper has a sleep in it so that's stupid.
---
nyx/connections/conn_panel.py | 31 +++++++------------------------
1 file changed, 7 insertions(+), 24 deletions(-)
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index e05a351..f74c245 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -98,9 +98,9 @@ class ConnectionPanel(panel.Panel, threading.Thread):
country_summary = None
- for arg in bridge_clients.split():
- if arg.startswith('CountrySummary='):
- country_summary = arg[15:]
+ for line in bridge_clients.split():
+ if line.startswith('CountrySummary='):
+ country_summary = line[15:]
break
if country_summary:
@@ -118,10 +118,6 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self._app_resolver = tracker.get_port_usage_tracker()
- # rate limits appResolver queries to once per update
-
- self.app_resolve_since_update = False
-
# mark the initially exitsing connection uptimes as being estimates
for entry in self._entries:
@@ -417,12 +413,6 @@ class ConnectionPanel(panel.Panel, threading.Thread):
for line_number in range(scroll_location, len(self._entry_lines)):
entry_line = self._entry_lines[line_number]
- # if this is an unresolved SOCKS, HIDDEN, or CONTROL entry then queue up
- # resolution for the applicaitions they belong to
-
- if isinstance(entry_line, conn_entry.ConnectionLine) and entry_line.is_unresolved_application():
- self._resolve_apps()
-
# hilighting if this is the selected line
extra_format = curses.A_STANDOUT if entry_line == cursor_selection else curses.A_NORMAL
@@ -459,8 +449,6 @@ class ConnectionPanel(panel.Panel, threading.Thread):
Fetches the newest resolved connections.
"""
- self.app_resolve_since_update = False
-
# if we don't have an initialized resolver then this is a no-op
if not nyx.util.tracker.get_connection_tracker().is_alive():
@@ -576,17 +564,15 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self.set_sort_order()
self._last_resource_fetch = current_resolution_count
- def _resolve_apps(self, flag_query = True):
+ self._resolve_apps()
+
+ def _resolve_apps(self):
"""
Triggers an asynchronous query for all unresolved SOCKS, HIDDEN, and
CONTROL entries.
-
- Arguments:
- flag_query - sets a flag to prevent further call from being respected
- until the next update if true
"""
- if self.app_resolve_since_update or not CONFIG['features.connection.resolveApps']:
+ if not CONFIG['features.connection.resolveApps']:
return
unresolved_lines = [l for l in self._entry_lines if isinstance(l, conn_entry.ConnectionLine) and l.is_unresolved_application()]
@@ -633,6 +619,3 @@ class ConnectionPanel(panel.Panel, threading.Thread):
line.is_application_resolving = False
else:
line.is_application_resolving = self._app_resolver.is_alive
-
- if flag_query:
- self.app_resolve_since_update = True
1
0

22 Sep '15
commit 6f91af2feb26e915fdc418e335a20232a7b7e9b4
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jul 12 12:40:44 2015 -0700
Speed connection panel up by a few seconds
The slowest part of initializing the connection panel is issuing the 'GETINFO
ns/all' query to populate the FingerprintTracker. When the user starts nyx then
immediately goes to the connection page this is a noticeable 3-4 second lag.
Doing this asynchronously in the connection panel's thread, at the *end* of
processing the first results. As a result the user doesn't see this lag at all.
Rather, if they immediately go to the connection page the fingerprints and
nicknames will simply be 'UNKNOWN' for a few seconds. Much better. :P
---
nyx/connections/conn_entry.py | 20 +++++++++++++-------
nyx/connections/conn_panel.py | 9 +++++++++
2 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py
index cd463be..5cb4274 100644
--- a/nyx/connections/conn_entry.py
+++ b/nyx/connections/conn_entry.py
@@ -65,7 +65,7 @@ def get_fingerprint_tracker():
global FINGERPRINT_TRACKER
if FINGERPRINT_TRACKER is None:
- FINGERPRINT_TRACKER = FingerprintTracker(tor_controller().get_network_statuses([]))
+ FINGERPRINT_TRACKER = FingerprintTracker()
return FINGERPRINT_TRACKER
@@ -892,23 +892,29 @@ class ConnectionLine(entries.ConnectionPanelLine):
class FingerprintTracker:
- def __init__(self, router_status_entries):
+ def __init__(self):
self._fingerprint_cache = {} # {address => [(port, fingerprint), ..]} for relays
self._nickname_cache = {} # fingerprint => nickname lookup cache
- for desc in router_status_entries:
- self._fingerprint_cache.setdefault(desc.address, []).append((desc.or_port, desc.fingerprint))
-
tor_controller().add_event_listener(self._new_consensus_event, stem.control.EventType.NEWCONSENSUS)
def _new_consensus_event(self, event):
+ self._nickname_cache = {}
+ self.update(event.desc)
+
+ def update(self, router_status_entries):
+ """
+ Updates our cache with the given router status entries.
+
+ :param list router_status_entries: router status entries to populate our cache with
+ """
+
new_fingerprint_cache = {}
- for desc in event.desc:
+ for desc in router_status_entries:
new_fingerprint_cache.setdefault(desc.address, []).append((desc.or_port, desc.fingerprint))
self._fingerprint_cache = new_fingerprint_cache
- self._nickname_cache = {}
def get_relay_fingerprint(self, address, port = None):
"""
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 2be5f8b..83b9d3a 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -325,6 +325,15 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self._update()
self.redraw(True)
+
+ # If this is our first run then fill in our fingerprint tracker. This
+ # requires fetching all the router status entries which takes a few
+ # seconds, so best done when we're finished with the rest of the first
+ # iteration to hide the lag.
+
+ if last_ran == -1:
+ conn_entry.get_fingerprint_tracker().update(tor_controller().get_network_statuses([]))
+
last_ran = time.time()
def get_help(self):
1
0
commit d2f1b3d8aa19a0e5124ee5650a52b4b7764fbe60
Author: Damian Johnson <atagar(a)torproject.org>
Date: Fri Jul 17 08:27:35 2015 -0700
Use circuit struct in constructors
Backing our CircEntry class with a circuit, rather than passing in its
attributes. I suspect we can go a lot further and simplify this, but one step
at a time.
---
nyx/connections/circ_entry.py | 10 ++++++----
nyx/connections/conn_panel.py | 7 +++----
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/nyx/connections/circ_entry.py b/nyx/connections/circ_entry.py
index 5e27243..02599e5 100644
--- a/nyx/connections/circ_entry.py
+++ b/nyx/connections/circ_entry.py
@@ -20,14 +20,16 @@ from stem.util import str_tools
class CircEntry(conn_entry.ConnectionEntry):
- def __init__(self, circuit_id, status, purpose, path):
+ def __init__(self, circ):
conn_entry.ConnectionEntry.__init__(self, nyx.util.tracker.Connection(time.time(), False, '127.0.0.1', 0, '127.0.0.1', 0, 'tcp'))
- self.circuit_id = circuit_id
- self.status = status
+ self.circuit_id = circ.id
+ self.status = circ.status
# drops to lowercase except the first letter
+ purpose = circ.purpose
+
if len(purpose) >= 2:
purpose = purpose[0].upper() + purpose[1:].lower()
@@ -38,7 +40,7 @@ class CircEntry(conn_entry.ConnectionEntry):
self.lines[0].base_type = conn_entry.Category.CIRCUIT
- self.update(status, path)
+ self.update(circ.status, [entry[0] for entry in circ.path])
def update(self, status, path):
"""
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 4fb3fd1..1bd1254 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -476,7 +476,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
# fetches, not client circuits)
if not (circ.status == 'BUILT' and len(circ.path) == 1):
- new_circuits[circ.id] = (circ.status, circ.purpose, [entry[0] for entry in circ.path])
+ new_circuits[circ.id] = circ
# Populates new_entries with any of our old entries that still exist.
# This is both for performance and to keep from resetting the uptime
@@ -488,7 +488,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
new_entry = new_circuits.get(old_entry.circuit_id)
if new_entry:
- old_entry.update(new_entry[0], new_entry[2])
+ old_entry.update(new_entry.status, [entry[0] for entry in new_entry.path])
new_entries.append(old_entry)
del new_circuits[old_entry.circuit_id]
elif isinstance(old_entry, conn_entry.ConnectionEntry):
@@ -524,8 +524,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
self._exit_port_usage[exit_port] = self._exit_port_usage.get(exit_port, 0) + 1
for circuit_id in new_circuits:
- status, purpose, path = new_circuits[circuit_id]
- new_entries.append(circ_entry.CircEntry(circuit_id, status, purpose, path))
+ new_entries.append(circ_entry.CircEntry(new_circuits[circuit_id]))
# Counts the relays in each of the categories. This also flushes the
# type cache for all of the connections (in case its changed since last
1
0
commit 1c225e8026b17c860be3980017987247cde3944e
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sun Jul 12 14:46:21 2015 -0700
Default value for Endpoint methods
Our Endpoint class returned 'UNKNOWN' as its fingerprint and nickname if...
well, unknown. Hardcoded values like this sucks. Using a default argument
instead.
---
nyx/connections/circ_entry.py | 6 +--
nyx/connections/conn_entry.py | 99 +++++++++++++++--------------------------
nyx/connections/conn_panel.py | 2 +-
nyx/util/tracker.py | 2 +-
4 files changed, 42 insertions(+), 67 deletions(-)
diff --git a/nyx/connections/circ_entry.py b/nyx/connections/circ_entry.py
index 63108ca..636464d 100644
--- a/nyx/connections/circ_entry.py
+++ b/nyx/connections/circ_entry.py
@@ -194,21 +194,21 @@ class CircLine(conn_entry.ConnectionLine):
# fills the nickname into the empty space here
- dst = '%s%-25s ' % (dst[:25], str_tools.crop(self.foreign.get_nickname(), 25, 0))
+ dst = '%s%-25s ' % (dst[:25], str_tools.crop(self.foreign.get_nickname('UNKNOWN'), 25, 0))
etc = self.get_etc_content(width - baseline_space - len(dst), listing_type)
elif listing_type == entries.ListingType.FINGERPRINT:
# dst width is derived as:
# src (9) + dst (40) + divider (7) + right gap (2) - bracket (3) = 55 char
- dst = '%-55s' % self.foreign.get_fingerprint()
+ dst = '%-55s' % self.foreign.get_fingerprint('UNKNOWN')
etc = self.get_etc_content(width - baseline_space - len(dst), listing_type)
else:
# min space for the nickname is 56 characters
etc = self.get_etc_content(width - baseline_space - 56, listing_type)
dst_layout = '%%-%is' % (width - baseline_space - len(etc))
- dst = dst_layout % self.foreign.get_nickname()
+ dst = dst_layout % self.foreign.get_nickname('UNKNOWN')
return ((dst + etc, line_format),
(' ' * (width - baseline_space - len(dst) - len(etc) + 5), line_format),
diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py
index 317da22..041e57a 100644
--- a/nyx/connections/conn_entry.py
+++ b/nyx/connections/conn_entry.py
@@ -60,20 +60,13 @@ CONFIG = conf.config_dict('nyx', {
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.
+ Connection endpoint, with basic consensus information if a relay.
"""
def __init__(self, address, port):
self.address = address
self.port = port
-
- # if true, we treat the port as an definitely not being an ORPort when
- # searching for matching fingerprints (otherwise we use it to possably
- # narrow results when unknown)
-
- self.is_not_or_port = True
+ self.is_or_port = False # if set, consider the port to possibly be an ORPort
# if set then this overwrites fingerprint lookups
@@ -81,7 +74,7 @@ class Endpoint:
def get_address(self):
"""
- Provides the IP address of the endpoint.
+ Provides the address of the endpoint.
"""
return self.address
@@ -93,56 +86,38 @@ class Endpoint:
return self.port
- def get_locale(self, default=None):
+ def get_locale(self, default = None):
"""
- Provides the two letter country code for the IP address' locale.
-
- Arguments:
- default - return value if no locale information is available
+ Provides the two letter country code of this relay.
"""
- controller = tor_controller()
- return controller.get_info('ip-to-country/%s' % self.address, default)
+ return tor_controller().get_info('ip-to-country/%s' % self.address, default)
- def get_fingerprint(self):
+ def get_fingerprint(self, default = None):
"""
- Provides the fingerprint of the relay, returning "UNKNOWN" if it can't be
- determined.
+ Provides the fingerprint of this relay.
"""
if self.fingerprint_overwrite:
return self.fingerprint_overwrite
- my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self.address)
-
- # If there were multiple matches and our port is likely the ORPort then
- # try again with that to narrow the results.
-
- if not my_fingerprint and not self.is_not_or_port:
- my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self.address, int(self.port))
-
- if my_fingerprint:
- return my_fingerprint
- else:
- return 'UNKNOWN'
+ my_fingerprint = nyx.util.tracker.get_consensus_tracker().get_relay_fingerprint(self.address, self.port if self.is_or_port else None)
+ return my_fingerprint if my_fingerprint else default
- def get_nickname(self):
+ def get_nickname(self, default = None):
"""
- Provides the nickname of the relay, retuning "UNKNOWN" if it can't be
- determined.
+ Provides the nickname of this relay.
"""
- my_fingerprint = self.get_fingerprint()
+ fingerprint = self.get_fingerprint()
- if my_fingerprint != 'UNKNOWN':
- my_nickname = nyx.util.tracker.get_consensus_tracker().get_relay_nickname(my_fingerprint)
+ if fingerprint:
+ nickname = nyx.util.tracker.get_consensus_tracker().get_relay_nickname(fingerprint)
- if my_nickname:
- return my_nickname
- else:
- return 'UNKNOWN'
- else:
- return 'UNKNOWN'
+ if nickname:
+ return nickname
+
+ return default
class ConnectionEntry(entries.ConnectionPanelEntry):
@@ -171,14 +146,14 @@ class ConnectionEntry(entries.ConnectionPanelEntry):
elif attr == entries.SortAttr.PORT:
return connection_line.sort_port
elif attr == entries.SortAttr.FINGERPRINT:
- return connection_line.foreign.get_fingerprint()
+ return connection_line.foreign.get_fingerprint('UNKNOWN')
elif attr == entries.SortAttr.NICKNAME:
my_nickname = connection_line.foreign.get_nickname()
- if my_nickname == 'UNKNOWN':
- return 'z' * 20 # orders at the end
- else:
+ if my_nickname:
return my_nickname.lower()
+ else:
+ return 'z' * 20 # orders at the end
elif attr == entries.SortAttr.CATEGORY:
return Category.index_of(connection_line.get_type())
elif attr == entries.SortAttr.UPTIME:
@@ -200,8 +175,8 @@ class ConnectionLine(entries.ConnectionPanelLine):
def __init__(self, local_address, local_port, remote_address, remote_port, include_port=True, include_expanded_addresses=True):
entries.ConnectionPanelLine.__init__(self)
- self.local = Endpoint(local_address, local_port)
- self.foreign = Endpoint(remote_address, remote_port)
+ self.local = Endpoint(local_address, int(local_port))
+ self.foreign = Endpoint(remote_address, int(remote_port))
self.start_time = time.time()
self.is_initial_connection = False
@@ -246,7 +221,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
if local_port in (my_or_port, my_dir_port):
self.base_type = Category.INBOUND
- self.local.is_not_or_port = False
+ self.local.is_or_port = True
elif local_port == my_socks_port:
self.base_type = Category.SOCKS
elif remote_port in my_hidden_service_ports:
@@ -255,7 +230,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
self.base_type = Category.CONTROL
else:
self.base_type = Category.OUTBOUND
- self.foreign.is_not_or_port = False
+ self.foreign.is_or_port = True
self.cached_type = None
@@ -425,7 +400,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
controller = tor_controller()
destination_fingerprint = self.foreign.get_fingerprint()
- if destination_fingerprint == 'UNKNOWN':
+ if not destination_fingerprint:
# Not a known relay. This might be an exit connection.
exit_policy = controller.get_exit_policy(None)
@@ -508,14 +483,14 @@ class ConnectionLine(entries.ConnectionPanelLine):
if width > used_space + 42 and CONFIG['features.connection.showColumn.fingerprint']:
# show fingerprint (column width: 42 characters)
- etc += '%-40s ' % self.foreign.get_fingerprint()
+ etc += '%-40s ' % self.foreign.get_fingerprint('UNKNOWN')
used_space += 42
if width > used_space + 10 and CONFIG['features.connection.showColumn.nickname']:
# show nickname (column width: remainder)
nickname_space = width - used_space
- nickname_label = str_tools.crop(self.foreign.get_nickname(), nickname_space, 0)
+ nickname_label = str_tools.crop(self.foreign.get_nickname('UNKNOWN'), nickname_space, 0)
etc += ('%%-%is ' % nickname_space) % nickname_label
used_space += nickname_space + 2
elif listing_type == entries.ListingType.FINGERPRINT:
@@ -534,7 +509,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
nickname_space -= 28
if CONFIG['features.connection.showColumn.nickname']:
- nickname_label = str_tools.crop(self.foreign.get_nickname(), nickname_space, 0)
+ nickname_label = str_tools.crop(self.foreign.get_nickname('UNKNOWN'), nickname_space, 0)
etc += ('%%-%is ' % nickname_space) % nickname_label
used_space += nickname_space + 2
@@ -544,7 +519,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
else:
if width > used_space + 42 and CONFIG['features.connection.showColumn.fingerprint']:
# show fingerprint (column width: 42 characters)
- etc += '%-40s ' % self.foreign.get_fingerprint()
+ etc += '%-40s ' % self.foreign.get_fingerprint('UNKNOWN')
used_space += 42
if width > used_space + 28 and CONFIG['features.connection.showColumn.destination']:
@@ -643,7 +618,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
if my_type == Category.CONTROL:
dst = 'localhost'
else:
- dst = self.foreign.get_fingerprint()
+ dst = self.foreign.get_fingerprint('UNKNOWN')
dst = '%-40s' % dst
@@ -653,12 +628,12 @@ class ConnectionLine(entries.ConnectionPanelLine):
used_space += len(etc)
else:
# base data requires 50 min characters
- src = self.local.get_nickname()
+ src = self.local.get_nickname('UNKNOWN')
if my_type == Category.CONTROL:
- dst = self.local.get_nickname()
+ dst = self.local.get_nickname('UNKNOWN')
else:
- dst = self.foreign.get_nickname()
+ dst = self.foreign.get_nickname('UNKNOWN')
min_base_space = 50
@@ -705,7 +680,7 @@ class ConnectionLine(entries.ConnectionPanelLine):
fingerprint = self.foreign.get_fingerprint()
controller = tor_controller()
- if fingerprint != 'UNKNOWN':
+ if fingerprint:
# single match - display information available about it
ns_entry = controller.get_info('ns/id/%s' % fingerprint, None)
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 7536a8d..87f1654 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -275,7 +275,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
break
color = nyx.connections.conn_entry.CATEGORY_COLOR[selection.get_type()]
- fingerprint = None if selection.foreign.get_fingerprint() == 'UNKNOWN' else selection.foreign.get_fingerprint()
+ fingerprint = selection.foreign.get_fingerprint()
is_close_key = lambda key: key.is_selection() or key.match('d') or key.match('left') or key.match('right')
key = descriptor_popup.show_descriptor_popup(fingerprint, color, self.max_x, is_close_key)
diff --git a/nyx/util/tracker.py b/nyx/util/tracker.py
index f721c6f..72bfcf2 100644
--- a/nyx/util/tracker.py
+++ b/nyx/util/tracker.py
@@ -723,7 +723,7 @@ class ConsensusTracker(object):
:param str fingerprint: relay to look up
- :reutrns: **str** with the nickname ("Unnamed" if unset), and **None** if
+ :returns: **str** with the nickname ("Unnamed" if unset), and **None** if
no such relay exists
"""
1
0
commit dc68c11df0bce65fd0dc5e26b26ca4ff3046d737
Author: Damian Johnson <atagar(a)torproject.org>
Date: Fri Jul 17 09:03:27 2015 -0700
Don't reuse old connection entries
We reused our existing connection entries for two reasons...
1. These entries tracked uptime.
2. Performance benefits due to entry provided caching.
Neither are true any longer. Uptime comes from the connection tracker or
circuit objects, while caching is similarly done lower down (mostly in
Stem). I banish thee, hacks!
---
nyx/connections/conn_panel.py | 49 ++++++-----------------------------------
1 file changed, 7 insertions(+), 42 deletions(-)
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 1bd1254..42f4124 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -464,46 +464,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
with self._vals_lock:
new_entries = [] # the new results we'll display
- # Fetches new connections and client circuits...
- # new_connections [(local ip, local port, foreign ip, foreign port)...]
- # new_circuits {circuit_id => (status, purpose, path)...}
-
- new_connections = conn_resolver.get_value()
- new_circuits = {}
-
- for circ in tor_controller().get_circuits([]):
- # Skips established single-hop circuits (these are for directory
- # fetches, not client circuits)
-
- if not (circ.status == 'BUILT' and len(circ.path) == 1):
- new_circuits[circ.id] = circ
-
- # Populates new_entries with any of our old entries that still exist.
- # This is both for performance and to keep from resetting the uptime
- # attributes. Note that CircEntries are a ConnectionEntry subclass so
- # we need to check for them first.
-
- for old_entry in self._entries:
- if isinstance(old_entry, circ_entry.CircEntry):
- new_entry = new_circuits.get(old_entry.circuit_id)
-
- if new_entry:
- old_entry.update(new_entry.status, [entry[0] for entry in new_entry.path])
- new_entries.append(old_entry)
- del new_circuits[old_entry.circuit_id]
- elif isinstance(old_entry, conn_entry.ConnectionEntry):
- if old_entry.connection in new_connections:
- new_entries.append(old_entry)
- new_connections.remove(old_entry.connection)
-
- # Reset any display attributes for the entries we're keeping
-
- for entry in new_entries:
- entry.reset_display()
-
- # Adds any new connection and circuit entries.
-
- for conn in new_connections:
+ for conn in conn_resolver.get_value():
new_conn_entry = conn_entry.ConnectionEntry(conn)
new_conn_line = new_conn_entry.getLines()[0]
@@ -523,8 +484,12 @@ class ConnectionPanel(panel.Panel, threading.Thread):
exit_port = new_conn_line.foreign.get_port()
self._exit_port_usage[exit_port] = self._exit_port_usage.get(exit_port, 0) + 1
- for circuit_id in new_circuits:
- new_entries.append(circ_entry.CircEntry(new_circuits[circuit_id]))
+ for circ in tor_controller().get_circuits([]):
+ # Skips established single-hop circuits (these are for directory
+ # fetches, not client circuits)
+
+ if not (circ.status == 'BUILT' and len(circ.path) == 1):
+ new_entries.append(circ_entry.CircEntry(circ))
# Counts the relays in each of the categories. This also flushes the
# type cache for all of the connections (in case its changed since last
1
0
commit 005d7c3634dae07c8d412144e8d73ed7e96cdee9
Author: Damian Johnson <atagar(a)torproject.org>
Date: Thu Jul 16 08:45:08 2015 -0700
Use Connection struct in constructors
Our tracker uses a connection struct, so no point in passing around all these
attributes individually. We also now use the connection's start_time rather
than determining it in the ConnectionEntry.
---
nyx/connections/circ_entry.py | 9 +++++----
nyx/connections/conn_entry.py | 29 ++++++++++++++---------------
nyx/connections/conn_panel.py | 21 ++++++++-------------
3 files changed, 27 insertions(+), 32 deletions(-)
diff --git a/nyx/connections/circ_entry.py b/nyx/connections/circ_entry.py
index 529242c..5e27243 100644
--- a/nyx/connections/circ_entry.py
+++ b/nyx/connections/circ_entry.py
@@ -9,6 +9,7 @@ followed by an entry for each hop in the circuit. For instance:
"""
import curses
+import time
import nyx.util.tracker
import nyx.util.ui_tools
@@ -20,7 +21,7 @@ from stem.util import str_tools
class CircEntry(conn_entry.ConnectionEntry):
def __init__(self, circuit_id, status, purpose, path):
- conn_entry.ConnectionEntry.__init__(self, '127.0.0.1', '0', '127.0.0.1', '0')
+ conn_entry.ConnectionEntry.__init__(self, nyx.util.tracker.Connection(time.time(), False, '127.0.0.1', 0, '127.0.0.1', 0, 'tcp'))
self.circuit_id = circuit_id
self.status = status
@@ -85,13 +86,13 @@ class CircHeaderLine(conn_entry.ConnectionLine):
"""
def __init__(self, circuit_id, purpose):
- conn_entry.ConnectionLine.__init__(self, '127.0.0.1', '0', '0.0.0.0', '0', False, False)
+ conn_entry.ConnectionLine.__init__(self, nyx.util.tracker.Connection(time.time(), False, '127.0.0.1', 0, '0.0.0.0', 0, 'tcp'), False, False)
self.circuit_id = circuit_id
self.purpose = purpose
self.is_built = False
def set_exit(self, exit_address, exit_port, exit_fingerprint):
- conn_entry.ConnectionLine.__init__(self, '127.0.0.1', '0', exit_address, exit_port, False, False)
+ conn_entry.ConnectionLine.__init__(self, nyx.util.tracker.Connection(time.time(), False, '127.0.0.1', 0, exit_address, exit_port, 'tcp'), False, False)
self.is_built = True
self.foreign.fingerprint_overwrite = exit_fingerprint
@@ -136,7 +137,7 @@ class CircLine(conn_entry.ConnectionLine):
"""
def __init__(self, remote_address, remote_port, remote_fingerprint, placement_label):
- conn_entry.ConnectionLine.__init__(self, '127.0.0.1', '0', remote_address, remote_port)
+ conn_entry.ConnectionLine.__init__(self, nyx.util.tracker.Connection(time.time(), False, '127.0.0.1', 0, remote_address, remote_port, 'tcp'))
self.foreign.fingerprint_overwrite = remote_fingerprint
self.placement_label = placement_label
self.include_port = False
diff --git a/nyx/connections/conn_entry.py b/nyx/connections/conn_entry.py
index 041e57a..7693982 100644
--- a/nyx/connections/conn_entry.py
+++ b/nyx/connections/conn_entry.py
@@ -3,7 +3,6 @@ Connection panel entries related to actual connections to or from the system
(ie, results seen by netstat, lsof, etc).
"""
-import time
import curses
import nyx.util.tracker
@@ -127,9 +126,10 @@ class ConnectionEntry(entries.ConnectionPanelEntry):
application, and controller categories.
"""
- def __init__(self, local_address, local_port, remote_address, remote_port):
+ def __init__(self, conn):
entries.ConnectionPanelEntry.__init__(self)
- self.lines = [ConnectionLine(local_address, local_port, remote_address, remote_port)]
+ self.connection = conn
+ self.lines = [ConnectionLine(conn)]
def get_sort_value(self, attr, listing_type):
"""
@@ -157,7 +157,7 @@ class ConnectionEntry(entries.ConnectionPanelEntry):
elif attr == entries.SortAttr.CATEGORY:
return Category.index_of(connection_line.get_type())
elif attr == entries.SortAttr.UPTIME:
- return connection_line.start_time
+ return self.connection.start_time
elif attr == entries.SortAttr.COUNTRY:
if connection.is_private_address(self.lines[0].foreign.get_address()):
return ''
@@ -172,13 +172,12 @@ class ConnectionLine(entries.ConnectionPanelLine):
Display component of the ConnectionEntry.
"""
- def __init__(self, local_address, local_port, remote_address, remote_port, include_port=True, include_expanded_addresses=True):
+ def __init__(self, conn, include_port=True, include_expanded_addresses=True):
entries.ConnectionPanelLine.__init__(self)
- self.local = Endpoint(local_address, int(local_port))
- self.foreign = Endpoint(remote_address, int(remote_port))
- self.start_time = time.time()
- self.is_initial_connection = False
+ self.local = Endpoint(conn.local_address, int(conn.local_port))
+ self.foreign = Endpoint(conn.remote_address, int(conn.remote_port))
+ self.connection = conn
# overwrite the local fingerprint with ours
@@ -219,14 +218,14 @@ class ConnectionLine(entries.ConnectionPanelLine):
if listen_addr and ':' in listen_addr:
my_or_port = listen_addr[listen_addr.find(':') + 1:]
- if local_port in (my_or_port, my_dir_port):
+ if conn.local_port in (my_or_port, my_dir_port):
self.base_type = Category.INBOUND
self.local.is_or_port = True
- elif local_port == my_socks_port:
+ elif conn.local_port == my_socks_port:
self.base_type = Category.SOCKS
- elif remote_port in my_hidden_service_ports:
+ elif conn.remote_port in my_hidden_service_ports:
self.base_type = Category.HIDDEN
- elif local_port == my_ctl_port:
+ elif conn.local_port == my_ctl_port:
self.base_type = Category.CONTROL
else:
self.base_type = Category.OUTBOUND
@@ -286,11 +285,11 @@ class ConnectionLine(entries.ConnectionPanelLine):
# fill in the current uptime and return the results
if CONFIG['features.connection.markInitialConnections']:
- time_prefix = '+' if self.is_initial_connection else ' '
+ time_prefix = '+' if self.connection.is_legacy else ' '
else:
time_prefix = ''
- time_label = time_prefix + '%5s' % str_tools.time_label(current_time - self.start_time, 1)
+ time_label = time_prefix + '%5s' % str_tools.time_label(current_time - self.connection.start_time, 1)
my_listing[2] = (time_label, my_listing[2][1])
return my_listing
diff --git a/nyx/connections/conn_panel.py b/nyx/connections/conn_panel.py
index 87f1654..4fb3fd1 100644
--- a/nyx/connections/conn_panel.py
+++ b/nyx/connections/conn_panel.py
@@ -454,12 +454,11 @@ class ConnectionPanel(panel.Panel, threading.Thread):
Fetches the newest resolved connections.
"""
- # if we don't have an initialized resolver then this is a no-op
+ conn_resolver = nyx.util.tracker.get_connection_tracker()
- if not nyx.util.tracker.get_connection_tracker().is_alive():
- return
+ if not conn_resolver.is_alive():
+ return # if we're not fetching connections then this is a no-op
- conn_resolver = nyx.util.tracker.get_connection_tracker()
current_resolution_count = conn_resolver.run_counter()
with self._vals_lock:
@@ -469,7 +468,7 @@ class ConnectionPanel(panel.Panel, threading.Thread):
# new_connections [(local ip, local port, foreign ip, foreign port)...]
# new_circuits {circuit_id => (status, purpose, path)...}
- new_connections = [(conn.local_address, conn.local_port, conn.remote_address, conn.remote_port) for conn in conn_resolver.get_value()]
+ new_connections = conn_resolver.get_value()
new_circuits = {}
for circ in tor_controller().get_circuits([]):
@@ -493,13 +492,9 @@ class ConnectionPanel(panel.Panel, threading.Thread):
new_entries.append(old_entry)
del new_circuits[old_entry.circuit_id]
elif isinstance(old_entry, conn_entry.ConnectionEntry):
- connection_line = old_entry.getLines()[0]
- conn_attr = (connection_line.local.get_address(), connection_line.local.get_port(),
- connection_line.foreign.get_address(), connection_line.foreign.get_port())
-
- if conn_attr in new_connections:
+ if old_entry.connection in new_connections:
new_entries.append(old_entry)
- new_connections.remove(conn_attr)
+ new_connections.remove(old_entry.connection)
# Reset any display attributes for the entries we're keeping
@@ -508,8 +503,8 @@ class ConnectionPanel(panel.Panel, threading.Thread):
# Adds any new connection and circuit entries.
- for local_address, local_port, remote_address, remote_port in new_connections:
- new_conn_entry = conn_entry.ConnectionEntry(local_address, local_port, remote_address, remote_port)
+ for conn in new_connections:
+ new_conn_entry = conn_entry.ConnectionEntry(conn)
new_conn_line = new_conn_entry.getLines()[0]
if new_conn_line.get_type() != conn_entry.Category.CIRCUIT:
1
0
commit 40f5bb69cc3bf48871d92816888e5e7842a4e75c
Author: Damian Johnson <atagar(a)torproject.org>
Date: Wed Jul 15 09:49:48 2015 -0700
Track connection uptime
Our connection panel does some gross things to keep track of connection uptime.
Our tracker class can easily handle this, simplifying the panel and making this
functionality more testable.
---
nyx/util/tracker.py | 26 +++++++++++++----
test/util/tracker/connection_tracker.py | 48 ++++++++++++++++++++++++++-----
2 files changed, 61 insertions(+), 13 deletions(-)
diff --git a/nyx/util/tracker.py b/nyx/util/tracker.py
index 638768b..2471aff 100644
--- a/nyx/util/tracker.py
+++ b/nyx/util/tracker.py
@@ -68,6 +68,14 @@ 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))
+
Resources = collections.namedtuple('Resources', [
'cpu_sample',
'cpu_average',
@@ -441,8 +449,10 @@ class ConnectionTracker(Daemon):
super(ConnectionTracker, self).__init__(rate)
self._connections = []
+ self._start_times = {} # connection => (unix_timestamp, is_legacy)
self._resolvers = connection.system_resolvers()
self._custom_resolver = None
+ self._is_first_run = True
# Number of times in a row we've either failed with our current resolver or
# concluded that our rate is too low.
@@ -462,12 +472,16 @@ class ConnectionTracker(Daemon):
try:
start_time = time.time()
+ new_connections, new_start_times = [], {}
- self._connections = connection.get_connections(
- resolver,
- process_pid = process_pid,
- process_name = process_name,
- )
+ for conn in connection.get_connections(resolver, process_pid = process_pid, process_name = process_name):
+ conn_start_time, is_legacy = self._start_times.get(conn, (start_time, self._is_first_run))
+ new_start_times[conn] = (conn_start_time, is_legacy)
+ new_connections.append(Connection(conn_start_time, is_legacy, *conn))
+
+ self._connections = new_connections
+ self._start_times = new_start_times
+ self._is_first_run = False
runtime = time.time() - start_time
@@ -539,7 +553,7 @@ class ConnectionTracker(Daemon):
"""
Provides a listing of tor's latest connections.
- :returns: **list** of :class:`~stem.util.connection.Connection` we last
+ :returns: **list** of :class:`~nyx.util.tracker.Connection` we last
retrieved, an empty list if our tracker's been stopped
"""
diff --git a/test/util/tracker/connection_tracker.py b/test/util/tracker/connection_tracker.py
index 416a96b..0f3c93f 100644
--- a/test/util/tracker/connection_tracker.py
+++ b/test/util/tracker/connection_tracker.py
@@ -7,9 +7,9 @@ from stem.util import connection
from mock import Mock, patch
-CONNECTION_1 = connection.Connection('127.0.0.1', 3531, '75.119.206.243', 22, 'tcp')
-CONNECTION_2 = connection.Connection('127.0.0.1', 1766, '86.59.30.40', 443, 'tcp')
-CONNECTION_3 = connection.Connection('127.0.0.1', 1059, '74.125.28.106', 80, 'tcp')
+STEM_CONNECTION_1 = connection.Connection('127.0.0.1', 3531, '75.119.206.243', 22, 'tcp')
+STEM_CONNECTION_2 = connection.Connection('127.0.0.1', 1766, '86.59.30.40', 443, 'tcp')
+STEM_CONNECTION_3 = connection.Connection('127.0.0.1', 1059, '74.125.28.106', 80, 'tcp')
class TestConnectionTracker(unittest.TestCase):
@@ -19,7 +19,7 @@ class TestConnectionTracker(unittest.TestCase):
@patch('nyx.util.tracker.connection.system_resolvers', Mock(return_value = [connection.Resolver.NETSTAT]))
def test_fetching_connections(self, get_value_mock, tor_controller_mock):
tor_controller_mock().get_pid.return_value = 12345
- get_value_mock.return_value = [CONNECTION_1, CONNECTION_2, CONNECTION_3]
+ get_value_mock.return_value = [STEM_CONNECTION_1, STEM_CONNECTION_2, STEM_CONNECTION_3]
with ConnectionTracker(0.04) as daemon:
time.sleep(0.01)
@@ -27,7 +27,7 @@ class TestConnectionTracker(unittest.TestCase):
connections = daemon.get_value()
self.assertEqual(1, daemon.run_counter())
- self.assertEqual([CONNECTION_1, CONNECTION_2, CONNECTION_3], connections)
+ self.assertEqual(['75.119.206.243', '86.59.30.40', '74.125.28.106'], [conn.remote_address for conn in connections])
get_value_mock.return_value = [] # no connection results
time.sleep(0.05)
@@ -63,7 +63,7 @@ class TestConnectionTracker(unittest.TestCase):
# Now make connection resolution work. We still shouldn't provide any
# results since we stopped looking.
- get_value_mock.return_value = [CONNECTION_1, CONNECTION_2]
+ get_value_mock.return_value = [STEM_CONNECTION_1, STEM_CONNECTION_2]
get_value_mock.side_effect = None
time.sleep(0.05)
self.assertEqual([], daemon.get_value())
@@ -73,4 +73,38 @@ class TestConnectionTracker(unittest.TestCase):
daemon.set_custom_resolver(connection.Resolver.NETSTAT)
time.sleep(0.05)
- self.assertEqual([CONNECTION_1, CONNECTION_2], daemon.get_value())
+ self.assertEqual(['75.119.206.243', '86.59.30.40'], [conn.remote_address for conn in daemon.get_value()])
+
+ @patch('nyx.util.tracker.tor_controller')
+ @patch('nyx.util.tracker.connection.get_connections')
+ @patch('nyx.util.tracker.system', Mock(return_value = Mock()))
+ @patch('nyx.util.tracker.connection.system_resolvers', Mock(return_value = [connection.Resolver.NETSTAT]))
+ def test_tracking_uptime(self, get_value_mock, tor_controller_mock):
+ tor_controller_mock().get_pid.return_value = 12345
+ get_value_mock.return_value = [STEM_CONNECTION_1]
+ first_start_time = time.time()
+
+ with ConnectionTracker(0.04) as daemon:
+ time.sleep(0.01)
+
+ connections = daemon.get_value()
+ self.assertEqual(1, len(connections))
+
+ self.assertEqual('75.119.206.243', connections[0].remote_address)
+ self.assertTrue(first_start_time < connections[0].start_time < time.time())
+ self.assertTrue(connections[0].is_legacy)
+
+ second_start_time = time.time()
+ get_value_mock.return_value = [STEM_CONNECTION_1, STEM_CONNECTION_2]
+ time.sleep(0.05)
+
+ connections = daemon.get_value()
+ self.assertEqual(2, len(connections))
+
+ self.assertEqual('75.119.206.243', connections[0].remote_address)
+ self.assertTrue(first_start_time < connections[0].start_time < time.time())
+ self.assertTrue(connections[0].is_legacy)
+
+ self.assertEqual('86.59.30.40', connections[1].remote_address)
+ self.assertTrue(second_start_time < connections[1].start_time < time.time())
+ self.assertFalse(connections[1].is_legacy)
1
0

22 Sep '15
commit 6d0f8d928340f1ae053d62576b2e48c082fa9c01
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon Jul 13 09:12:40 2015 -0700
Move get_relay_address() to ConsensusTracker
Moving this helper method not only tidies things up, but greatly reduces the
number of GETINFO requests we make (we already process all router status
entries, so why query them again?).
Also, we weren't updating the address/port of relays if they changed in a new
consensus. This fixes that.
---
nyx/connections/circ_entry.py | 42 +++--------------------------------------
nyx/util/tracker.py | 27 +++++++++++++++++++++++++-
2 files changed, 29 insertions(+), 40 deletions(-)
diff --git a/nyx/connections/circ_entry.py b/nyx/connections/circ_entry.py
index 636464d..529242c 100644
--- a/nyx/connections/circ_entry.py
+++ b/nyx/connections/circ_entry.py
@@ -10,15 +10,13 @@ followed by an entry for each hop in the circuit. For instance:
import curses
+import nyx.util.tracker
import nyx.util.ui_tools
from nyx.connections import entries, conn_entry
-from nyx.util import tor_controller
from stem.util import str_tools
-ADDRESS_LOOKUP_CACHE = {}
-
class CircEntry(conn_entry.ConnectionEntry):
def __init__(self, circuit_id, status, purpose, path):
@@ -54,15 +52,14 @@ class CircEntry(conn_entry.ConnectionEntry):
self.status = status
self.lines = [self.lines[0]]
- controller = tor_controller()
if status == 'BUILT' and not self.lines[0].is_built:
- exit_ip, exit_port = get_relay_address(controller, path[-1], ('192.168.0.1', '0'))
+ exit_ip, exit_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(path[-1], ('192.168.0.1', 0))
self.lines[0].set_exit(exit_ip, exit_port, path[-1])
for i in range(len(path)):
relay_fingerprint = path[i]
- relay_ip, relay_port = get_relay_address(controller, relay_fingerprint, ('192.168.0.1', '0'))
+ relay_ip, relay_port = nyx.util.tracker.get_consensus_tracker().get_relay_address(relay_fingerprint, ('192.168.0.1', 0))
if i == len(path) - 1:
if status == 'BUILT':
@@ -213,36 +210,3 @@ class CircLine(conn_entry.ConnectionLine):
return ((dst + etc, line_format),
(' ' * (width - baseline_space - len(dst) - len(etc) + 5), line_format),
('%-14s' % self.placement_label, line_format))
-
-
-def get_relay_address(controller, relay_fingerprint, default = None):
- """
- Provides the (IP Address, ORPort) tuple for a given relay. If the lookup
- fails then this returns the default.
-
- Arguments:
- relay_fingerprint - fingerprint of the relay
- """
-
- result = default
-
- if controller.is_alive():
- # query the address if it isn't yet cached
- if relay_fingerprint not in ADDRESS_LOOKUP_CACHE:
- if relay_fingerprint == controller.get_info('fingerprint', None):
- # this is us, simply check the config
- my_address = controller.get_info('address', None)
- my_or_port = controller.get_conf('ORPort', None)
-
- if my_address and my_or_port:
- ADDRESS_LOOKUP_CACHE[relay_fingerprint] = (my_address, my_or_port)
- else:
- # check the consensus for the relay
- relay = controller.get_network_status(relay_fingerprint, None)
-
- if relay:
- ADDRESS_LOOKUP_CACHE[relay_fingerprint] = (relay.address, relay.or_port)
-
- result = ADDRESS_LOOKUP_CACHE.get(relay_fingerprint, default)
-
- return result
diff --git a/nyx/util/tracker.py b/nyx/util/tracker.py
index 72bfcf2..638768b 100644
--- a/nyx/util/tracker.py
+++ b/nyx/util/tracker.py
@@ -32,7 +32,8 @@ Background tasks for gathering information about the tor process.
|- update - updates the consensus information we're based on
|- get_relay_nickname - provides the nickname for a given relay
|- get_relay_fingerprint - provides the relay running at a location
- +- get_all_relay_fingerprints - provides all relays running at a location
+ |- get_all_relay_fingerprints - provides all relays running at a location
+ +- get_relay_address - provides the address a relay is running at
.. data:: Resources
@@ -696,6 +697,7 @@ class ConsensusTracker(object):
def __init__(self):
self._fingerprint_cache = {} # {address => [(port, fingerprint), ..]} for relays
self._nickname_cache = {} # fingerprint => nickname lookup cache
+ self._address_cache = {}
tor_controller().add_event_listener(self._new_consensus_event, stem.control.EventType.NEWCONSENSUS)
@@ -711,11 +713,14 @@ class ConsensusTracker(object):
"""
new_fingerprint_cache = {}
+ new_address_cache = {}
for desc in router_status_entries:
new_fingerprint_cache.setdefault(desc.address, []).append((desc.or_port, desc.fingerprint))
+ new_address_cache[desc.fingerprint] = (desc.address, desc.or_port)
self._fingerprint_cache = new_fingerprint_cache
+ self._address_cache = new_address_cache
def get_relay_nickname(self, fingerprint):
"""
@@ -781,3 +786,23 @@ class ConsensusTracker(object):
"""
return self._fingerprint_cache.get(address, [])
+
+ def get_relay_address(self, fingerprint, default):
+ """
+ Provides the (address, port) tuple where a relay is running.
+
+ :param str fingerprint: fingerprint to be checked
+
+ :returns: **tuple** with a **str** address and **int** port
+ """
+
+ controller = tor_controller()
+
+ if fingerprint == controller.get_info('fingerprint', None):
+ my_address = controller.get_info('address', None)
+ my_or_ports = controller.get_ports(stem.control.Listener.OR, [])
+
+ if my_address and len(my_or_ports) == 1:
+ return (my_address, my_or_ports[0])
+
+ return self._address_cache.get(fingerprint, default)
1
0