[Python-checkins] bpo-35537: Add setsid parameter to os.posix_spawn() and os.posix_spawnp() (GH-11608)

Victor Stinner webhook-mailer at python.org
Fri Feb 1 05:05:35 EST 2019


https://github.com/python/cpython/commit/80c5dfe74b4402d0a220c9227f262ec6fde1d7fc
commit: 80c5dfe74b4402d0a220c9227f262ec6fde1d7fc
branch: master
author: Joannah Nanjekye <33177550+nanjekyejoannah at users.noreply.github.com>
committer: Victor Stinner <vstinner at redhat.com>
date: 2019-02-01T11:05:22+01:00
summary:

bpo-35537: Add setsid parameter to os.posix_spawn() and os.posix_spawnp() (GH-11608)

files:
A Misc/NEWS.d/next/Library/2019-01-18-13-44-13.bpo-35537.R1lbTl.rst
M Doc/library/os.rst
M Lib/test/test_posix.py
M Modules/clinic/posixmodule.c.h
M Modules/posixmodule.c

diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index 2aa51f875d9e..aa1316b1f24a 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -3386,7 +3386,7 @@ written in Python, such as a mail server's external command delivery program.
 
 
 .. function:: posix_spawn(path, argv, env, *, file_actions=None, \
-                          setpgroup=None, resetids=False, setsigmask=(), \
+                          setpgroup=None, resetids=False, setsid=False, setsigmask=(), \
                           setsigdef=(), scheduler=None)
 
    Wraps the :c:func:`posix_spawn` C library API for use from Python.
@@ -3444,6 +3444,11 @@ written in Python, such as a mail server's external command delivery program.
    setting of the effective UID and GID. This argument corresponds to the C
    library :c:data:`POSIX_SPAWN_RESETIDS` flag.
 
+   If the *setsid* argument is ``True``, it will create a new session ID
+   for `posix_spawn`. *setsid* requires :c:data:`POSIX_SPAWN_SETSID`
+   or :c:data:`POSIX_SPAWN_SETSID_NP` flag. Otherwise, :exc:`NotImplementedError`
+   is raised.
+
    The *setsigmask* argument will set the signal mask to the signal set
    specified. If the parameter is not used, then the child inherits the
    parent's signal mask. This argument corresponds to the C library
@@ -3462,9 +3467,10 @@ written in Python, such as a mail server's external command delivery program.
 
    .. versionadded:: 3.7
 
+   .. availability:: Unix.
 
 .. function:: posix_spawnp(path, argv, env, *, file_actions=None, \
-                          setpgroup=None, resetids=False, setsigmask=(), \
+                          setpgroup=None, resetids=False, setsid=False, setsigmask=(), \
                           setsigdef=(), scheduler=None)
 
    Wraps the :c:func:`posix_spawnp` C library API for use from Python.
@@ -3475,6 +3481,8 @@ written in Python, such as a mail server's external command delivery program.
 
    .. versionadded:: 3.8
 
+   .. availability:: See :func:`posix_spawn` documentation.
+
 
 .. function:: register_at_fork(*, before=None, after_in_parent=None, \
                                after_in_child=None)
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index 165b83713408..bc7a2d6fb3dc 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -1624,6 +1624,22 @@ def test_setsigmask_wrong_type(self):
                             os.environ, setsigmask=[signal.NSIG,
                                                     signal.NSIG+1])
 
+    def test_start_new_session(self):
+        # For code coverage of calling setsid().  We don't care if we get an
+        # EPERM error from it depending on the test execution environment, that
+        # still indicates that it was called.
+        code = "import os; print(os.getpgid(os.getpid()))"
+        try:
+            self.spawn_func(sys.executable,
+                            [sys.executable, "-c", code],
+                            os.environ, setsid=True)
+        except NotImplementedError as exc:
+            self.skipTest("setsid is not supported: %s" % exc)
+        else:
+            parent_pgid = os.getpgid(os.getpid())
+            child_pgid = int(output)
+            self.assertNotEqual(parent_pgid, child_pgid)
+
     @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
                          'need signal.pthread_sigmask()')
     def test_setsigdef(self):
