[vte] spawn: Use close_range to set CLOEXEC on all FDs



commit 6ff6542e902a1b7db6eca18cf8bccf24da431679
Author: Christian Persch <chpe src gnome org>
Date:   Mon Feb 1 22:04:12 2021 +0100

    spawn: Use close_range to set CLOEXEC on all FDs
    
    ... when available; otherwise fall back to iterating over /proc/self/fd
    as previously.

 src/missing.cc | 36 ++++++++++++++++++++++++++++++++++-
 src/missing.hh | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+), 1 deletion(-)
---
diff --git a/src/missing.cc b/src/missing.cc
index b013f8da..3cf4654f 100644
--- a/src/missing.cc
+++ b/src/missing.cc
@@ -134,6 +134,26 @@ getrlimit_NOFILE_max(void)
         return RLIM_INFINITY;
 }
 
+#ifdef __linux__
+
+static inline int
+_vte_close_range(int first_fd,
+                 int last_fd,
+                 unsigned flags)
+{
+#ifdef SYS_close_range
+        return syscall(SYS_close_range,
+                       unsigned(first_fd),
+                       last_fd == -1 ? ~0u : unsigned(last_fd),
+                       flags);
+#else
+        errno = ENOSYS;
+        return -1;
+#endif
+}
+
+#endif /* __linux__ */
+
 /* This function is called between fork and execve/_exit and so must be
  * async-signal-safe; see man:signal-safety(7).
  */
@@ -149,7 +169,21 @@ fdwalk(int (*cb)(void *data, int fd),
   int res = 0;
 
 #ifdef __linux__
-  /* Avoid use of opendir/closedir since these are not async-signal-safe. */
+
+  /* First, try close_range(CLOEXEC) which is faster than the methods
+   * below, and works even if /proc is not available.
+   */
+  res = _vte_close_range(0, -1, CLOSE_RANGE_CLOEXEC);
+  if (res == 0)
+          return 0;
+  if (res == -1 &&
+      errno != ENOSYS /* old kernel */ &&
+      errno != EINVAL /* flags not supported */)
+          return res;
+
+  /* Fall back to iterating over /proc/self/fd.
+   * Avoid use of opendir/closedir since these are not async-signal-safe.
+   */
   int dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
   if (dir_fd >= 0)
     {
diff --git a/src/missing.hh b/src/missing.hh
index fbeb8a2e..2dd8047d 100644
--- a/src/missing.hh
+++ b/src/missing.hh
@@ -33,3 +33,62 @@ int fdwalk(int (*cb)(void* data, int fd),
 char* strchrnul(char const* s,
                 int c);
 #endif
+
+#ifdef __linux__
+
+/* BEGIN
+ * The following is copied from systemd/src/basic/missing_syscall_def.h (LGPL2.1+)
+ */
+#ifndef __NR_close_range
+#  if defined(__aarch64__)
+#    define __NR_close_range 436
+#  elif defined(__alpha__)
+#    define __NR_close_range 546
+#  elif defined(__arc__) || defined(__tilegx__)
+#    define __NR_close_range 436
+#  elif defined(__arm__)
+#    define __NR_close_range 436
+#  elif defined(__i386__)
+#    define __NR_close_range 436
+#  elif defined(__ia64__)
+#    define __NR_close_range 1460
+#  elif defined(__m68k__)
+#    define __NR_close_range 436
+#  elif defined(_MIPS_SIM)
+#    if _MIPS_SIM == _MIPS_SIM_ABI32
+#      define __NR_close_range 4436
+#    elif _MIPS_SIM == _MIPS_SIM_NABI32
+#      define __NR_close_range 6436
+#    elif _MIPS_SIM == _MIPS_SIM_ABI64
+#      define __NR_close_range 5436
+#    else
+#      error "Unknown MIPS ABI"
+#    endif
+#  elif defined(__powerpc__)
+#    define __NR_close_range 436
+#  elif defined(__s390__)
+#    define __NR_close_range 436
+#  elif defined(__sparc__)
+#    define __NR_close_range 436
+#  elif defined(__x86_64__)
+#    if defined(__ILP32__)
+#      define __NR_close_range (436 | /* __X32_SYSCALL_BIT */ 0x40000000)
+#    else
+#      define __NR_close_range 436
+#    endif
+#  else
+#    warning "close_range() syscall number is unknown for your architecture"
+#  endif
+#endif /* !__NR_close_range */
+
+/* END copied from systemd */
+
+#if !defined(SYS_close_range) && defined(__NR_close_range)
+#define SYS_close_range __NR_close_range
+#endif
+
+#ifndef CLOSE_RANGE_CLOEXEC
+#define CLOSE_RANGE_CLOEXEC (1u << 2)
+#endif
+
+#endif /* __linux__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]