[Python-checkins] closes bpo-38712: Add signal.pidfd_send_signal. (GH-17070)

Benjamin Peterson webhook-mailer at python.org
Tue Nov 19 23:39:21 EST 2019


https://github.com/python/cpython/commit/7483451577916e693af6d20cf520b2cc7e2174d2
commit: 7483451577916e693af6d20cf520b2cc7e2174d2
branch: master
author: Benjamin Peterson <benjamin at python.org>
committer: GitHub <noreply at github.com>
date: 2019-11-19T20:39:14-08:00
summary:

closes bpo-38712: Add signal.pidfd_send_signal. (GH-17070)

This exposes a Linux-specific syscall for sending a signal to a process
identified by a file descriptor rather than a pid.

For simplicity, we don't support the siginfo_t parameter to the syscall. This
parameter allows implementing a pidfd version of rt_sigqueueinfo(2), which
Python also doesn't support.

files:
A Misc/NEWS.d/next/Library/2019-11-05-21-10-12.bpo-38712.ezJ0TP.rst
M Doc/library/signal.rst
M Doc/whatsnew/3.9.rst
M Lib/test/test_signal.py
M Modules/clinic/signalmodule.c.h
M Modules/signalmodule.c

diff --git a/Doc/library/signal.rst b/Doc/library/signal.rst
index 8fecc2b7eed0e..a79fc501352d4 100644
--- a/Doc/library/signal.rst
+++ b/Doc/library/signal.rst
@@ -247,6 +247,19 @@ The :mod:`signal` module defines the following functions:
    .. versionadded:: 3.8
 
 
+.. function:: pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)
+
+   Send signal *sig* to the process referred to by file descriptor *pidfd*.
+   Python does not currently support the *siginfo* parameter; it must be
+   ``None``.  The *flags* argument is provided for future extensions; no flag
+   values are currently defined.
+
+   See the :manpage:`pidfd_send_signal(2)` man page for more information.
+
+   .. availability:: Linux 5.1+
+   .. versionadded:: 3.9
+
+
 .. function:: pthread_kill(thread_id, signalnum)
 
    Send the signal *signalnum* to the thread *thread_id*, another thread in the
diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst
index ddb295f845c75..ce1d3e0e96a58 100644
--- a/Doc/whatsnew/3.9.rst
+++ b/Doc/whatsnew/3.9.rst
@@ -197,6 +197,13 @@ now raises :exc:`ImportError` instead of :exc:`ValueError` for invalid relative
 import attempts.
 (Contributed by Ngalim Siregar in :issue:`37444`.)
 
+signal
+------
+
+Exposed the Linux-specific :func:`signal.pidfd_send_signal` for sending to
+signals to a process using a file descriptor instead of a pid. (:issue:`38712`)
+
+
 Optimizations
 =============
 
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
index d41e94b07f430..119e9e079ac03 100644
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -1273,6 +1273,25 @@ def handler(a, b):
         self.assertTrue(is_ok)
 
 
+class PidfdSignalTest(unittest.TestCase):
+
+    @unittest.skipUnless(
+        hasattr(signal, "pidfd_send_signal"),
+        "pidfd support not built in",
+    )
+    def test_pidfd_send_signal(self):
+        with self.assertRaises(OSError) as cm:
+            signal.pidfd_send_signal(0, signal.SIGINT)
+        if cm.exception.errno == errno.ENOSYS:
+            self.skipTest("kernel does not support pidfds")
+        self.assertEqual(cm.exception.errno, errno.EBADF)
+        my_pidfd = os.open(f'/proc/{os.getpid()}', os.O_DIRECTORY)
+        self.addCleanup(os.close, my_pidfd)
+        with self.assertRaisesRegexp(TypeError, "^siginfo must be None$"):
+            signal.pidfd_send_signal(my_pidfd, signal.SIGINT, object(), 0)
+        with self.assertRaises(KeyboardInterrupt):
+            signal.pidfd_send_signal(my_pidfd, signal.SIGINT)
+
 def tearDownModule():
     support.reap_children()
 
