Index: Doc/library/socket.rst =================================================================== --- Doc/library/socket.rst (revision 85382) +++ Doc/library/socket.rst (working copy) @@ -157,7 +157,22 @@ :func:`socket`. (Only :const:`SOCK_STREAM` and :const:`SOCK_DGRAM` appear to be generally useful.) +.. data:: SOCK_CLOEXEC + SOCK_NONBLOCK + These two constants, if defined, can be combined with the socket types and + allow you to set some flags atomically (thus avoiding possible race + conditions and the need for separate calls). + + .. seealso:: + + `Secure File Descriptor Handling ` + for a more thorough explanation. + + Availability: Linux >= 2.6.27. + + .. versionadded:: 3.2 + .. data:: SO_* SOMAXCONN MSG_* Index: Lib/test/test_socket.py =================================================================== --- Lib/test/test_socket.py (revision 85382) +++ Lib/test/test_socket.py (working copy) @@ -17,6 +17,10 @@ from weakref import proxy import signal import math +try: + import fcntl +except ImportError: + fcntl = False def try_address(host, port=0, family=socket.AF_INET): """Try to bind a socket on the given host:port and return True @@ -1753,7 +1757,59 @@ self.assertTrue(sock._closed) self.assertRaises(socket.error, sock.sendall, b'foo') +@unittest.skipUnless(hasattr(socket, "SOCK_CLOEXEC"), + "SOCK_CLOEXEC not defined") +@unittest.skipUnless(fcntl, "module fcntl not available") +class CloexecConstantTest(unittest.TestCase): + def test_SOCK_CLOEXEC(self): + s = socket.socket(socket.AF_INET, + socket.SOCK_STREAM | socket.SOCK_CLOEXEC) + self.assertTrue(s.type & socket.SOCK_CLOEXEC) + self.assertTrue(fcntl.fcntl(s, fcntl.F_GETFD) & fcntl.FD_CLOEXEC) + +@unittest.skipUnless(hasattr(socket, "SOCK_CLOEXEC"), + "SOCK_CLOEXEC not defined") +@unittest.skipUnless(hasattr(socket, "SOCK_NONBLOCK"), + "SOCK_NONBLOCK not defined") +class NonblockConstantTest(unittest.TestCase): + def checkNonblock(self, s, nonblock=True, timeout=0.0): + if nonblock: + self.assertTrue(s.type & socket.SOCK_NONBLOCK) + self.assertEqual(s.gettimeout(), timeout) + else: + self.assertFalse(s.type & socket.SOCK_NONBLOCK) + self.assertEqual(s.gettimeout(), None) + + def test_SOCK_NONBLOCK(self): + # a lot of it seems silly and redundant, but I wanted to test that + # changing back and forth worked ok + s = socket.socket(socket.AF_INET, + socket.SOCK_STREAM | socket.SOCK_NONBLOCK) + self.checkNonblock(s) + s.setblocking(1) + self.checkNonblock(s, False) + s.setblocking(0) + self.checkNonblock(s) + s.settimeout(None) + self.checkNonblock(s, False) + s.settimeout(2.0) + self.checkNonblock(s, timeout=2.0) + s.setblocking(1) + self.checkNonblock(s, False) + # defaulttimeout + t = socket.getdefaulttimeout() + socket.setdefaulttimeout(0.0) + self.checkNonblock(socket.socket()) + socket.setdefaulttimeout(None) + self.checkNonblock(socket.socket(), False) + socket.setdefaulttimeout(2.0) + self.checkNonblock(socket.socket(), timeout=2.0) + socket.setdefaulttimeout(None) + self.checkNonblock(socket.socket(), False) + socket.setdefaulttimeout(t) + + def test_main(): tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, TestExceptions, BufferIOTest, BasicTCPTest2, BasicUDPTest, UDPTimeoutTest ] @@ -1769,6 +1825,8 @@ NetworkConnectionAttributesTest, NetworkConnectionBehaviourTest, ContextManagersTest, + CloexecConstantTest, + NonblockConstantTest ]) if hasattr(socket, "socketpair"): tests.append(BasicSocketPairTest) Index: Modules/socketmodule.c =================================================================== --- Modules/socketmodule.c (revision 85382) +++ Modules/socketmodule.c (working copy) @@ -615,6 +615,12 @@ #ifndef MS_WINDOWS int delay_flag; #endif +#ifdef SOCK_NONBLOCK + if (block) + s->sock_type &= (~SOCK_NONBLOCK); + else + s->sock_type |= SOCK_NONBLOCK; +#endif Py_BEGIN_ALLOW_THREADS #ifndef MS_WINDOWS @@ -767,10 +773,17 @@ s->sock_timeout = defaulttimeout; s->errorhandler = &set_error; +#ifdef SOCK_NONBLOCK + if (type & SOCK_NONBLOCK) + s->sock_timeout = 0.0; + else +#endif + { + s->sock_timeout = defaulttimeout; + if (defaulttimeout >= 0.0) + internal_setblocking(s, 0); + } - if (defaulttimeout >= 0.0) - internal_setblocking(s, 0); - } @@ -1645,7 +1658,9 @@ PyObject *addr = NULL; PyObject *res = NULL; int timeout; - +#ifdef SOCK_CLOEXEC + int flags = 0; +#endif if (!getsockaddrlen(s, &addrlen)) return NULL; memset(&addrbuf, 0, addrlen); @@ -1656,8 +1671,15 @@ BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS timeout = internal_select_ex(s, 0, interval); - if (!timeout) + if (!timeout) { +#ifdef SOCK_CLOEXEC + // inherit socket flags and use accept4 call + flags = s->sock_type & (SOCK_CLOEXEC | SOCK_NONBLOCK); + newfd = accept4(s->sock_fd, SAS2SA(&addrbuf), &addrlen, flags); +#else newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen); +#endif //SOCK_CLOEXEC + } Py_END_ALLOW_THREADS if (timeout == 1) { @@ -4599,6 +4621,12 @@ #if defined(SOCK_RDM) PyModule_AddIntConstant(m, "SOCK_RDM", SOCK_RDM); #endif +#ifdef SOCK_CLOEXEC + PyModule_AddIntConstant(m, "SOCK_CLOEXEC", SOCK_CLOEXEC); +#endif +#ifdef SOCK_NONBLOCK + PyModule_AddIntConstant(m, "SOCK_NONBLOCK", SOCK_NONBLOCK); +#endif #ifdef SO_DEBUG PyModule_AddIntConstant(m, "SO_DEBUG", SO_DEBUG);