Skip to content

Commit 04685d8

Browse files
committed
speed up closing all file descriptors in the child process (Fixes #125 #147)
1 parent 3c0f354 commit 04685d8

File tree

8 files changed

+64
-8
lines changed

8 files changed

+64
-8
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.12.26
1+
0.12.27

native/exec_pty.c

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#include <sys/ioctl.h>
2222
#include <fcntl.h>
2323
#include <sys/wait.h>
24+
#include <sys/syscall.h>
25+
#include <dirent.h>
26+
#include <ctype.h>
2427

2528
#include "exec_pty.h"
2629

@@ -53,6 +56,65 @@ void restore_signals() {
5356
restore_signal(SIGQUIT);
5457
}
5558

59+
static int sys_close_range_wrapper(unsigned int from_fd_inclusive) {
60+
// Use fast `close_range` (https://man7.org/linux/man-pages/man2/close_range.2.html) if available.
61+
// Cannot call `close_range` from libc, as it may be unavailable in older libc.
62+
# if defined(__linux__) && defined(SYS_close_range) && defined(CLOSE_RANGE_UNSHARE)
63+
return syscall(SYS_close_range, from_fd_inclusive, ~0U, CLOSE_RANGE_UNSHARE);
64+
# else
65+
return -1;
66+
# endif
67+
}
68+
69+
static int close_all_fds_using_parsing(unsigned int from_fd_inclusive) {
70+
// If `opendir` is implemented using a file descriptor, we may close it accidentally.
71+
// Let's close a few lowest file descriptors, in hope that `opendir` will use it.
72+
int lowest_fds_to_close = 2;
73+
for (int i = 0; i < lowest_fds_to_close; i++) {
74+
close(from_fd_inclusive + i);
75+
}
76+
77+
#if defined(_ALLBSD_SOURCE)
78+
#define FD_DIR "/dev/fd"
79+
#else
80+
#define FD_DIR "/proc/self/fd"
81+
#endif
82+
83+
DIR *dirp = opendir(FD_DIR);
84+
if (dirp == NULL) return -1;
85+
86+
struct dirent *direntp;
87+
88+
while ((direntp = readdir(dirp)) != NULL) {
89+
int fd;
90+
if (isdigit(direntp->d_name[0])) {
91+
fd = strtol(direntp->d_name, NULL, 10);
92+
if (fd >= from_fd_inclusive + lowest_fds_to_close && fd != dirfd(dirp)) {
93+
close(fd);
94+
}
95+
}
96+
}
97+
98+
closedir(dirp);
99+
100+
return 0;
101+
}
102+
103+
static void close_all_fds_fallback(unsigned int from_fd_inclusive) {
104+
int fdlimit = sysconf(_SC_OPEN_MAX);
105+
if (fdlimit == -1) fdlimit = 65535; // arbitrary default, just in case
106+
for (int fd = from_fd_inclusive; fd < fdlimit; fd++) {
107+
close(fd);
108+
}
109+
}
110+
111+
static void close_all_fds() {
112+
unsigned int from_fd = STDERR_FILENO + 1;
113+
if (sys_close_range_wrapper(from_fd) == 0) return;
114+
if (close_all_fds_using_parsing(from_fd) == 0) return;
115+
close_all_fds_fallback(from_fd);
116+
}
117+
56118
pid_t exec_pty(const char *path, char *const argv[], char *const envp[], const char *dirpath,
57119
const char *pts_name, int fdm, const char *err_pts_name, int err_fdm, int console)
58120
{
@@ -122,13 +184,7 @@ pid_t exec_pty(const char *path, char *const argv[], char *const envp[], const c
122184
if (console && err_fds >= 0) close(err_fds);
123185

124186
/* Close all the fd's in the child */
125-
{
126-
int fdlimit = sysconf(_SC_OPEN_MAX);
127-
int fd = 3;
128-
129-
while (fd < fdlimit)
130-
close(fd++);
131-
}
187+
close_all_fds();
132188

133189
restore_signals();
134190

os/linux/aarch64/libpty.so

7.47 KB
Binary file not shown.

os/linux/arm/libpty.so

148 Bytes
Binary file not shown.

os/linux/mips64el/libpty.so

11 KB
Binary file not shown.

os/linux/ppc64le/libpty.so

1.62 KB
Binary file not shown.

os/linux/x86-64/libpty.so

100644100755
8 KB
Binary file not shown.

os/linux/x86/libpty.so

100644100755
4.01 KB
Binary file not shown.

0 commit comments

Comments
 (0)