diff --git a/Misc/NEWS.d/next/Library/2019-11-05-21-10-12.bpo-38712.ezJ0TP.rst b/Misc/NEWS.d/next/Library/2019-11-05-21-10-12.bpo-38712.ezJ0TP.rst
new file mode 100644
index 0000000000000..81d01aa7111d4
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-11-05-21-10-12.bpo-38712.ezJ0TP.rst
@@ -0,0 +1,3 @@
+Add the Linux-specific :func:`signal.pidfd_send_signal` function, which
+allows sending a signal to a process identified by a file descriptor rather
+than a pid.
diff --git a/Modules/clinic/signalmodule.c.h b/Modules/clinic/signalmodule.c.h
index 3cb1db14878c5..7f60e28a3a28a 100644
--- a/Modules/clinic/signalmodule.c.h
+++ b/Modules/clinic/signalmodule.c.h
@@ -611,6 +611,76 @@ signal_pthread_kill(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
 
 #endif /* defined(HAVE_PTHREAD_KILL) */
 
+#if (defined(__linux__) && defined(__NR_pidfd_send_signal))
+
+PyDoc_STRVAR(signal_pidfd_send_signal__doc__,
+"pidfd_send_signal($module, pidfd, signalnum, siginfo=None, flags=0, /)\n"
+"--\n"
+"\n"
+"Send a signal to a process referred to by a pid file descriptor.");
+
+#define SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF    \
+    {"pidfd_send_signal", (PyCFunction)(void(*)(void))signal_pidfd_send_signal, METH_FASTCALL, signal_pidfd_send_signal__doc__},
+
+static PyObject *
+signal_pidfd_send_signal_impl(PyObject *module, int pidfd, int signalnum,
+                              PyObject *siginfo, int flags);
+
+static PyObject *
+signal_pidfd_send_signal(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+    PyObject *return_value = NULL;
+    int pidfd;
+    int signalnum;
+    PyObject *siginfo = Py_None;
+    int flags = 0;
+
+    if (!_PyArg_CheckPositional("pidfd_send_signal", nargs, 2, 4)) {
+        goto exit;
+    }
+    if (PyFloat_Check(args[0])) {
+        PyErr_SetString(PyExc_TypeError,
+                        "integer argument expected, got float" );
+        goto exit;
+    }
+    pidfd = _PyLong_AsInt(args[0]);
+    if (pidfd == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    if (PyFloat_Check(args[1])) {
+        PyErr_SetString(PyExc_TypeError,
+                        "integer argument expected, got float" );
+        goto exit;
+    }
+    signalnum = _PyLong_AsInt(args[1]);
+    if (signalnum == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+    if (nargs < 3) {
+        goto skip_optional;
+    }
+    siginfo = args[2];
+    if (nargs < 4) {
+        goto skip_optional;
+    }
+    if (PyFloat_Check(args[3])) {
+        PyErr_SetString(PyExc_TypeError,
+                        "integer argument expected, got float" );
+        goto exit;
+    }
+    flags = _PyLong_AsInt(args[3]);
+    if (flags == -1 && PyErr_Occurred()) {
+        goto exit;
+    }
+skip_optional:
+    return_value = signal_pidfd_send_signal_impl(module, pidfd, signalnum, siginfo, flags);
+
+exit:
+    return return_value;
+}
+
+#endif /* (defined(__linux__) && defined(__NR_pidfd_send_signal)) */
+
 #ifndef SIGNAL_ALARM_METHODDEF
     #define SIGNAL_ALARM_METHODDEF
 #endif /* !defined(SIGNAL_ALARM_METHODDEF) */
@@ -658,4 +728,8 @@ signal_pthread_kill(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
 #ifndef SIGNAL_PTHREAD_KILL_METHODDEF
     #define SIGNAL_PTHREAD_KILL_METHODDEF
 #endif /* !defined(SIGNAL_PTHREAD_KILL_METHODDEF) */
-/*[clinic end generated code: output=3320b8f73c20ba60 input=a9049054013a1b77]*/
+
+#ifndef SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF
+    #define SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF
+#endif /* !defined(SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF) */
+/*[clinic end generated code: output=b41b4b6bd9ad4da2 input=a9049054013a1b77]*/
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index 1d99f877e1620..693b90b6c631e 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -25,6 +25,9 @@
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
 #endif
+#ifdef HAVE_SYS_SYSCALL_H
+#include <sys/syscall.h>
+#endif
 #ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
@@ -1250,6 +1253,38 @@ signal_pthread_kill_impl(PyObject *module, unsigned long thread_id,
 #endif   /* #if defined(HAVE_PTHREAD_KILL) */
 
 
+#if defined(__linux__) && defined(__NR_pidfd_send_signal)
+/*[clinic input]
+signal.pidfd_send_signal
+
+    pidfd: int
+    signalnum: int
+    siginfo: object = None
+    flags: int = 0
+    /
+
+Send a signal to a process referred to by a pid file descriptor.
+[clinic start generated code]*/
+
+static PyObject *
+signal_pidfd_send_signal_impl(PyObject *module, int pidfd, int signalnum,
+                              PyObject *siginfo, int flags)
+/*[clinic end generated code: output=2d59f04a75d9cbdf input=2a6543a1f4ac2000]*/
+
+{
+    if (siginfo != Py_None) {
+        PyErr_SetString(PyExc_TypeError, "siginfo must be None");
+        return NULL;
+    }
+    if (syscall(__NR_pidfd_send_signal, pidfd, signalnum, NULL, flags) < 0) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+    Py_RETURN_NONE;
+}
+#endif
+
+
 
 /* List of functions defined in the module -- some of the methoddefs are
    defined to nothing if the corresponding C function is not available. */
@@ -1265,6 +1300,7 @@ static PyMethodDef signal_methods[] = {
     {"set_wakeup_fd", (PyCFunction)(void(*)(void))signal_set_wakeup_fd, METH_VARARGS | METH_KEYWORDS, set_wakeup_fd_doc},
     SIGNAL_SIGINTERRUPT_METHODDEF
     SIGNAL_PAUSE_METHODDEF
+    SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF
     SIGNAL_PTHREAD_KILL_METHODDEF
     SIGNAL_PTHREAD_SIGMASK_METHODDEF
     SIGNAL_SIGPENDING_METHODDEF



More information about the Python-checkins mailing list