diff --git a/Misc/NEWS.d/next/Library/2019-01-18-13-44-13.bpo-35537.R1lbTl.rst b/Misc/NEWS.d/next/Library/2019-01-18-13-44-13.bpo-35537.R1lbTl.rst
new file mode 100644
index 000000000000..56f23a179a4b
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-01-18-13-44-13.bpo-35537.R1lbTl.rst
@@ -0,0 +1 @@
+:func:`os.posix_spawn` and :func:`os.posix_spawnp` now have a *setsid* parameter.
\ No newline at end of file
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
index ce17709c38ba..d47618ce25c0 100644
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -1726,8 +1726,8 @@ os_execve(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *k
 
 PyDoc_STRVAR(os_posix_spawn__doc__,
 "posix_spawn($module, path, argv, env, /, *, file_actions=(),\n"
-"            setpgroup=None, resetids=False, setsigmask=(),\n"
-"            setsigdef=(), scheduler=None)\n"
+"            setpgroup=None, resetids=False, setsid=False,\n"
+"            setsigmask=(), setsigdef=(), scheduler=None)\n"
 "--\n"
 "\n"
 "Execute the program specified by path in a new process.\n"
@@ -1743,7 +1743,9 @@ PyDoc_STRVAR(os_posix_spawn__doc__,
 "  setpgroup\n"
 "    The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.\n"
 "  resetids\n"
-"    If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.\n"
+"    If the value is `true` the POSIX_SPAWN_RESETIDS will be activated.\n"
+"  setsid\n"
+"    If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.\n"
 "  setsigmask\n"
 "    The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n"
 "  setsigdef\n"
@@ -1757,30 +1759,32 @@ PyDoc_STRVAR(os_posix_spawn__doc__,
 static PyObject *
 os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
                     PyObject *env, PyObject *file_actions,
-                    PyObject *setpgroup, int resetids, PyObject *setsigmask,
-                    PyObject *setsigdef, PyObject *scheduler);
+                    PyObject *setpgroup, int resetids, int setsid,
+                    PyObject *setsigmask, PyObject *setsigdef,
+                    PyObject *scheduler);
 
 static PyObject *
 os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
     PyObject *return_value = NULL;
-    static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsigmask", "setsigdef", "scheduler", NULL};
-    static _PyArg_Parser _parser = {"O&OO|$OOiOOO:posix_spawn", _keywords, 0};
+    static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsid", "setsigmask", "setsigdef", "scheduler", NULL};
+    static _PyArg_Parser _parser = {"O&OO|$OOiiOOO:posix_spawn", _keywords, 0};
     path_t path = PATH_T_INITIALIZE("posix_spawn", "path", 0, 0);
     PyObject *argv;
     PyObject *env;
     PyObject *file_actions = NULL;
     PyObject *setpgroup = NULL;
     int resetids = 0;
+    int setsid = 0;
     PyObject *setsigmask = NULL;
     PyObject *setsigdef = NULL;
     PyObject *scheduler = NULL;
 
     if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        path_converter, &path, &argv, &env, &file_actions, &setpgroup, &resetids, &setsigmask, &setsigdef, &scheduler)) {
+        path_converter, &path, &argv, &env, &file_actions, &setpgroup, &resetids, &setsid, &setsigmask, &setsigdef, &scheduler)) {
         goto exit;
     }
-    return_value = os_posix_spawn_impl(module, &path, argv, env, file_actions, setpgroup, resetids, setsigmask, setsigdef, scheduler);
+    return_value = os_posix_spawn_impl(module, &path, argv, env, file_actions, setpgroup, resetids, setsid, setsigmask, setsigdef, scheduler);
 
 exit:
     /* Cleanup for path */
@@ -1795,8 +1799,8 @@ os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje
 
 PyDoc_STRVAR(os_posix_spawnp__doc__,
 "posix_spawnp($module, path, argv, env, /, *, file_actions=(),\n"
-"             setpgroup=None, resetids=False, setsigmask=(),\n"
-"             setsigdef=(), scheduler=None)\n"
+"             setpgroup=None, resetids=False, setsid=False,\n"
+"             setsigmask=(), setsigdef=(), scheduler=None)\n"
 "--\n"
 "\n"
 "Execute the program specified by path in a new process.\n"
@@ -1813,6 +1817,8 @@ PyDoc_STRVAR(os_posix_spawnp__doc__,
 "    The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.\n"
 "  resetids\n"
 "    If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.\n"
+"  setsid\n"
+"    If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.\n"
 "  setsigmask\n"
 "    The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n"
 "  setsigdef\n"
@@ -1826,30 +1832,32 @@ PyDoc_STRVAR(os_posix_spawnp__doc__,
 static PyObject *
 os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv,
                      PyObject *env, PyObject *file_actions,
-                     PyObject *setpgroup, int resetids, PyObject *setsigmask,
-                     PyObject *setsigdef, PyObject *scheduler);
+                     PyObject *setpgroup, int resetids, int setsid,
+                     PyObject *setsigmask, PyObject *setsigdef,
+                     PyObject *scheduler);
 
 static PyObject *
 os_posix_spawnp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
     PyObject *return_value = NULL;
-    static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsigmask", "setsigdef", "scheduler", NULL};
-    static _PyArg_Parser _parser = {"O&OO|$OOiOOO:posix_spawnp", _keywords, 0};
+    static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsid", "setsigmask", "setsigdef", "scheduler", NULL};
+    static _PyArg_Parser _parser = {"O&OO|$OOiiOOO:posix_spawnp", _keywords, 0};
     path_t path = PATH_T_INITIALIZE("posix_spawnp", "path", 0, 0);
     PyObject *argv;
     PyObject *env;
     PyObject *file_actions = NULL;
     PyObject *setpgroup = NULL;
     int resetids = 0;
+    int setsid = 0;
     PyObject *setsigmask = NULL;
     PyObject *setsigdef = NULL;
     PyObject *scheduler = NULL;
 
     if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
-        path_converter, &path, &argv, &env, &file_actions, &setpgroup, &resetids, &setsigmask, &setsigdef, &scheduler)) {
+        path_converter, &path, &argv, &env, &file_actions, &setpgroup, &resetids, &setsid, &setsigmask, &setsigdef, &scheduler)) {
         goto exit;
     }
-    return_value = os_posix_spawnp_impl(module, &path, argv, env, file_actions, setpgroup, resetids, setsigmask, setsigdef, scheduler);
+    return_value = os_posix_spawnp_impl(module, &path, argv, env, file_actions, setpgroup, resetids, setsid, setsigmask, setsigdef, scheduler);
 
 exit:
     /* Cleanup for path */
@@ -7331,4 +7339,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
 #ifndef OS_GETRANDOM_METHODDEF
     #define OS_GETRANDOM_METHODDEF
 #endif /* !defined(OS_GETRANDOM_METHODDEF) */
-/*[clinic end generated code: output=dabd0fa27bf87044 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d50ff73e5b5198b9 input=a9049054013a1b77]*/
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index b25b5220cdb3..e516b5bed33a 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -5166,10 +5166,11 @@ convert_sched_param(PyObject *param, struct sched_param *res);
 #endif
 
 static int
-parse_posix_spawn_flags(PyObject *setpgroup, int resetids, PyObject *setsigmask,
+parse_posix_spawn_flags(PyObject *setpgroup, int resetids, int setsid, PyObject *setsigmask,
                         PyObject *setsigdef, PyObject *scheduler,
                         posix_spawnattr_t *attrp)
 {
+    const char *func_name = "posix_spawnp";
     long all_flags = 0;
 
     errno = posix_spawnattr_init(attrp);
@@ -5195,6 +5196,17 @@ parse_posix_spawn_flags(PyObject *setpgroup, int resetids, PyObject *setsigmask,
         all_flags |= POSIX_SPAWN_RESETIDS;
     }
 
+    if (setsid) {
+#ifdef POSIX_SPAWN_SETSID
+        all_flags |= POSIX_SPAWN_SETSID;
+#elif defined(POSIX_SPAWN_SETSID_NP)
+        all_flags |= POSIX_SPAWN_SETSID_NP;
+#else
+        argument_unavailable_error(func_name, "setsid");
+        return -1;
+#endif
+    }
+
    if (setsigmask) {
         sigset_t set;
         if (!_Py_Sigset_Converter(setsigmask, &set)) {
@@ -5385,7 +5397,7 @@ parse_file_actions(PyObject *file_actions,
 static PyObject *
 py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *argv,
                PyObject *env, PyObject *file_actions,
-               PyObject *setpgroup, int resetids, PyObject *setsigmask,
+               PyObject *setpgroup, int resetids, int setsid, PyObject *setsigmask,
                PyObject *setsigdef, PyObject *scheduler)
 {
     EXECV_CHAR **argvlist = NULL;
@@ -5400,7 +5412,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a
     pid_t pid;
     int err_code;
 
-    /* posix_spawn has three arguments: (path, argv, env), where
+    /* posix_spawn and posix_spawnp have three arguments: (path, argv, env), where
        argv is a list or tuple of strings and env is a dictionary
        like posix.environ. */
 
@@ -5455,7 +5467,7 @@ py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *a
         file_actionsp = &file_actions_buf;
     }
 
-    if (parse_posix_spawn_flags(setpgroup, resetids, setsigmask,
+    if (parse_posix_spawn_flags(setpgroup, resetids, setsid, setsigmask,
                                 setsigdef, scheduler, &attr)) {
         goto exit;
     }
@@ -5519,7 +5531,9 @@ os.posix_spawn
     setpgroup: object = NULL
         The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.
     resetids: bool(accept={int}) = False
-        If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.
+        If the value is `true` the POSIX_SPAWN_RESETIDS will be activated.
+    setsid: bool(accept={int}) = False
+        If the value is `true` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.
     setsigmask: object(c_default='NULL') = ()
         The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.
     setsigdef: object(c_default='NULL') = ()
@@ -5533,12 +5547,13 @@ Execute the program specified by path in a new process.
 static PyObject *
 os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
                     PyObject *env, PyObject *file_actions,
-                    PyObject *setpgroup, int resetids, PyObject *setsigmask,
-                    PyObject *setsigdef, PyObject *scheduler)
-/*[clinic end generated code: output=45dfa4c515d09f2c input=2891c2f1d457e39b]*/
+                    PyObject *setpgroup, int resetids, int setsid,
+                    PyObject *setsigmask, PyObject *setsigdef,
+                    PyObject *scheduler)
+/*[clinic end generated code: output=14a1098c566bc675 input=8c6305619a00ad04]*/
 {
     return py_posix_spawn(0, module, path, argv, env, file_actions,
-                          setpgroup, resetids, setsigmask, setsigdef,
+                          setpgroup, resetids, setsid, setsigmask, setsigdef,
                           scheduler);
 }
  #endif /* HAVE_POSIX_SPAWN */
@@ -5563,6 +5578,8 @@ os.posix_spawnp
         The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.
     resetids: bool(accept={int}) = False
         If the value is `True` the POSIX_SPAWN_RESETIDS will be activated.
+    setsid: bool(accept={int}) = False
+        If the value is `True` the POSIX_SPAWN_SETSID or POSIX_SPAWN_SETSID_NP will be activated.
     setsigmask: object(c_default='NULL') = ()
         The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.
     setsigdef: object(c_default='NULL') = ()
@@ -5576,12 +5593,13 @@ Execute the program specified by path in a new process.
 static PyObject *
 os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv,
                      PyObject *env, PyObject *file_actions,
-                     PyObject *setpgroup, int resetids, PyObject *setsigmask,
-                     PyObject *setsigdef, PyObject *scheduler)
-/*[clinic end generated code: output=7955dc0edc82b8c3 input=b7576eb25b1ed9eb]*/
+                     PyObject *setpgroup, int resetids, int setsid,
+                     PyObject *setsigmask, PyObject *setsigdef,
+                     PyObject *scheduler)
+/*[clinic end generated code: output=7b9aaefe3031238d input=c1911043a22028da]*/
 {
     return py_posix_spawn(1, module, path, argv, env, file_actions,
-                          setpgroup, resetids, setsigmask, setsigdef,
+                          setpgroup, resetids, setsid, setsigmask, setsigdef,
                           scheduler);
 }
 #endif /* HAVE_POSIX_SPAWNP */



More information about the Python-checkins mailing list