Skip to content

Commit 7483451

Browse files
authored
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.
1 parent be143ec commit 7483451

File tree

6 files changed

+153
-1
lines changed

6 files changed

+153
-1
lines changed

Doc/library/signal.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,19 @@ The :mod:`signal` module defines the following functions:
247247
.. versionadded:: 3.8
248248

249249

250+
.. function:: pidfd_send_signal(pidfd, sig, siginfo=None, flags=0)
251+
252+
Send signal *sig* to the process referred to by file descriptor *pidfd*.
253+
Python does not currently support the *siginfo* parameter; it must be
254+
``None``. The *flags* argument is provided for future extensions; no flag
255+
values are currently defined.
256+
257+
See the :manpage:`pidfd_send_signal(2)` man page for more information.
258+
259+
.. availability:: Linux 5.1+
260+
.. versionadded:: 3.9
261+
262+
250263
.. function:: pthread_kill(thread_id, signalnum)
251264

252265
Send the signal *signalnum* to the thread *thread_id*, another thread in the

Doc/whatsnew/3.9.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,13 @@ now raises :exc:`ImportError` instead of :exc:`ValueError` for invalid relative
197197
import attempts.
198198
(Contributed by Ngalim Siregar in :issue:`37444`.)
199199

200+
signal
201+
------
202+
203+
Exposed the Linux-specific :func:`signal.pidfd_send_signal` for sending to
204+
signals to a process using a file descriptor instead of a pid. (:issue:`38712`)
205+
206+
200207
Optimizations
201208
=============
202209

Lib/test/test_signal.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,25 @@ def handler(a, b):
12731273
self.assertTrue(is_ok)
12741274

12751275

1276+
class PidfdSignalTest(unittest.TestCase):
1277+
1278+
@unittest.skipUnless(
1279+
hasattr(signal, "pidfd_send_signal"),
1280+
"pidfd support not built in",
1281+
)
1282+
def test_pidfd_send_signal(self):
1283+
with self.assertRaises(OSError) as cm:
1284+
signal.pidfd_send_signal(0, signal.SIGINT)
1285+
if cm.exception.errno == errno.ENOSYS:
1286+
self.skipTest("kernel does not support pidfds")
1287+
self.assertEqual(cm.exception.errno, errno.EBADF)
1288+
my_pidfd = os.open(f'/proc/{os.getpid()}', os.O_DIRECTORY)
1289+
self.addCleanup(os.close, my_pidfd)
1290+
with self.assertRaisesRegexp(TypeError, "^siginfo must be None$"):
1291+
signal.pidfd_send_signal(my_pidfd, signal.SIGINT, object(), 0)
1292+
with self.assertRaises(KeyboardInterrupt):
1293+
signal.pidfd_send_signal(my_pidfd, signal.SIGINT)
1294+
12761295
def tearDownModule():
12771296
support.reap_children()
12781297

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add the Linux-specific :func:`signal.pidfd_send_signal` function, which
2+
allows sending a signal to a process identified by a file descriptor rather
3+
than a pid.

Modules/clinic/signalmodule.c.h

Lines changed: 75 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/signalmodule.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#ifdef HAVE_SIGNAL_H
2626
#include <signal.h>
2727
#endif
28+
#ifdef HAVE_SYS_SYSCALL_H
29+
#include <sys/syscall.h>
30+
#endif
2831
#ifdef HAVE_SYS_STAT_H
2932
#include <sys/stat.h>
3033
#endif
@@ -1250,6 +1253,38 @@ signal_pthread_kill_impl(PyObject *module, unsigned long thread_id,
12501253
#endif /* #if defined(HAVE_PTHREAD_KILL) */
12511254

12521255

1256+
#if defined(__linux__) && defined(__NR_pidfd_send_signal)
1257+
/*[clinic input]
1258+
signal.pidfd_send_signal
1259+
1260+
pidfd: int
1261+
signalnum: int
1262+
siginfo: object = None
1263+
flags: int = 0
1264+
/
1265+
1266+
Send a signal to a process referred to by a pid file descriptor.
1267+
[clinic start generated code]*/
1268+
1269+
static PyObject *
1270+
signal_pidfd_send_signal_impl(PyObject *module, int pidfd, int signalnum,
1271+
PyObject *siginfo, int flags)
1272+
/*[clinic end generated code: output=2d59f04a75d9cbdf input=2a6543a1f4ac2000]*/
1273+
1274+
{
1275+
if (siginfo != Py_None) {
1276+
PyErr_SetString(PyExc_TypeError, "siginfo must be None");
1277+
return NULL;
1278+
}
1279+
if (syscall(__NR_pidfd_send_signal, pidfd, signalnum, NULL, flags) < 0) {
1280+
PyErr_SetFromErrno(PyExc_OSError);
1281+
return NULL;
1282+
}
1283+
Py_RETURN_NONE;
1284+
}
1285+
#endif
1286+
1287+
12531288

12541289
/* List of functions defined in the module -- some of the methoddefs are
12551290
defined to nothing if the corresponding C function is not available. */
@@ -1265,6 +1300,7 @@ static PyMethodDef signal_methods[] = {
12651300
{"set_wakeup_fd", (PyCFunction)(void(*)(void))signal_set_wakeup_fd, METH_VARARGS | METH_KEYWORDS, set_wakeup_fd_doc},
12661301
SIGNAL_SIGINTERRUPT_METHODDEF
12671302
SIGNAL_PAUSE_METHODDEF
1303+
SIGNAL_PIDFD_SEND_SIGNAL_METHODDEF
12681304
SIGNAL_PTHREAD_KILL_METHODDEF
12691305
SIGNAL_PTHREAD_SIGMASK_METHODDEF
12701306
SIGNAL_SIGPENDING_METHODDEF

0 commit comments

Comments
 (0)