.. 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
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
=============
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()
--- /dev/null
+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.
#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) */
#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]*/
#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
#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. */
{"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