diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py index 1f3ea504fff439..6b92cf8a128f63 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -102,7 +102,7 @@ def address_type(address): return 'AF_INET' elif type(address) is str and address.startswith('\\\\'): return 'AF_PIPE' - elif type(address) is str: + elif type(address) is str or util.is_abstract_socket_namespace(address): return 'AF_UNIX' else: raise ValueError('address type of %r unrecognized' % address) @@ -587,7 +587,8 @@ def __init__(self, address, family, backlog=1): self._family = family self._last_accepted = None - if family == 'AF_UNIX': + if family == 'AF_UNIX' and not util.is_abstract_socket_namespace(address): + # Linux abstract socket namespaces do not need to be explicitly unlinked self._unlink = util.Finalize( self, os.unlink, args=(address,), exitpriority=0 ) diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py index 040b46e66a0330..ffd1735848c455 100644 --- a/Lib/multiprocessing/forkserver.py +++ b/Lib/multiprocessing/forkserver.py @@ -116,7 +116,8 @@ def ensure_running(self): with socket.socket(socket.AF_UNIX) as listener: address = connection.arbitrary_address('AF_UNIX') listener.bind(address) - os.chmod(address, 0o600) + if not util.is_abstract_socket_namespace(address): + os.chmod(address, 0o600) listener.listen() # all client processes own the write end of the "alive" pipe; diff --git a/Lib/multiprocessing/util.py b/Lib/multiprocessing/util.py index 0f9eac7e9fb4dc..f5331abe821fb1 100644 --- a/Lib/multiprocessing/util.py +++ b/Lib/multiprocessing/util.py @@ -102,6 +102,29 @@ def log_to_stderr(level=None): _log_to_stderr = True return _logger + +# Abstract socket support + +def _platform_supports_abstract_sockets(): + if sys.platform == "linux": + return True + if hasattr(sys, 'getandroidapilevel'): + return True + return False + + +def is_abstract_socket_namespace(address): + if not address: + return False + if isinstance(address, bytes): + return address[0] == 0 + elif isinstance(address, str): + return address[0] == "\0" + raise TypeError('address type of {address!r} unrecognized') + + +abstract_sockets_supported = _platform_supports_abstract_sockets() + # # Function returning a temp directory which will be removed on exit # diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 18947170cbcdc3..30ea23da694991 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -3140,6 +3140,19 @@ def test_context(self): if self.TYPE == 'processes': self.assertRaises(OSError, l.accept) + @unittest.skipUnless(util.abstract_sockets_supported, + "test needs abstract socket support") + def test_abstract_socket(self): + with self.connection.Listener("\0something") as listener: + with self.connection.Client(listener.address) as client: + with listener.accept() as d: + client.send(1729) + self.assertEqual(d.recv(), 1729) + + if self.TYPE == 'processes': + self.assertRaises(OSError, listener.accept) + + class _TestListenerClient(BaseTestCase): ALLOWED_TYPES = ('processes', 'threads') diff --git a/Misc/NEWS.d/next/Library/2020-03-09-01-45-06.bpo-39850.eaJNIE.rst b/Misc/NEWS.d/next/Library/2020-03-09-01-45-06.bpo-39850.eaJNIE.rst new file mode 100644 index 00000000000000..57f98f5f0d6fb8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-03-09-01-45-06.bpo-39850.eaJNIE.rst @@ -0,0 +1,2 @@ +:mod:`multiprocessing` now supports abstract socket addresses (if abstract sockets +are supported in the running platform). Patch by Pablo Galindo.