"""
-import ctypes
import os
import sys
import warnings # noqa: F401 (loaded lazily by os.execvp() which happens too late)
ENOENT = 2
ENOSYS = 38
F_DUPFD = 0
-F_GETFD = 1
FS_IOC_GETFLAGS = 0x80086601
FS_IOC_SETFLAGS = 0x40086602
FS_NOCOW_FL = 0x00800000
SIGSTOP = 19
+def is_valid_fd(fd: int) -> bool:
+ try:
+ os.close(os.dup(fd))
+ except OSError as e:
+ if e.errno != EBADF:
+ raise
+
+ return False
+
+ return True
+
+
+def open_file_descriptors() -> list[int]:
+ fds = []
+
+ with os.scandir("/proc/self/fd") as it:
+ for e in it:
+ if not e.is_symlink() and (e.is_file() or e.is_dir()):
+ continue
+
+ try:
+ fd = int(e.name)
+ except ValueError:
+ continue
+
+ fds.append(fd)
+
+ # os.scandir() either opens a file descriptor to the given path or dups the given file descriptor. Either
+ # way, there will be an extra file descriptor in the fds array that's not valid anymore now, so find out
+ # which one and drop it.
+ return sorted(fd for fd in fds if is_valid_fd(fd))
+
+
+class CloseNewFileDescriptors:
+ def __enter__(self) -> None:
+ self.fds = set(open_file_descriptors())
+
+ def __exit__(self, *args: object, **kwargs: object) -> None:
+ for fd in open_file_descriptors():
+ if fd not in self.fds:
+ os.close(fd)
+
+
+# Since python 3.14, importing ctypes opens an extra file descriptor which is used to allocate libffi
+# closures which are in turn used by ctypes to pass python functions as C callback function pointers. We
+# don't use this functionality, yet the file descriptor is still opened and messes with the file descriptor
+# packing logic since the file descriptor to libffi will be passed as a packed file descriptor to the
+# executable we're invoking. To avoid that from happening, we close libffi's file descriptor after importing
+# ctypes. See https://github.com/python/cpython/issues/135893.
+with CloseNewFileDescriptors():
+ import ctypes
+
+
class mount_attr(ctypes.Structure):
_fields_ = [
("attr_set", ctypes.c_uint64),
def pack_file_descriptors() -> int:
- fds = []
-
- with os.scandir("/proc/self/fd") as it:
- for e in it:
- if not e.is_symlink() and (e.is_file() or e.is_dir()):
- continue
-
- try:
- fd = int(e.name)
- except ValueError:
- continue
-
- if fd < SD_LISTEN_FDS_START:
- continue
-
- fds.append(fd)
-
- # os.scandir() either opens a file descriptor to the given path or dups the given file descriptor. Either
- # way, there will be an extra file descriptor in the fds array that's not valid anymore now, so find out
- # which one and drop it.
- fds = sorted(fd for fd in fds if libc.fcntl(fd, F_GETFD, 0) >= 0)
+ fds = [fd for fd in open_file_descriptors() if fd >= SD_LISTEN_FDS_START]
# The following is a reimplementation of pack_fds() in systemd.