Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5062,6 +5062,18 @@ written in Python, such as a mail server's external command delivery program.
.. availability:: Linux >= 5.10
.. versionadded:: 3.12

.. function:: pidfd_getfd(pidfd, targetfd, *, flags=0)

Duplicate *targetfd* from the process referred to by the process file
descriptor *pidfd*, into the calling process. The returned file descriptor
is :ref:`non-inheritable <fd_inheritance>`.

*flags* is reserved, and currently must be ``0``.

See the :manpage:`pidfd_getfd(2)` man page for more details.

.. availability:: Linux >= 5.6, Android >= :func:`build-time <sys.getandroidapilevel>` API level 31
.. versionadded:: next

.. function:: plock(op, /)

Expand Down
8 changes: 5 additions & 3 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,12 @@ New modules
Improved modules
================

module_name
-----------
os
--

* TODO
* Add :func:`os.pidfd_getfd` for duplicating a file descriptor from another
process via a pidfd. Available on Linux 5.6+.
(Contributed by Maurycy Pawłowski-Wieroński in :gh:`149464`.)

.. Add improved modules above alphabetically, not here at the end.

Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(person)
STRUCT_FOR_ID(pi_factory)
STRUCT_FOR_ID(pid)
STRUCT_FOR_ID(pidfd)
STRUCT_FOR_ID(pointer_bits)
STRUCT_FOR_ID(policy)
STRUCT_FOR_ID(pos)
Expand Down Expand Up @@ -831,6 +832,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(take_bytes)
STRUCT_FOR_ID(target)
STRUCT_FOR_ID(target_is_directory)
STRUCT_FOR_ID(targetfd)
STRUCT_FOR_ID(task)
STRUCT_FOR_ID(tb_frame)
STRUCT_FOR_ID(tb_lasti)
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions Lib/test/test_os/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,34 @@ def test_pidfd_open(self):
self.assertEqual(cm.exception.errno, errno.EINVAL)
os.close(os.pidfd_open(os.getpid(), 0))

@unittest.skipUnless(hasattr(os, "pidfd_getfd"), "pidfd_getfd unavailable")
def test_pidfd_getfd(self):
fd = os.open(__file__, os.O_RDONLY)
self.addCleanup(os.close, fd)
pidfd = os.pidfd_open(os.getpid(), 0)
self.addCleanup(os.close, pidfd)
try:
dupfd = os.pidfd_getfd(pidfd, fd)
except OSError as exc:
if exc.errno == errno.ENOSYS:
self.skipTest("system does not support pidfd_getfd")
if isinstance(exc, PermissionError):
self.skipTest(f"pidfd_getfd syscall blocked: {exc!r}")
raise
self.addCleanup(os.close, dupfd)

self.assertFalse(os.get_inheritable(dupfd)) # PEP 446
self.assertEqual(os.fstat(fd), os.fstat(dupfd))

with self.assertRaises(OSError) as cm:
os.pidfd_getfd(-1, 0)
self.assertEqual(cm.exception.errno, errno.EBADF)

Comment thread
maurycy marked this conversation as resolved.
with self.assertRaises(OSError) as cm:
bad_fd = os_helper.make_bad_fd()
os.pidfd_getfd(pidfd, bad_fd)
self.assertEqual(cm.exception.errno, errno.EBADF)

@os_helper.skip_unless_hardlink
@os_helper.skip_unless_symlink
def test_link_follow_symlinks(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add :func:`os.pidfd_getfd` for duplicating a file descriptor from another
process via a pidfd. Available on Linux 5.6+. Patch by Maurycy
Pawłowski-Wieroński.
Comment thread
maurycy marked this conversation as resolved.
93 changes: 92 additions & 1 deletion Modules/clinic/posixmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -10777,6 +10777,35 @@ os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags)
#endif


#if defined(__linux__) && defined(__NR_pidfd_getfd) && \
!(defined(__ANDROID__) && __ANDROID_API__ < 31)
/*[clinic input]
os.pidfd_getfd
pidfd: int
Comment thread
maurycy marked this conversation as resolved.
A process file descriptor.
targetfd: int
The file descriptor to duplicate from the target process.
*
flags: unsigned_int = 0
Reserved, must be 0.
Comment thread
picnixz marked this conversation as resolved.

Duplicate a file descriptor from the process referred to by *pidfd*.
[clinic start generated code]*/

static PyObject *
os_pidfd_getfd_impl(PyObject *module, int pidfd, int targetfd,
unsigned int flags)
/*[clinic end generated code: output=e1a1415a13c7137f input=ef6417fb10deb1cc]*/
{
int fd = syscall(__NR_pidfd_getfd, pidfd, targetfd, flags);
if (fd < 0) {
return posix_error();
}
return PyLong_FromLong(fd);
}
#endif


#ifdef HAVE_SETNS
/*[clinic input]
os.setns
Expand Down Expand Up @@ -17606,6 +17635,7 @@ static PyMethodDef posix_methods[] = {
OS_WAITID_METHODDEF
OS_WAITPID_METHODDEF
OS_PIDFD_OPEN_METHODDEF
OS_PIDFD_GETFD_METHODDEF
OS_GETSID_METHODDEF
OS_SETSID_METHODDEF
OS_SETPGID_METHODDEF
Expand Down
Loading