python3/backport-20104-Add-flag-capabilities-to-posix_spawn-GH-66.patch
2021-05-25 05:10:34 -04:00

461 lines
17 KiB
Diff

From 52628b241a1559840479e72c32214cf4a6b4a92f Mon Sep 17 00:00:00 2001
From: Pablo Galindo <Pablogsal@gmail.com>
Date: Fri, 7 Sep 2018 16:44:24 +0100
Subject: [PATCH] bpo-20104: Add flag capabilities to posix_spawn (GH-6693)
Implement the "attributes objects" parameter of `os.posix_spawn` to complete the implementation and fully cover the underlying API.
Conflict:NA
Reference:https://github.com/python/cpython/commit/254a4663d8c5970ae2928185c50ebaa6c7e62c80
Signed-off-by: hanxinke <hanxinke@huawei.com>
---
Lib/test/test_posix.py | 143 ++++++++++++++++++
.../2018-05-05-23-26-58.bpo-20104.tDBciE.rst | 2 +
Modules/clinic/posixmodule.c.h | 37 ++++-
Modules/posixmodule.c | 140 ++++++++++++++++-
4 files changed, 310 insertions(+), 12 deletions(-)
create mode 100644 Misc/NEWS.d/next/Core and Builtins/2018-05-05-23-26-58.bpo-20104.tDBciE.rst
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index a22baac..1103ff3 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -8,6 +8,7 @@ posix = support.import_module('posix')
import errno
import sys
+import signal
import time
import os
import platform
@@ -16,6 +17,7 @@ import stat
import tempfile
import unittest
import warnings
+import textwrap
_DUMMY_SYMLINK = os.path.join(tempfile.gettempdir(),
support.TESTFN + '-dummy-symlink')
@@ -1558,6 +1560,147 @@ class TestPosixSpawn(unittest.TestCase):
)
self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+ def test_resetids_explicit_default(self):
+ pid = posix.posix_spawn(
+ sys.executable,
+ [sys.executable, '-c', 'pass'],
+ os.environ,
+ resetids=False
+ )
+ self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+
+ def test_resetids(self):
+ pid = posix.posix_spawn(
+ sys.executable,
+ [sys.executable, '-c', 'pass'],
+ os.environ,
+ resetids=True
+ )
+ self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+
+ def test_resetids_wrong_type(self):
+ with self.assertRaises(TypeError):
+ posix.posix_spawn(sys.executable,
+ [sys.executable, "-c", "pass"],
+ os.environ, resetids=None)
+
+ def test_setpgroup(self):
+ pid = posix.posix_spawn(
+ sys.executable,
+ [sys.executable, '-c', 'pass'],
+ os.environ,
+ setpgroup=os.getpgrp()
+ )
+ self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+
+ def test_setpgroup_wrong_type(self):
+ with self.assertRaises(TypeError):
+ posix.posix_spawn(sys.executable,
+ [sys.executable, "-c", "pass"],
+ os.environ, setpgroup="023")
+
+ @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
+ 'need signal.pthread_sigmask()')
+ def test_setsigmask(self):
+ code = textwrap.dedent("""\
+ import _testcapi, signal
+ _testcapi.raise_signal(signal.SIGUSR1)""")
+
+ pid = posix.posix_spawn(
+ sys.executable,
+ [sys.executable, '-c', code],
+ os.environ,
+ setsigmask=[signal.SIGUSR1]
+ )
+ self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+
+ def test_setsigmask_wrong_type(self):
+ with self.assertRaises(TypeError):
+ posix.posix_spawn(sys.executable,
+ [sys.executable, "-c", "pass"],
+ os.environ, setsigmask=34)
+ with self.assertRaises(TypeError):
+ posix.posix_spawn(sys.executable,
+ [sys.executable, "-c", "pass"],
+ os.environ, setsigmask=["j"])
+ with self.assertRaises(ValueError):
+ posix.posix_spawn(sys.executable,
+ [sys.executable, "-c", "pass"],
+ os.environ, setsigmask=[signal.NSIG,
+ signal.NSIG+1])
+
+ @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
+ 'need signal.pthread_sigmask()')
+ def test_setsigdef(self):
+ original_handler = signal.signal(signal.SIGUSR1, signal.SIG_IGN)
+ code = textwrap.dedent("""\
+ import _testcapi, signal
+ _testcapi.raise_signal(signal.SIGUSR1)""")
+ try:
+ pid = posix.posix_spawn(
+ sys.executable,
+ [sys.executable, '-c', code],
+ os.environ,
+ setsigdef=[signal.SIGUSR1]
+ )
+ finally:
+ signal.signal(signal.SIGUSR1, original_handler)
+
+ pid2, status = os.waitpid(pid, 0)
+ self.assertEqual(pid2, pid)
+ self.assertTrue(os.WIFSIGNALED(status), status)
+ self.assertEqual(os.WTERMSIG(status), signal.SIGUSR1)
+
+ def test_setsigdef_wrong_type(self):
+ with self.assertRaises(TypeError):
+ posix.posix_spawn(sys.executable,
+ [sys.executable, "-c", "pass"],
+ os.environ, setsigdef=34)
+ with self.assertRaises(TypeError):
+ posix.posix_spawn(sys.executable,
+ [sys.executable, "-c", "pass"],
+ os.environ, setsigdef=["j"])
+ with self.assertRaises(ValueError):
+ posix.posix_spawn(sys.executable,
+ [sys.executable, "-c", "pass"],
+ os.environ, setsigdef=[signal.NSIG, signal.NSIG+1])
+
+ @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler")
+ def test_setscheduler_only_param(self):
+ policy = os.sched_getscheduler(0)
+ priority = os.sched_get_priority_min(policy)
+ code = textwrap.dedent(f"""\
+ import os
+ if os.sched_getscheduler(0) != {policy}:
+ os.exit(101)
+ if os.sched_getparam(0).sched_priority != {priority}:
+ os.exit(102)""")
+ pid = posix.posix_spawn(
+ sys.executable,
+ [sys.executable, '-c', code],
+ os.environ,
+ scheduler=(None, os.sched_param(priority))
+ )
+ self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+
+ @unittest.skipUnless(hasattr(posix, 'sched_setscheduler'), "can't change scheduler")
+ def test_setscheduler_with_policy(self):
+ policy = os.sched_getscheduler(0)
+ priority = os.sched_get_priority_min(policy)
+ code = textwrap.dedent(f"""\
+ import os
+ if os.sched_getscheduler(0) != {policy}:
+ os.exit(101)
+ if os.sched_getparam(0).sched_priority != {priority}:
+ os.exit(102)""")
+ pid = posix.posix_spawn(
+ sys.executable,
+ [sys.executable, '-c', code],
+ os.environ,
+ scheduler=(policy, os.sched_param(priority))
+ )
+ self.assertEqual(os.waitpid(pid, 0), (pid, 0))
+
def test_multiple_file_actions(self):
file_actions = [
(os.POSIX_SPAWN_OPEN, 3, os.path.realpath(__file__), os.O_RDONLY, 0),
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-05-05-23-26-58.bpo-20104.tDBciE.rst b/Misc/NEWS.d/next/Core and Builtins/2018-05-05-23-26-58.bpo-20104.tDBciE.rst
new file mode 100644
index 0000000..1d725ba
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-05-05-23-26-58.bpo-20104.tDBciE.rst
@@ -0,0 +1,2 @@
+Added support for the `setpgroup`, `resetids`, `setsigmask`, `setsigdef` and
+`scheduler` parameters of `posix_spawn`. Patch by Pablo Galindo.
diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h
index 1371ef6..a3b5a8b 100644
--- a/Modules/clinic/posixmodule.c.h
+++ b/Modules/clinic/posixmodule.c.h
@@ -1730,7 +1730,9 @@ exit:
#if defined(HAVE_POSIX_SPAWN)
PyDoc_STRVAR(os_posix_spawn__doc__,
-"posix_spawn($module, path, argv, env, file_actions=None, /)\n"
+"posix_spawn($module, path, argv, env, file_actions=None, /, *,\n"
+" setpgroup=None, resetids=False, setsigmask=(),\n"
+" setsigdef=(), scheduler=None)\n"
"--\n"
"\n"
"Execute the program specified by path in a new process.\n"
@@ -1742,29 +1744,48 @@ PyDoc_STRVAR(os_posix_spawn__doc__,
" env\n"
" Dictionary of strings mapping to strings.\n"
" file_actions\n"
-" A sequence of file action tuples.");
+" A sequence of file action tuples.\n"
+" 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"
+" setsigmask\n"
+" The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.\n"
+" setsigdef\n"
+" The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag.\n"
+" scheduler\n"
+" A tuple with the scheduler policy (optional) and parameters.");
#define OS_POSIX_SPAWN_METHODDEF \
- {"posix_spawn", (PyCFunction)os_posix_spawn, METH_FASTCALL, os_posix_spawn__doc__},
+ {"posix_spawn", (PyCFunction)os_posix_spawn, METH_FASTCALL|METH_KEYWORDS, os_posix_spawn__doc__},
static PyObject *
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 *setsigdef, PyObject *scheduler);
static PyObject *
-os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+os_posix_spawn(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"", "", "", "", "setpgroup", "resetids", "setsigmask", "setsigdef", "scheduler", NULL};
+ static _PyArg_Parser _parser = {"O&OO|O$OiOOO:posix_spawn", _keywords, 0};
path_t path = PATH_T_INITIALIZE("posix_spawn", "path", 0, 0);
PyObject *argv;
PyObject *env;
PyObject *file_actions = Py_None;
+ PyObject *setpgroup = NULL;
+ int resetids = 0;
+ PyObject *setsigmask = NULL;
+ PyObject *setsigdef = NULL;
+ PyObject *scheduler = NULL;
- if (!_PyArg_ParseStack(args, nargs, "O&OO|O:posix_spawn",
- path_converter, &path, &argv, &env, &file_actions)) {
+ if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
+ path_converter, &path, &argv, &env, &file_actions, &setpgroup, &resetids, &setsigmask, &setsigdef, &scheduler)) {
goto exit;
}
- return_value = os_posix_spawn_impl(module, &path, argv, env, file_actions);
+ return_value = os_posix_spawn_impl(module, &path, argv, env, file_actions, setpgroup, resetids, setsigmask, setsigdef, scheduler);
exit:
/* Cleanup for path */
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 441c82e..a1808f6 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -5138,6 +5138,114 @@ enum posix_spawn_file_actions_identifier {
POSIX_SPAWN_DUP2
};
+static int
+convert_sched_param(PyObject *param, struct sched_param *res);
+
+static int
+parse_posix_spawn_flags(PyObject *setpgroup, int resetids, PyObject *setsigmask,
+ PyObject *setsigdef, PyObject *scheduler,
+ posix_spawnattr_t *attrp)
+{
+ long all_flags = 0;
+
+ errno = posix_spawnattr_init(attrp);
+ if (errno) {
+ posix_error();
+ return -1;
+ }
+
+ if (setpgroup) {
+ pid_t pgid = PyLong_AsPid(setpgroup);
+ if (pgid == (pid_t)-1 && PyErr_Occurred()) {
+ goto fail;
+ }
+ errno = posix_spawnattr_setpgroup(attrp, pgid);
+ if (errno) {
+ posix_error();
+ goto fail;
+ }
+ all_flags |= POSIX_SPAWN_SETPGROUP;
+ }
+
+ if (resetids) {
+ all_flags |= POSIX_SPAWN_RESETIDS;
+ }
+
+ if (setsigmask) {
+ sigset_t set;
+ if (!_Py_Sigset_Converter(setsigmask, &set)) {
+ goto fail;
+ }
+ errno = posix_spawnattr_setsigmask(attrp, &set);
+ if (errno) {
+ posix_error();
+ goto fail;
+ }
+ all_flags |= POSIX_SPAWN_SETSIGMASK;
+ }
+
+ if (setsigdef) {
+ sigset_t set;
+ if (!_Py_Sigset_Converter(setsigdef, &set)) {
+ goto fail;
+ }
+ errno = posix_spawnattr_setsigdefault(attrp, &set);
+ if (errno) {
+ posix_error();
+ goto fail;
+ }
+ all_flags |= POSIX_SPAWN_SETSIGDEF;
+ }
+
+ if (scheduler) {
+#ifdef POSIX_SPAWN_SETSCHEDULER
+ PyObject *py_schedpolicy;
+ struct sched_param schedparam;
+
+ if (!PyArg_ParseTuple(scheduler, "OO&"
+ ";A scheduler tuple must have two elements",
+ &py_schedpolicy, convert_sched_param, &schedparam)) {
+ goto fail;
+ }
+ if (py_schedpolicy != Py_None) {
+ int schedpolicy = _PyLong_AsInt(py_schedpolicy);
+
+ if (schedpolicy == -1 && PyErr_Occurred()) {
+ goto fail;
+ }
+ errno = posix_spawnattr_setschedpolicy(attrp, schedpolicy);
+ if (errno) {
+ posix_error();
+ goto fail;
+ }
+ all_flags |= POSIX_SPAWN_SETSCHEDULER;
+ }
+ errno = posix_spawnattr_setschedparam(attrp, &schedparam);
+ if (errno) {
+ posix_error();
+ goto fail;
+ }
+ all_flags |= POSIX_SPAWN_SETSCHEDPARAM;
+#else
+ PyErr_SetString(PyExc_NotImplementedError,
+ "The scheduler option is not supported in this system.");
+ goto fail;
+#endif
+ }
+
+ errno = posix_spawnattr_setflags(attrp, all_flags);
+ if (errno) {
+ posix_error();
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ (void)posix_spawnattr_destroy(attrp);
+ return -1;
+}
+
static int
parse_file_actions(PyObject *file_actions,
posix_spawn_file_actions_t *file_actionsp,
@@ -5238,6 +5346,7 @@ parse_file_actions(PyObject *file_actions,
}
Py_DECREF(file_action);
}
+
Py_DECREF(seq);
return 0;
@@ -5260,19 +5369,33 @@ os.posix_spawn
file_actions: object = None
A sequence of file action tuples.
/
-
+ *
+ 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.
+ setsigmask: object(c_default='NULL') = ()
+ The sigmask to use with the POSIX_SPAWN_SETSIGMASK flag.
+ setsigdef: object(c_default='NULL') = ()
+ The sigmask to use with the POSIX_SPAWN_SETSIGDEF flag.
+ scheduler: object = NULL
+ A tuple with the scheduler policy (optional) and parameters.
Execute the program specified by path in a new process.
[clinic start generated code]*/
static PyObject *
os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
- PyObject *env, PyObject *file_actions)
-/*[clinic end generated code: output=d023521f541c709c input=a3db1021d33230dc]*/
+ PyObject *env, PyObject *file_actions,
+ PyObject *setpgroup, int resetids, PyObject *setsigmask,
+ PyObject *setsigdef, PyObject *scheduler)
+/*[clinic end generated code: output=45dfa4c515d09f2c input=2d7a7578430a90f0]*/
{
EXECV_CHAR **argvlist = NULL;
EXECV_CHAR **envlist = NULL;
posix_spawn_file_actions_t file_actions_buf;
posix_spawn_file_actions_t *file_actionsp = NULL;
+ posix_spawnattr_t attr;
+ posix_spawnattr_t *attrp = NULL;
Py_ssize_t argc, envc;
PyObject *result = NULL;
PyObject *temp_buffer = NULL;
@@ -5334,9 +5457,15 @@ os_posix_spawn_impl(PyObject *module, path_t *path, PyObject *argv,
file_actionsp = &file_actions_buf;
}
+ if (parse_posix_spawn_flags(setpgroup, resetids, setsigmask,
+ setsigdef, scheduler, &attr)) {
+ goto exit;
+ }
+ attrp = &attr;
+
_Py_BEGIN_SUPPRESS_IPH
err_code = posix_spawn(&pid, path->narrow,
- file_actionsp, NULL, argvlist, envlist);
+ file_actionsp, attrp, argvlist, envlist);
_Py_END_SUPPRESS_IPH
if (err_code) {
errno = err_code;
@@ -5349,6 +5478,9 @@ exit:
if (file_actionsp) {
(void)posix_spawn_file_actions_destroy(file_actionsp);
}
+ if (attrp) {
+ (void)posix_spawnattr_destroy(attrp);
+ }
if (envlist) {
free_string_array(envlist, envc);
}
--
2.23.0