From 6c4c45efaeb40f4f837570f57d90a0b3429c6ae9 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 5 Nov 2019 19:21:29 -0800 Subject: [PATCH] bpo-38692: Add os.pidfd_open. (GH-17063) --- Doc/library/os.rst | 13 ++++++ Doc/whatsnew/3.9.rst | 3 ++ Lib/test/test_posix.py | 9 ++++ .../2019-11-05-07-18-24.bpo-38692.UpatA7.rst | 1 + Modules/clinic/posixmodule.c.h | 44 ++++++++++++++++++- Modules/posixmodule.c | 25 +++++++++++ 6 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2019-11-05-07-18-24.bpo-38692.UpatA7.rst diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 8e9d9e6f034..9c907a7ee59 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -3539,6 +3539,19 @@ written in Python, such as a mail server's external command delivery program. .. availability:: Unix. +.. function:: pidfd_open(pid, flags=0) + + Return a file descriptor referring to the process *pid*. This descriptor can + be used to perform process management without races and signals. The *flags* + argument is provided for future extensions; no flag values are currently + defined. + + See the :manpage:`pidfd_open(2)` man page for more details. + + .. availability:: Linux 5.3+ + .. versionadded:: 3.9 + + .. function:: plock(op) Lock program segments into memory. The value of *op* (defined in diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 3cac9c5eedb..7e778058c8d 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -150,6 +150,9 @@ os Added :data:`~os.CLD_KILLED` and :data:`~os.CLD_STOPPED` for :attr:`si_code`. (Contributed by Dong-hee Na in :issue:`38493`.) +Exposed the Linux-specific :func:`os.pidfd_open` for process management with +file descriptors. (:issue:`38692`) + threading --------- diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py index 17e4ded2e2d..98a39c3f040 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -1470,6 +1470,15 @@ class PosixTester(unittest.TestCase): open(fn, 'wb').close() self.assertRaises(ValueError, os.stat, fn_with_NUL) + @unittest.skipUnless(hasattr(os, "pidfd_open"), "pidfd_open unavailable") + def test_pidfd_open(self): + with self.assertRaises(OSError) as cm: + os.pidfd_open(-1) + if cm.exception.errno == errno.ENOSYS: + self.skipTest("system does not support pidfd_open") + self.assertEqual(cm.exception.errno, errno.EINVAL) + os.close(os.pidfd_open(os.getpid(), 0)) + class PosixGroupsTester(unittest.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2019-11-05-07-18-24.bpo-38692.UpatA7.rst b/Misc/NEWS.d/next/Library/2019-11-05-07-18-24.bpo-38692.UpatA7.rst new file mode 100644 index 00000000000..fd19c6f9eb5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-11-05-07-18-24.bpo-38692.UpatA7.rst @@ -0,0 +1 @@ +Expose the Linux ``pidfd_open`` syscall as :func:`os.pidfd_open`. diff --git a/Modules/clinic/posixmodule.c.h b/Modules/clinic/posixmodule.c.h index 3dada674fba..aa4756a620a 100644 --- a/Modules/clinic/posixmodule.c.h +++ b/Modules/clinic/posixmodule.c.h @@ -3963,6 +3963,44 @@ os_wait(PyObject *module, PyObject *Py_UNUSED(ignored)) #endif /* defined(HAVE_WAIT) */ +#if (defined(__linux__) && defined(__NR_pidfd_open)) + +PyDoc_STRVAR(os_pidfd_open__doc__, +"pidfd_open($module, /, pid, flags=0)\n" +"--\n" +"\n" +"Return a file descriptor referring to the process *pid*.\n" +"\n" +"The descriptor can be used to perform process management without races and\n" +"signals."); + +#define OS_PIDFD_OPEN_METHODDEF \ + {"pidfd_open", (PyCFunction)(void(*)(void))os_pidfd_open, METH_FASTCALL|METH_KEYWORDS, os_pidfd_open__doc__}, + +static PyObject * +os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags); + +static PyObject * +os_pidfd_open(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"pid", "flags", NULL}; + static _PyArg_Parser _parser = {"" _Py_PARSE_PID "|O&:pidfd_open", _keywords, 0}; + pid_t pid; + unsigned int flags = 0; + + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, + &pid, _PyLong_UnsignedInt_Converter, &flags)) { + goto exit; + } + return_value = os_pidfd_open_impl(module, pid, flags); + +exit: + return return_value; +} + +#endif /* (defined(__linux__) && defined(__NR_pidfd_open)) */ + #if (defined(HAVE_READLINK) || defined(MS_WINDOWS)) PyDoc_STRVAR(os_readlink__doc__, @@ -8480,6 +8518,10 @@ exit: #define OS_WAIT_METHODDEF #endif /* !defined(OS_WAIT_METHODDEF) */ +#ifndef OS_PIDFD_OPEN_METHODDEF + #define OS_PIDFD_OPEN_METHODDEF +#endif /* !defined(OS_PIDFD_OPEN_METHODDEF) */ + #ifndef OS_READLINK_METHODDEF #define OS_READLINK_METHODDEF #endif /* !defined(OS_READLINK_METHODDEF) */ @@ -8731,4 +8773,4 @@ exit: #ifndef OS__REMOVE_DLL_DIRECTORY_METHODDEF #define OS__REMOVE_DLL_DIRECTORY_METHODDEF #endif /* !defined(OS__REMOVE_DLL_DIRECTORY_METHODDEF) */ -/*[clinic end generated code: output=c6e67d475eef00c4 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=51ba5b9536420cea input=a9049054013a1b77]*/ diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 6d837c6552f..46cf7b2f55a 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7861,6 +7861,30 @@ os_wait_impl(PyObject *module) } #endif /* HAVE_WAIT */ +#if defined(__linux__) && defined(__NR_pidfd_open) +/*[clinic input] +os.pidfd_open + pid: pid_t + flags: unsigned_int = 0 + +Return a file descriptor referring to the process *pid*. + +The descriptor can be used to perform process management without races and +signals. +[clinic start generated code]*/ + +static PyObject * +os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags) +/*[clinic end generated code: output=5c7252698947dc41 input=c3fd99ce947ccfef]*/ +{ + int fd = syscall(__NR_pidfd_open, pid, flags); + if (fd < 0) { + return posix_error(); + } + return PyLong_FromLong(fd); +} +#endif + #if defined(HAVE_READLINK) || defined(MS_WINDOWS) /*[clinic input] @@ -13671,6 +13695,7 @@ static PyMethodDef posix_methods[] = { OS_WAIT4_METHODDEF OS_WAITID_METHODDEF OS_WAITPID_METHODDEF + OS_PIDFD_OPEN_METHODDEF OS_GETSID_METHODDEF OS_SETSID_METHODDEF OS_SETPGID_METHODDEF