*, sock=None, backlog=100, ssl=None, \
ssl_handshake_timeout=None, \
ssl_shutdown_timeout=None, \
- start_serving=True, cleanup_socket=True)
+ start_serving=True, cleanup_socket=True, mode=None)
:async:
Similar to :meth:`loop.create_server` but works with the
be removed from the filesystem when the server is closed, unless the
socket has been replaced after the server has been created.
+ If *mode* is not ``None``, the permissions of the socket file created
+ for *path* are changed to *mode* (as accepted by :func:`os.chmod`)
+ right after binding, before the server starts accepting connections,
+ so a connection can never be accepted while the default,
+ umask-derived permissions are still in effect. *mode* cannot be
+ combined with *sock* and is not supported for abstract Unix sockets.
+
See the documentation of the :meth:`loop.create_server` method
for information about arguments to this method.
Added the *cleanup_socket* parameter.
+ .. versionchanged:: 3.16
+
+ Added the *mode* parameter.
+
.. method:: loop.connect_accepted_socket(protocol_factory, \
sock, *, ssl=None, ssl_handshake_timeout=None, \
.. function:: start_unix_server(client_connected_cb, path=None, \
*, limit=None, sock=None, backlog=100, ssl=None, \
ssl_handshake_timeout=None, \
- ssl_shutdown_timeout=None, start_serving=True, cleanup_socket=True)
+ ssl_shutdown_timeout=None, start_serving=True, \
+ cleanup_socket=True, mode=None)
:async:
Start a Unix socket server.
be removed from the filesystem when the server is closed, unless the
socket has been replaced after the server has been created.
+ If *mode* is not ``None``, the permissions of the Unix socket file
+ are set to *mode* before the server starts accepting connections.
+
See also the documentation of :meth:`loop.create_unix_server`.
.. note::
.. versionchanged:: 3.13
Added the *cleanup_socket* parameter.
+ .. versionchanged:: 3.16
+ Added the *mode* parameter.
+
StreamReader
============
Improved modules
================
+asyncio
+-------
+
+* Add the *mode* parameter to :meth:`asyncio.loop.create_unix_server` and
+ :func:`asyncio.start_unix_server` to set the permissions of the Unix
+ socket file created for *path*.
+ (Contributed by Sam Bull in :gh:`94984`.)
+
+
codecs
------
sock=None, backlog=100, ssl=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
- start_serving=True):
+ start_serving=True, mode=None):
"""A coroutine which creates a UNIX Domain Socket server.
The return value is a Server object, which can be used to stop
the user should await Server.start_serving() or
Server.serve_forever() to make the server to start accepting
connections.
+
+ mode, if not None, is applied to the socket file created for
+ path with os.chmod() after binding and before the server
+ starts accepting connections.
"""
raise NotImplementedError
sock=None, backlog=100, ssl=None,
ssl_handshake_timeout=None,
ssl_shutdown_timeout=None,
- start_serving=True, cleanup_socket=True):
+ start_serving=True, cleanup_socket=True, mode=None):
if isinstance(ssl, bool):
raise TypeError('ssl argument must be an SSLContext or None')
'path and sock can not be specified at the same time')
path = os.fspath(path)
+ if mode is not None and path and path[0] in (0, '\x00'):
+ raise ValueError(
+ 'mode is not supported for abstract sockets')
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Check for abstract socket. `str` and `bytes` paths are supported.
except:
sock.close()
raise
+
+ if mode is not None:
+ # The socket cannot accept connections until listen() is
+ # called, which happens later in Server._start_serving(),
+ # so no connection can be accepted while the socket still
+ # has the default permissions.
+ try:
+ os.chmod(path, mode)
+ except:
+ sock.close()
+ raise
else:
if sock is None:
raise ValueError(
'path was not specified, and no sock specified')
+ if mode is not None:
+ raise ValueError(
+ 'mode is only meaningful with path')
+
if (sock.family != socket.AF_UNIX or
sock.type != socket.SOCK_STREAM):
raise ValueError(
self.loop.run_until_complete(coro)
self.assertTrue(sock.close.called)
+ @socket_helper.skip_unless_bind_unix_socket
+ def test_create_unix_server_mode(self):
+ # Two distinct modes: whatever the umask, at most one of them
+ # can coincide with the default permissions, so a no-op chmod
+ # cannot pass both subtests.
+ for mode in (0o600, 0o644):
+ with self.subTest(mode=mode):
+ with test_utils.unix_socket_path() as path:
+ srv = self.loop.run_until_complete(
+ self.loop.create_unix_server(
+ lambda: None, path, mode=mode))
+ try:
+ self.assertEqual(
+ stat.S_IMODE(os.stat(path).st_mode), mode)
+ finally:
+ srv.close()
+ self.loop.run_until_complete(srv.wait_closed())
+
+ def test_create_unix_server_mode_sock(self):
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ with sock:
+ coro = self.loop.create_unix_server(lambda: None, path=None,
+ sock=sock, mode=0o600)
+ with self.assertRaisesRegex(ValueError,
+ 'mode is only meaningful with path'):
+ self.loop.run_until_complete(coro)
+
+ def test_create_unix_server_mode_abstract(self):
+ # The check is a pure string test, so it runs on all platforms.
+ for path in ('\x00spam', b'\x00spam'):
+ with self.subTest(path=path):
+ coro = self.loop.create_unix_server(lambda: None, path,
+ mode=0o600)
+ with self.assertRaisesRegex(
+ ValueError, 'mode is not supported for abstract'):
+ self.loop.run_until_complete(coro)
+
+ @mock.patch('asyncio.unix_events.socket')
+ def test_create_unix_server_chmod_error(self, m_socket):
+ # Ensure that the socket is closed when os.chmod() fails
+ sock = mock.Mock()
+ m_socket.socket.return_value = sock
+
+ with mock.patch('asyncio.unix_events.os.chmod',
+ side_effect=PermissionError):
+ coro = self.loop.create_unix_server(lambda: None, path='/test',
+ mode=0o600)
+ with self.assertRaises(PermissionError):
+ self.loop.run_until_complete(coro)
+ self.assertTrue(sock.close.called)
+
def test_create_unix_connection_path_sock(self):
coro = self.loop.create_unix_connection(
lambda: None, os.devnull, sock=object())
--- /dev/null
+Add the *mode* parameter to :meth:`asyncio.loop.create_unix_server` and
+:func:`asyncio.start_unix_server` to set the permissions of the Unix
+socket file created for *path*, applied before the server starts
+accepting connections.