commit bbf30360ee42dd2af05ff9c7b0e06ebdc6d62d05 Author: teor teor@torproject.org Date: Tue Oct 23 16:20:45 2018 +1000
Debug: print tor versions when launching tor
And for unsupported torrc options.
Also limit the number of unsupported torrc options that are printed. --- lib/chutney/TorNet.py | 103 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 73 insertions(+), 30 deletions(-)
diff --git a/lib/chutney/TorNet.py b/lib/chutney/TorNet.py index 8c6cb57..2aa7235 100644 --- a/lib/chutney/TorNet.py +++ b/lib/chutney/TorNet.py @@ -27,9 +27,13 @@ import chutney.Templating import chutney.Traffic
_BASE_ENVIRON = None +_TOR_VERSIONS = None _TORRC_OPTIONS = None _THE_NETWORK = None
+TORRC_OPTION_WARN_LIMIT = 10 +torrc_option_warn_count = 0 + # Get verbose tracebacks, so we can diagnose better. cgitb.enable(format="plain")
@@ -172,6 +176,49 @@ def run_tor_gencert(cmdline, passphrase): assert empty_stderr is None return stdouterr
+def get_tor_version(tor): + """Return the version of the tor binary. + Versions are cached for each unique tor path. + """ + # find the version of the current tor binary, and cache it + if tor not in _TOR_VERSIONS: + cmdline = [ + tor, + "--version", + ] + tor_version = run_tor(cmdline) + # clean it up a bit + tor_version = tor_version.strip() + tor_version = tor_version.replace("version ", "") + tor_version = tor_version.replace(").", ")") + # check we received a tor version, and nothing else + assert re.match(r'^[-+.() A-Za-z0-9]+$', tor_version) + # cache the version for this tor binary's path + _TOR_VERSIONS[tor] = tor_version + else: + tor_version = _TOR_VERSIONS[tor] + return tor_version + +def get_torrc_options(tor): + """Return the torrc options supported by the tor binary. + Options are cached for each unique tor path. + """ + # find the options the current tor binary supports, and cache them + if tor not in _TORRC_OPTIONS: + cmdline = [ + tor, + "--list-torrc-options", + ] + opts = run_tor(cmdline) + # check we received a list of options, and nothing else + assert re.match(r'(^\w+$)+', opts, flags=re.MULTILINE) + torrc_opts = opts.split() + # cache the options for this tor binary's path + _TORRC_OPTIONS[tor] = torrc_opts + else: + torrc_opts = _TORRC_OPTIONS[tor] + return torrc_opts +
class Node(object):
@@ -361,26 +408,12 @@ class LocalNodeBuilder(NodeBuilder): # now filter the options we're about to write, commenting out # the options that the current tor binary doesn't support tor = self._env['tor'] - # find the options the current tor binary supports, and cache them - if tor not in _TORRC_OPTIONS: - cmdline = [ - tor, - "--list-torrc-options", - ] - opts = run_tor(cmdline) - # check we received a list of options, and nothing else - assert re.match(r'(^\w+$)+', opts, flags=re.MULTILINE) - torrc_opts = opts.split() - # cache the options for this tor binary's path - _TORRC_OPTIONS[tor] = torrc_opts - else: - torrc_opts = _TORRC_OPTIONS[tor] + tor_version = get_tor_version(tor) + torrc_opts = get_torrc_options(tor) # check if each option is supported before writing it # Unsupported option values may need special handling. with open(fn_out, 'w') as f: # we need to do case-insensitive option comparison - # even if this is a static whitelist, - # so we convert to lowercase as close to the loop as possible lower_opts = [opt.lower() for opt in torrc_opts] # keep ends when splitting lines, so we can write them out # using writelines() without messing around with "\n"s @@ -391,17 +424,20 @@ class LocalNodeBuilder(NodeBuilder): if (len(sline) == 0 or sline[0] == '#' or sline.split()[0].lower() in lower_opts): - f.writelines([line]) + pass else: - # well, this could get spammy - # TODO: warn once per option per tor binary - # TODO: print tor version? - print(("The tor binary at %r does not support the " - "option in the torrc line:\n" - "%r") % (tor, line.strip())) - # we could decide to skip these lines entirely - # TODO: write tor version? - f.writelines(["# " + tor + " unsupported: " + line]) + warn_msg = (("The tor binary at {} does not support " + + "the option in the torrc line:\n{}") + .format(tor, line.strip())) + if torrc_option_warn_count < TORRC_OPTION_WARN_LIMIT: + print(warn_msg) + torrc_option_warn_count += 1 + else: + debug(warn_msg) + # always dump the full output to the torrc file + line = ("# {} version {} does not support: {}" + .format(tor, tor_version, line)) + f.writelines([line])
def _getTorrcTemplate(self): """Return the template used to write the torrc for this node.""" @@ -625,18 +661,21 @@ class LocalNodeController(NodeController): nick = self._env['nick'] datadir = self._env['dir'] corefile = "core.%s" % pid + tor_version = get_tor_version(self._env['tor']) if self.isRunning(pid): if listRunning: - print("%s is running with PID %s" % (nick, pid)) + print("{} is running with PID {}: {}" + .format(nick, pid, tor_version)) return True elif os.path.exists(os.path.join(datadir, corefile)): if listNonRunning: - print("%s seems to have crashed, and left core file %s" % ( - nick, corefile)) + print("{} seems to have crashed, and left core file {}: {}" + .format(nick, corefile, tor_version)) return False else: if listNonRunning: - print("%s is stopped" % nick) + print("{} is stopped: {}" + .format(nick, tor_version)) return False
def hup(self): @@ -1180,9 +1219,13 @@ def parseArgs():
def main(): global _BASE_ENVIRON + global _TOR_VERSIONS global _TORRC_OPTIONS global _THE_NETWORK _BASE_ENVIRON = TorEnviron(chutney.Templating.Environ(**DEFAULTS)) + # _TOR_VERSIONS gets initialised on demand as a map of + # "/path/to/tor" => "Tor version ..." + _TOR_VERSIONS = dict() # _TORRC_OPTIONS gets initialised on demand as a map of # "/path/to/tor" => ["SupportedOption1", "SupportedOption2", ...] # Or it can be pre-populated as a static whitelist of options
tor-commits@lists.torproject.org