[tor-commits] [chutney/master] Debug: refactor subprocess launches into their own functions

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


commit db3570aca753cba6c02a1fd0ba6da04717483cf9
Author: teor <teor at torproject.org>
Date:   Tue Oct 23 14:46:09 2018 +1000

    Debug: refactor subprocess launches into their own functions
---
 lib/chutney/TorNet.py | 140 ++++++++++++++++++++++++++++++--------------------
 1 file changed, 85 insertions(+), 55 deletions(-)

diff --git a/lib/chutney/TorNet.py b/lib/chutney/TorNet.py
index faa3d88..d59a846 100644
--- a/lib/chutney/TorNet.py
+++ b/lib/chutney/TorNet.py
@@ -99,6 +99,71 @@ def _warnMissingTor(tor_path, cmdline, tor_name="tor"):
            "containing {}.")
           .format(tor_name, tor_path, " ".join(cmdline), tor_name))
 
+def run_tor(cmdline):
+    """Run the tor command line cmdline, which must start with the path or
+       name of a tor binary.
+
+       Returns the combined stdout and stderr of the process.
+    """
+    try:
+        stdouterr = subprocess.check_output(cmdline,
+                                            stderr=subprocess.STDOUT,
+                                            universal_newlines=True,
+                                            bufsize=-1)
+    except OSError as e:
+        # only catch file not found error
+        if e.errno == errno.ENOENT:
+            _warnMissingTor(cmdline[0], cmdline)
+            sys.exit(1)
+        else:
+            raise
+    except subprocess.CalledProcessError as e:
+        # only catch file not found error
+        if e.returncode == 127:
+            _warnMissingTor(cmdline[0], cmdline)
+            sys.exit(1)
+        else:
+            raise
+    return stdouterr
+
+def launch_process(cmdline, tor_name="tor", stdin=None):
+    """Launch the command line cmdline, which must start with the path or
+       name of a binary. Use tor_name as the canonical name of the binary.
+       Pass stdin to the Popen constructor.
+
+       Returns the Popen object for the launched process.
+    """
+    try:
+        p = subprocess.Popen(cmdline,
+                             stdin=stdin,
+                             stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT,
+                             universal_newlines=True,
+                             bufsize=-1)
+    except OSError as e:
+        # only catch file not found error
+        if e.errno == errno.ENOENT:
+            _warnMissingTor(cmdline[0], cmdline, tor_name=tor_name)
+            sys.exit(1)
+        else:
+            raise
+    return p
+
+def run_tor_gencert(cmdline, passphrase):
+    """Run the tor-gencert command line cmdline, which must start with the
+       path or name of a tor-gencert binary.
+       Then send passphrase to the stdin of the process.
+
+       Returns the combined stdout and stderr of the process.
+    """
+    p = launch_process(cmdline,
+                        tor_name="tor-gencert",
+                        stdin=subprocess.PIPE)
+    (stdouterr, empty_stderr) = p.communicate(passphrase + "\n")
+    assert p.returncode == 0  # XXXX BAD!
+    assert empty_stderr is None
+    return stdouterr
+
 
 class Node(object):
 
@@ -294,16 +359,7 @@ class LocalNodeBuilder(NodeBuilder):
                 tor,
                 "--list-torrc-options",
                 "--hush"]
-            try:
-                opts = subprocess.check_output(cmdline, bufsize=-1,
-                                               universal_newlines=True)
-            except OSError as e:
-                # only catch file not found error
-                if e.errno == errno.ENOENT:
-                    _warnMissingTor(tor, cmdline)
-                    sys.exit(1)
-                else:
-                    raise
+            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()
@@ -419,19 +475,7 @@ class LocalNodeBuilder(NodeBuilder):
             '-a', addr]
         print("Creating identity key %s for %s with %s" % (
             idfile, self._env['nick'], " ".join(cmdline)))
-        try:
-            p = subprocess.Popen(cmdline, stdin=subprocess.PIPE)
-        except OSError as e:
-            # only catch file not found error
-            if e.errno == errno.ENOENT:
-                _warnMissingTor(tor_gencert, cmdline,
-                                tor_name="tor-gencert",
-                                tor_env="CHUTNEY_TOR_GENCERT")
-                sys.exit(1)
-            else:
-                raise
-        p.communicate(passphrase + "\n")
-        assert p.returncode == 0  # XXXX BAD!
+        outerr = run_tor_gencert(cmdline, passphrase)
 
     def _genRouterKey(self):
         """Generate an identity key for this router, unless we already have,
@@ -449,20 +493,11 @@ class LocalNodeBuilder(NodeBuilder):
             "--datadirectory", datadir,
             "--quiet",
             ]
-        try:
-            p = subprocess.Popen(cmdline, stdout=subprocess.PIPE)
-        except OSError as e:
-            # only catch file not found error
-            if e.errno == errno.ENOENT:
-                _warnMissingTor(tor, cmdline)
-                sys.exit(1)
-            else:
-                raise
-        stdout, stderr = p.communicate()
-        fingerprint = "".join((stdout.rstrip().split('\n')[-1]).split()[1:])
+        stdouterr = run_tor(cmdline)
+        fingerprint = "".join((stdouterr.rstrip().split('\n')[-1]).split()[1:])
         if not re.match(r'^[A-F0-9]{40}$', fingerprint):
-            print(("Error when calling %r. It gave %r as a fingerprint "
-                   " and %r on stderr.") % (" ".join(cmdline), stdout, stderr))
+            print("Error when getting fingerprint using '%r'. It output '%r'."
+                  .format(" ".join(cmdline), stdouterr))
             sys.exit(1)
         self._env['fingerprint'] = fingerprint
 
@@ -620,18 +655,11 @@ class LocalNodeController(NodeController):
             "-f", torrc,
             "--quiet",
             ]
-        try:
-            p = subprocess.Popen(cmdline)
-        except OSError as e:
-            # only catch file not found error
-            if e.errno == errno.ENOENT:
-                _warnMissingTor(tor_path, cmdline):
-                sys.exit(1)
-            else:
-                raise
+        p = launch_process(cmdline)
         if self.waitOnLaunch():
             # this requires that RunAsDaemon is set
-            p.wait()
+            (stdouterr, empty_stderr) = p.communicate()
+            assert empty_stderr is None
         else:
             # this does not require RunAsDaemon to be set, but is slower.
             #
@@ -648,16 +676,18 @@ class LocalNodeController(NodeController):
             p.poll()
         if p.returncode is not None and p.returncode != 0:
             if self._env['poll_launch_time'] is None:
-                print("Couldn't launch %s (%s): %s" % (self._env['nick'],
-                                                       " ".join(cmdline),
-                                                       p.returncode))
+                print("Couldn't launch {} command '{}': exit {}, output '{}'"
+                      .format(self._env['nick'],
+                              " ".join(cmdline),
+                              p.returncode,
+                              stdouterr))
             else:
-                print("Couldn't poll %s (%s) "
-                      "after waiting %s seconds for launch"
-                      ": %s" % (self._env['nick'],
-                                " ".join(cmdline),
-                                self._env['poll_launch_time'],
-                                p.returncode))
+                print(("Couldn't poll {} command '{}' " +
+                       "after waiting {} seconds for launch: " +
+                       "exit {}").format(self._env['nick'],
+                                         " ".join(cmdline),
+                                         self._env['poll_launch_time'],
+                                         p.returncode))
             return False
         return True
 





More information about the tor-commits mailing list