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

This commit is contained in:
Joannah Nanjekye 2019-02-01 13:05:22 +03:00 committed by Victor Stinner
parent 05f1b93f58
commit 80c5dfe74b
5 changed files with 84 additions and 33 deletions

View file

@ -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, \ .. function:: posix_spawn(path, argv, env, *, file_actions=None, \
setpgroup=None, resetids=False, setsigmask=(), \ setpgroup=None, resetids=False, setsid=False, setsigmask=(), \
setsigdef=(), scheduler=None) setsigdef=(), scheduler=None)
Wraps the :c:func:`posix_spawn` C library API for use from Python. 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 setting of the effective UID and GID. This argument corresponds to the C
library :c:data:`POSIX_SPAWN_RESETIDS` flag. 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 The *setsigmask* argument will set the signal mask to the signal set
specified. If the parameter is not used, then the child inherits the specified. If the parameter is not used, then the child inherits the
parent's signal mask. This argument corresponds to the C library 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 .. versionadded:: 3.7
.. availability:: Unix.
.. function:: posix_spawnp(path, argv, env, *, file_actions=None, \ .. function:: posix_spawnp(path, argv, env, *, file_actions=None, \
setpgroup=None, resetids=False, setsigmask=(), \ setpgroup=None, resetids=False, setsid=False, setsigmask=(), \
setsigdef=(), scheduler=None) setsigdef=(), scheduler=None)
Wraps the :c:func:`posix_spawnp` C library API for use from Python. 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 .. versionadded:: 3.8
.. availability:: See :func:`posix_spawn` documentation.
.. function:: register_at_fork(*, before=None, after_in_parent=None, \ .. function:: register_at_fork(*, before=None, after_in_parent=None, \
after_in_child=None) after_in_child=None)

View file

@ -1624,6 +1624,22 @@ class _PosixSpawnMixin:
os.environ, setsigmask=[signal.NSIG, os.environ, setsigmask=[signal.NSIG,
signal.NSIG+1]) 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'), @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()') 'need signal.pthread_sigmask()')
def test_setsigdef(self): def test_setsigdef(self):

View file

@ -0,0 +1 @@
:func:`os.posix_spawn` and :func:`os.posix_spawnp` now have a *setsid* parameter.

View file

