diff --git a/docs/changelog.rst b/docs/changelog.rst index f653d366..9e226f96 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -64,6 +64,9 @@ Mitogen for Ansible * `#343 `_: the sudo ``--login`` option is supported. +* `#344 `_: connections no longer + fail when the parent machine's logged in username contains slashes. + * Runs with many targets executed the module dependency scanner redundantly due to missing synchronization, causing significant wasted computation in the connection multiplexer subprocess. For one real-world playbook the scanner @@ -101,6 +104,7 @@ Thanks! Mitogen would not be possible without the support of users. A huge thanks for the bug reports in this release contributed by `Alex Russu `_, +`atoom `_, `Dan Quackenbush `_, `Jesse London `_, `Luca Nunzi `_, diff --git a/mitogen/parent.py b/mitogen/parent.py index 14e0ef6d..5ff6ced8 100644 --- a/mitogen/parent.py +++ b/mitogen/parent.py @@ -93,6 +93,19 @@ def get_core_source(): return inspect.getsource(mitogen.core) +def get_default_remote_name(): + """ + Return the default name appearing in argv[0] of remote machines. + """ + s = u'%s@%s:%d' + s %= (getpass.getuser(), socket.gethostname(), os.getpid()) + # In mixed UNIX/Windows environments, the username may contain slashes. + return s.translate({ + ord(u'\\'): ord(u'_'), + ord(u'/'): ord(u'_') + }) + + def is_immediate_child(msg, stream): """ Handler policy that requires messages to arrive only from immediately @@ -765,8 +778,7 @@ class Stream(mitogen.core.Stream): if connect_timeout: self.connect_timeout = connect_timeout if remote_name is None: - remote_name = '%s@%s:%d' - remote_name %= (getpass.getuser(), socket.gethostname(), os.getpid()) + remote_name = get_default_remote_name() if '/' in remote_name or '\\' in remote_name: raise ValueError('remote_name= cannot contain slashes') self.remote_name = remote_name diff --git a/tests/parent_test.py b/tests/parent_test.py index 06eac97e..0b8b5e9a 100644 --- a/tests/parent_test.py +++ b/tests/parent_test.py @@ -5,6 +5,7 @@ import sys import tempfile import time +import mock import unittest2 import testlib @@ -28,6 +29,21 @@ def wait_for_child(pid, timeout=1.0): assert False, "wait_for_child() timed out" +class GetDefaultRemoteNameTest(testlib.TestCase): + func = staticmethod(mitogen.parent.get_default_remote_name) + + @mock.patch('os.getpid') + @mock.patch('getpass.getuser') + @mock.patch('socket.gethostname') + def test_slashes(self, mock_gethostname, mock_getuser, mock_getpid): + # Ensure slashes appearing in the remote name are replaced with + # underscores. + mock_gethostname.return_value = 'box' + mock_getuser.return_value = 'ECORP\\Administrator' + mock_getpid.return_value = 123 + self.assertEquals("ECORP_Administrator@box:123", self.func()) + + class ReapChildTest(testlib.RouterMixin, testlib.TestCase): def test_connect_timeout(self): # Ensure the child process is reaped if the connection times out.