[tor-commits] [chutney/master] Debug: print tor versions when launching tor

teor at torproject.org teor at torproject.org
Tue Oct 30 11:30:02 UTC 2018


commit bbf30360ee42dd2af05ff9c7b0e06ebdc6d62d05
Author: teor <teor at 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





More information about the tor-commits mailing list