Skip to content

Commit a2932e9

Browse files
maurycypicnixz
andauthored
gh-149464: Add os.pidfd_getfd(pidfd, targetfd, flags=0) function (#149465)
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent bd6bf91 commit a2932e9

10 files changed

Lines changed: 184 additions & 4 deletions

File tree

Doc/library/os.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5062,6 +5062,18 @@ written in Python, such as a mail server's external command delivery program.
50625062
.. availability:: Linux >= 5.10
50635063
.. versionadded:: 3.12
50645064

5065+
.. function:: pidfd_getfd(pidfd, targetfd, *, flags=0)
5066+
5067+
Duplicate *targetfd* from the process referred to by the process file
5068+
descriptor *pidfd*, into the calling process. The returned file descriptor
5069+
is :ref:`non-inheritable <fd_inheritance>`.
5070+
5071+
*flags* is reserved, and currently must be ``0``.
5072+
5073+
See the :manpage:`pidfd_getfd(2)` man page for more details.
5074+
5075+
.. availability:: Linux >= 5.6, Android >= :func:`build-time <sys.getandroidapilevel>` API level 31
5076+
.. versionadded:: next
50655077

50665078
.. function:: plock(op, /)
50675079

Doc/whatsnew/3.16.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,12 @@ New modules
8686
Improved modules
8787
================
8888

89-
module_name
90-
-----------
89+
os
90+
--
9191

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

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

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,7 @@ struct _Py_global_strings {
713713
STRUCT_FOR_ID(person)
714714
STRUCT_FOR_ID(pi_factory)
715715
STRUCT_FOR_ID(pid)
716+
STRUCT_FOR_ID(pidfd)
716717
STRUCT_FOR_ID(pointer_bits)
717718
STRUCT_FOR_ID(policy)
718719
STRUCT_FOR_ID(pos)
@@ -831,6 +832,7 @@ struct _Py_global_strings {
831832
STRUCT_FOR_ID(take_bytes)
832833
STRUCT_FOR_ID(target)
833834
STRUCT_FOR_ID(target_is_directory)
835+
STRUCT_FOR_ID(targetfd)
834836
STRUCT_FOR_ID(task)
835837
STRUCT_FOR_ID(tb_frame)
836838
STRUCT_FOR_ID(tb_lasti)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_os/test_posix.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,34 @@ def test_pidfd_open(self):
16041604
self.assertEqual(cm.exception.errno, errno.EINVAL)
16051605
os.close(os.pidfd_open(os.getpid(), 0))
16061606

1607+
@unittest.skipUnless(hasattr(os, "pidfd_getfd"), "pidfd_getfd unavailable")
1608+
def test_pidfd_getfd(self):
1609+
fd = os.open(__file__, os.O_RDONLY)
1610+
self.addCleanup(os.close, fd)
1611+
pidfd = os.pidfd_open(os.getpid(), 0)
1612+
self.addCleanup(os.close, pidfd)
1613+
try:
1614+
dupfd = os.pidfd_getfd(pidfd, fd)
1615+
except OSError as exc:
1616+
if exc.errno == errno.ENOSYS:
1617+
self.skipTest("system does not support pidfd_getfd")
1618+
if isinstance(exc, PermissionError):
1619+
self.skipTest(f"pidfd_getfd syscall blocked: {exc!r}")
1620+
raise
1621+
self.addCleanup(os.close, dupfd)
1622+
1623+
self.assertFalse(os.get_inheritable(dupfd)) # PEP 446
1624+
self.assertEqual(os.fstat(fd), os.fstat(dupfd))
1625+
1626+
with self.assertRaises(OSError) as cm:
1627+
os.pidfd_getfd(-1, 0)
1628+
self.assertEqual(cm.exception.errno, errno.EBADF)
1629+
1630+
with self.assertRaises(OSError) as cm:
1631+
bad_fd = os_helper.make_bad_fd()
1632+
os.pidfd_getfd(pidfd, bad_fd)
1633+
self.assertEqual(cm.exception.errno, errno.EBADF)
1634+
16071635
@os_helper.skip_unless_hardlink
16081636
@os_helper.skip_unless_symlink
16091637
def test_link_follow_symlinks(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add :func:`os.pidfd_getfd` for duplicating a file descriptor from another
2+
process via a pidfd. Available on Linux 5.6+. Patch by Maurycy
3+
Pawłowski-Wieroński.

Modules/clinic/posixmodule.c.h

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

Modules/posixmodule.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10777,6 +10777,35 @@ os_pidfd_open_impl(PyObject *module, pid_t pid, unsigned int flags)
1077710777
#endif
1077810778

1077910779

10780+
#if defined(__linux__) && defined(__NR_pidfd_getfd) && \
10781+
!(defined(__ANDROID__) && __ANDROID_API__ < 31)
10782+
/*[clinic input]
10783+
os.pidfd_getfd
10784+
pidfd: int
10785+
A process file descriptor.
10786+
targetfd: int
10787+
The file descriptor to duplicate from the target process.
10788+
*
10789+
flags: unsigned_int = 0
10790+
Reserved, must be 0.
10791+
10792+
Duplicate a file descriptor from the process referred to by *pidfd*.
10793+
[clinic start generated code]*/
10794+
10795+
static PyObject *
10796+
os_pidfd_getfd_impl(PyObject *module, int pidfd, int targetfd,
10797+
unsigned int flags)
10798+
/*[clinic end generated code: output=e1a1415a13c7137f input=ef6417fb10deb1cc]*/
10799+
{
10800+
int fd = syscall(__NR_pidfd_getfd, pidfd, targetfd, flags);
10801+
if (fd < 0) {
10802+
return posix_error();
10803+
}
10804+
return PyLong_FromLong(fd);
10805+
}
10806+
#endif
10807+
10808+
1078010809
#ifdef HAVE_SETNS
1078110810
/*[clinic input]
1078210811
os.setns
@@ -17606,6 +17635,7 @@ static PyMethodDef posix_methods[] = {
1760617635
OS_WAITID_METHODDEF
1760717636
OS_WAITPID_METHODDEF
1760817637
OS_PIDFD_OPEN_METHODDEF
17638+
OS_PIDFD_GETFD_METHODDEF
1760917639
OS_GETSID_METHODDEF
1761017640
OS_SETSID_METHODDEF
1761117641
OS_SETPGID_METHODDEF

0 commit comments

Comments
 (0)