[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] When launching tor avoid writing a torrc to disk if able
commit a2d13131003b0d1abf22d9e5475a762c3f4d4679
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Feb 22 18:44:31 2015 -0800
When launching tor avoid writing a torrc to disk if able
Tor now supports providing the torrc via stdin, so taking advantage of that
when we can...
https://trac.torproject.org/projects/tor/ticket/13865
---
docs/change_log.rst | 1 +
stem/process.py | 64 +++++++++++++++++++++++++++++++++----------------
test/integ/process.py | 43 +++++++++++++++++++++++++++++----
3 files changed, 83 insertions(+), 25 deletions(-)
diff --git a/docs/change_log.rst b/docs/change_log.rst
index 58fa34f..af883ce 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -51,6 +51,7 @@ conversion (:trac:`14075`).
* **Controller**
+ * :func:`~stem.process.launch_tor_with_config` avoids writing a temporary torrc to disk if able (:trac:`13865`)
* The 'strict' argument of :func:`~stem.exit_policy.ExitPolicy.can_exit_to` didn't behave as documented (:trac:`14314`)
* **Descriptors**
diff --git a/stem/process.py b/stem/process.py
index be69810..b8b1f92 100644
--- a/stem/process.py
+++ b/stem/process.py
@@ -25,13 +25,15 @@ import subprocess
import tempfile
import stem.prereq
+import stem.util.str_tools
import stem.util.system
+import stem.version
NO_TORRC = '<no torrc>'
DEFAULT_INIT_TIMEOUT = 90
-def launch_tor(tor_cmd = 'tor', args = None, torrc_path = None, completion_percent = 100, init_msg_handler = None, timeout = DEFAULT_INIT_TIMEOUT, take_ownership = False):
+def launch_tor(tor_cmd = 'tor', args = None, torrc_path = None, completion_percent = 100, init_msg_handler = None, timeout = DEFAULT_INIT_TIMEOUT, take_ownership = False, stdin = None):
"""
Initializes a tor process. This blocks until initialization completes or we
error out.
@@ -60,6 +62,7 @@ def launch_tor(tor_cmd = 'tor', args = None, torrc_path = None, completion_perce
:param bool take_ownership: asserts ownership over the tor process so it
aborts if this python process terminates or a :class:`~stem.control.Controller`
we establish to it disconnects
+ :param str stdin: content to provide on stdin
:returns: **subprocess.Popen** instance for the tor subprocess
@@ -102,7 +105,11 @@ def launch_tor(tor_cmd = 'tor', args = None, torrc_path = None, completion_perce
if take_ownership:
runtime_args += ['__OwningControllerProcess', str(os.getpid())]
- tor_process = subprocess.Popen(runtime_args, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+ tor_process = subprocess.Popen(runtime_args, stdout = subprocess.PIPE, stdin = subprocess.PIPE, stderr = subprocess.PIPE)
+
+ if stdin:
+ tor_process.stdin.write(stem.util.str_tools._to_bytes(stdin))
+ tor_process.stdin.close()
if timeout:
def timeout_handler(signum, frame):
@@ -208,6 +215,14 @@ def launch_tor_with_config(config, tor_cmd = 'tor', completion_percent = 100, in
timeout without success
"""
+ # TODO: Drop this version check when tor 0.2.6.3 or higher is the only game
+ # in town.
+
+ try:
+ use_stdin = stem.version.get_system_tor_version(tor_cmd) >= stem.version.Requirement.TORRC_VIA_STDIN
+ except IOError:
+ use_stdin = False
+
# we need to be sure that we're logging to stdout to figure out when we're
# done bootstrapping
@@ -227,24 +242,31 @@ def launch_tor_with_config(config, tor_cmd = 'tor', completion_percent = 100, in
if not has_stdout:
config['Log'].append('NOTICE stdout')
- torrc_descriptor, torrc_path = tempfile.mkstemp(prefix = 'torrc-', text = True)
+ config_str = ''
+
+ for key, values in list(config.items()):
+ if isinstance(values, str):
+ config_str += '%s %s\n' % (key, values)
+ else:
+ for value in values:
+ config_str += '%s %s\n' % (key, value)
+
+ if use_stdin:
+ return launch_tor(tor_cmd, ['-f', '-'], None, completion_percent, init_msg_handler, timeout, take_ownership, stdin = config_str)
+ else:
+ torrc_descriptor, torrc_path = tempfile.mkstemp(prefix = 'torrc-', text = True)
- try:
- with open(torrc_path, 'w') as torrc_file:
- for key, values in list(config.items()):
- if isinstance(values, str):
- torrc_file.write('%s %s\n' % (key, values))
- else:
- for value in values:
- torrc_file.write('%s %s\n' % (key, value))
-
- # prevents tor from erroring out due to a missing torrc if it gets a sighup
- args = ['__ReloadTorrcOnSIGHUP', '0']
-
- return launch_tor(tor_cmd, args, torrc_path, completion_percent, init_msg_handler, timeout, take_ownership)
- finally:
try:
- os.close(torrc_descriptor)
- os.remove(torrc_path)
- except:
- pass
+ with open(torrc_path, 'w') as torrc_file:
+ torrc_file.write(config_str)
+
+ # prevents tor from erroring out due to a missing torrc if it gets a sighup
+ args = ['__ReloadTorrcOnSIGHUP', '0']
+
+ return launch_tor(tor_cmd, args, torrc_path, completion_percent, init_msg_handler, timeout, take_ownership)
+ finally:
+ try:
+ os.close(torrc_descriptor)
+ os.remove(torrc_path)
+ except:
+ pass
diff --git a/test/integ/process.py b/test/integ/process.py
index d889272..e393a25 100644
--- a/test/integ/process.py
+++ b/test/integ/process.py
@@ -29,9 +29,9 @@ from test.runner import (
try:
# added in python 3.3
- from unittest.mock import patch
+ from unittest.mock import patch, Mock
except ImportError:
- from mock import patch
+ from mock import patch, Mock
BASIC_RELAY_TORRC = """\
ORPort 6000
@@ -253,9 +253,10 @@ class TestProcess(unittest.TestCase):
self.assertTrue('Configuration was valid' in output)
@only_run_once
- def test_launch_tor_with_config(self):
+ @patch('stem.version.get_system_tor_version', Mock(return_value = stem.version.Version('0.0.0.1')))
+ def test_launch_tor_with_config_via_file(self):
"""
- Exercises launch_tor_with_config.
+ Exercises launch_tor_with_config when we write a torrc to disk.
"""
# Launch tor without a torrc, but with a control port. Confirms that this
@@ -289,6 +290,40 @@ class TestProcess(unittest.TestCase):
tor_process.wait()
@only_run_once
+ @require_version(stem.version.Requirement.TORRC_VIA_STDIN)
+ def test_launch_tor_with_config_via_stdin(self):
+ """
+ Exercises launch_tor_with_config when we provide our torrc via stdin.
+ """
+
+ runner = test.runner.get_runner()
+ tor_process = stem.process.launch_tor_with_config(
+ tor_cmd = runner.get_tor_command(),
+ config = {
+ 'SocksPort': '2777',
+ 'ControlPort': '2778',
+ 'DataDirectory': self.data_directory,
+ },
+ completion_percent = 5
+ )
+
+ control_socket = None
+ try:
+ control_socket = stem.socket.ControlPort(port = 2778)
+ stem.connection.authenticate(control_socket, chroot_path = runner.get_chroot())
+
+ # exercises the socket
+ control_socket.send('GETCONF ControlPort')
+ getconf_response = control_socket.recv()
+ self.assertEqual('ControlPort=2778', str(getconf_response))
+ finally:
+ if control_socket:
+ control_socket.close()
+
+ tor_process.kill()
+ tor_process.wait()
+
+ @only_run_once
def test_with_invalid_config(self):
"""
Spawn a tor process with a configuration that should make it dead on arrival.
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits