From 80c5dfe74b4402d0a220c9227f262ec6fde1d7fc Mon Sep 17 00:00:00 2001 From: Joannah Nanjekye <33177550+nanjekyejoannah@users.noreply.github.com> Date: Fri, 1 Feb 2019 13:05:22 +0300 Subject: [PATCH] bpo-35537: Add setsid parameter to os.posix_spawn() and os.posix_spawnp() (GH-11608) --- Doc/library/os.rst | 12 ++++- Lib/test/test_posix.py | 16 +++++++ .../2019-01-18-13-44-13.bpo-35537.R1lbTl.rst | 1 + Modules/clinic/posixmodule.c.h | 44 +++++++++++-------- Modules/posixmodule.c | 44 +++++++++++++------ 5 files changed, 84 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2019-01-18-13-44-13.bpo-35537.R1lbTl.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 2aa51f875d9..aa1316b1f24 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 165b8371340..bc7a2d6fb3d 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1624,6 +1624,22 @@ class _PosixSpawnMixin: 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 00000000000..56f23a179a4 --- /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 ce17709c38b..d47618ce25c 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -1726,8 +1726,8 @@ exit: 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 @@ exit: 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 @@ exit: #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 b25b5220cdb..e516b5bed33 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 @@ fail: 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 */