summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Persch <chpe@src.gnome.org>2021-01-19 21:05:45 +0100
committerChristian Persch <chpe@src.gnome.org>2021-01-19 21:05:45 +0100
commit0a8f1c0d97437e1989dee4a07ea1ea8d85a09f94 (patch)
treef6b9888e057f1fb92b4c0f23a7f0f888b4aedbd8
parent59e11fd751806876dc1a28c9c6d9a17d75db57d2 (diff)
downloadvte-wip/close-range.tar.gz
spawn: Use close_range to set CLOEXEC on all FDswip/close-range
... when available; otherwise fall back to iterating over /proc/self/fd as previously.
-rw-r--r--src/missing.cc37
-rw-r--r--src/missing.hh55
2 files changed, 91 insertions, 1 deletions
diff --git a/src/missing.cc b/src/missing.cc
index 214419fc..e890ad7e 100644
--- a/src/missing.cc
+++ b/src/missing.cc
@@ -134,6 +134,27 @@ getrlimit_NOFILE_max(void)
return RLIM_INFINITY;
}
+#ifdef __linux__
+
+static inline int
+_vte_close_range(int first_fd,
+ int last_fd,
+ unsigned flags)
+{
+#ifdef __NR_close_range
+ errno = 0;
+ return syscall(__NR_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 +170,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 07422708..39cb433a 100644
--- a/src/missing.hh
+++ b/src/missing.hh
@@ -33,3 +33,58 @@ 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 */
+
+#ifndef CLOSE_RANGE_CLOEXEC
+#define CLOSE_RANGE_CLOEXEC (1u << 2)
+#endif
+
+#endif /* __linux__ */