@ -1726,8 +1726,8 @@ exit:
PyDoc_STRVAR(os_posix_spawn__doc__, PyDoc_STRVAR(os_posix_spawn__doc__,
"posix_spawn($module, path, argv, env, /, *, file_actions=(),\n" "posix_spawn($module, path, argv, env, /, *, file_actions=(),\n"
" setpgroup=None, resetids=False, setsigmask=(),\n" " setpgroup=None, resetids=False, setsid=False,\n"
" setsigdef=(), scheduler=None)\n" " setsigmask=(), setsigdef=(), scheduler=None)\n"
"--\n" "--\n"
"\n" "\n"
"Execute the program specified by path in a new process.\n" "Execute the program specified by path in a new process.\n"
@ -1743,7 +1743,9 @@ PyDoc_STRVAR(os_posix_spawn__doc__,
" setpgroup\n" " setpgroup\n"
" The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.\n" " The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.\n"
" resetids\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" " setsigmask\n"
" The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n" " The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n"
" setsigdef\n" " setsigdef\n"
@ -1757,30 +1759,32 @@ PyDoc_STRVAR(os_posix_spawn__doc__,
static PyObject * static PyObject *
os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
PyObject *env, PyObject *file_actions, PyObject *env, PyObject *file_actions,
PyObject *setpgroup, int resetids, PyObject *setsigmask, PyObject *setpgroup, int resetids, int setsid,
PyObject *setsigdef, PyObject *scheduler); PyObject *setsigmask, PyObject *setsigdef,
PyObject *scheduler);
static PyObject * static PyObject *
os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsigmask", "setsigdef", "scheduler", NULL}; static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsid", "setsigmask", "setsigdef", "scheduler", NULL};
static _PyArg_Parser _parser = {"O&OO|$OOiOOO:posix_spawn", _keywords, 0}; static _PyArg_Parser _parser = {"O&OO|$OOiiOOO:posix_spawn", _keywords, 0};
path_t path = PATH_T_INITIALIZE("posix_spawn", "path", 0, 0); path_t path = PATH_T_INITIALIZE("posix_spawn", "path", 0, 0);
PyObject *argv; PyObject *argv;
PyObject *env; PyObject *env;
PyObject *file_actions = NULL; PyObject *file_actions = NULL;
PyObject *setpgroup = NULL; PyObject *setpgroup = NULL;
int resetids = 0; int resetids = 0;
int setsid = 0;
PyObject *setsigmask = NULL; PyObject *setsigmask = NULL;
PyObject *setsigdef = NULL; PyObject *setsigdef = NULL;
PyObject *scheduler = NULL; PyObject *scheduler = NULL;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 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; 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: exit:
/* Cleanup for path */ /* Cleanup for path */
@ -1795,8 +1799,8 @@ exit:
PyDoc_STRVAR(os_posix_spawnp__doc__, PyDoc_STRVAR(os_posix_spawnp__doc__,
"posix_spawnp($module, path, argv, env, /, *, file_actions=(),\n" "posix_spawnp($module, path, argv, env, /, *, file_actions=(),\n"
" setpgroup=None, resetids=False, setsigmask=(),\n" " setpgroup=None, resetids=False, setsid=False,\n"
" setsigdef=(), scheduler=None)\n" " setsigmask=(), setsigdef=(), scheduler=None)\n"
"--\n" "--\n"
"\n" "\n"
"Execute the program specified by path in a new process.\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" " The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.\n"
" resetids\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" " setsigmask\n"
" The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n" " The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n"
" setsigdef\n" " setsigdef\n"
@ -1826,30 +1832,32 @@ PyDoc_STRVAR(os_posix_spawnp__doc__,
static PyObject * static PyObject *
os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv, os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv,
PyObject *env, PyObject *file_actions, PyObject *env, PyObject *file_actions,
PyObject *setpgroup, int resetids, PyObject *setsigmask, PyObject *setpgroup, int resetids, int setsid,
PyObject *setsigdef, PyObject *scheduler); PyObject *setsigmask, PyObject *setsigdef,
PyObject *scheduler);
static PyObject * static PyObject *
os_posix_spawnp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) os_posix_spawnp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsigmask", "setsigdef", "scheduler", NULL}; static const char * const _keywords[] = {"", "", "", "file_actions", "setpgroup", "resetids", "setsid", "setsigmask", "setsigdef", "scheduler", NULL};
static _PyArg_Parser _parser = {"O&OO|$OOiOOO:posix_spawnp", _keywords, 0}; static _PyArg_Parser _parser = {"O&OO|$OOiiOOO:posix_spawnp", _keywords, 0};
path_t path = PATH_T_INITIALIZE("posix_spawnp", "path", 0, 0); path_t path = PATH_T_INITIALIZE("posix_spawnp", "path", 0, 0);
PyObject *argv; PyObject *argv;
PyObject *env; PyObject *env;
PyObject *file_actions = NULL; PyObject *file_actions = NULL;
PyObject *setpgroup = NULL; PyObject *setpgroup = NULL;
int resetids = 0; int resetids = 0;
int setsid = 0;
PyObject *setsigmask = NULL; PyObject *setsigmask = NULL;
PyObject *setsigdef = NULL; PyObject *setsigdef = NULL;
PyObject *scheduler = NULL; PyObject *scheduler = NULL;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, 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; 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: exit:
/* Cleanup for path */ /* Cleanup for path */
@ -7331,4 +7339,4 @@ exit:
#ifndef OS_GETRANDOM_METHODDEF #ifndef OS_GETRANDOM_METHODDEF
#define OS_GETRANDOM_METHODDEF #define OS_GETRANDOM_METHODDEF
#endif /* !defined(OS_GETRANDOM_METHODDEF) */ #endif /* !defined(OS_GETRANDOM_METHODDEF) */
/*[clinic end generated code: output=dabd0fa27bf87044 input=a9049054013a1b77]*/ /*[clinic end generated code: output=d50ff73e5b5198b9 input=a9049054013a1b77]*/

View file

@ -5166,10 +5166,11 @@ convert_sched_param(PyObject *param, struct sched_param *res);
#endif #endif
static int 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, PyObject *setsigdef, PyObject *scheduler,
posix_spawnattr_t *attrp) posix_spawnattr_t *attrp)
{ {
const char *func_name = "posix_spawnp";
long all_flags = 0; long all_flags = 0;
errno = posix_spawnattr_init(attrp); errno = posix_spawnattr_init(attrp);
@ -5195,6 +5196,17 @@ parse_posix_spawn_flags(PyObject *setpgroup, int resetids, PyObject *setsigmask,
all_flags |= POSIX_SPAWN_RESETIDS; 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) { if (setsigmask) {
sigset_t set; sigset_t set;
if (!_Py_Sigset_Converter(setsigmask, &set)) { if (!_Py_Sigset_Converter(setsigmask, &set)) {
@ -5385,7 +5397,7 @@ fail:
static PyObject * static PyObject *
py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *argv, py_posix_spawn(int use_posix_spawnp, PyObject *module, path_t *path, PyObject *argv,
PyObject *env, PyObject *file_actions, PyObject *env, PyObject *file_actions,
PyObject *setpgroup, int resetids, PyObject *setsigmask, PyObject *setpgroup, int resetids, int setsid, PyObject *setsigmask,
PyObject *setsigdef, PyObject *scheduler) PyObject *setsigdef, PyObject *scheduler)
{ {
EXECV_CHAR **argvlist = NULL; 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; pid_t pid;
int err_code; 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 argv is a list or tuple of strings and env is a dictionary
like posix.environ. */ 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; 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)) { setsigdef, scheduler, &attr)) {
goto exit; goto exit;
} }
@ -5519,7 +5531,9 @@ os.posix_spawn
setpgroup: object = NULL setpgroup: object = NULL
The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.
resetids: bool(accept={int}) = False 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') = () setsigmask: object(c_default='NULL') = ()
The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.
setsigdef: object(c_default='NULL') = () setsigdef: object(c_default='NULL') = ()
@ -5533,12 +5547,13 @@ Execute the program specified by path in a new process.
static PyObject * static PyObject *
os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv, os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
PyObject *env, PyObject *file_actions, PyObject *env, PyObject *file_actions,
PyObject *setpgroup, int resetids, PyObject *setsigmask, PyObject *setpgroup, int resetids, int setsid,
PyObject *setsigdef, PyObject *scheduler) PyObject *setsigmask, PyObject *setsigdef,
/*[clinic end generated code: output=45dfa4c515d09f2c input=2891c2f1d457e39b]*/ PyObject *scheduler)
/*[clinic end generated code: output=14a1098c566bc675 input=8c6305619a00ad04]*/
{ {
return py_posix_spawn(0, module, path, argv, env, file_actions, return py_posix_spawn(0, module, path, argv, env, file_actions,
setpgroup, resetids, setsigmask, setsigdef, setpgroup, resetids, setsid, setsigmask, setsigdef,
scheduler); scheduler);
} }
#endif /* HAVE_POSIX_SPAWN */ #endif /* HAVE_POSIX_SPAWN */
@ -5563,6 +5578,8 @@ os.posix_spawnp
The pgroup to use with the POSIX_SPAWN_SETPGROUP flag. The pgroup to use with the POSIX_SPAWN_SETPGROUP flag.
resetids: bool(accept={int}) = False 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') = () setsigmask: object(c_default='NULL') = ()
The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag. The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.
setsigdef: object(c_default='NULL') = () setsigdef: object(c_default='NULL') = ()
@ -5576,12 +5593,13 @@ Execute the program specified by path in a new process.
static PyObject * static PyObject *
os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv, os_posix_spawnp_impl(PyObject *module, path_t *path, PyObject *argv,
PyObject *env, PyObject *file_actions, PyObject *env, PyObject *file_actions,
PyObject *setpgroup, int resetids, PyObject *setsigmask, PyObject *setpgroup, int resetids, int setsid,
PyObject *setsigdef, PyObject *scheduler) PyObject *setsigmask, PyObject *setsigdef,
/*[clinic end generated code: output=7955dc0edc82b8c3 input=b7576eb25b1ed9eb]*/ PyObject *scheduler)
/*[clinic end generated code: output=7b9aaefe3031238d input=c1911043a22028da]*/
{ {
return py_posix_spawn(1, module, path, argv, env, file_actions, return py_posix_spawn(1, module, path, argv, env, file_actions,
setpgroup, resetids, setsigmask, setsigdef, setpgroup, resetids, setsid, setsigmask, setsigdef,
scheduler); scheduler);
} }
#endif /* HAVE_POSIX_SPAWNP */ #endif /* HAVE_POSIX_SPAWNP */