Improve file descriptor closing loops (#14213)

* Add for_each_open_fd() and fix second instance of _SC_OPEN_MAX

* Add argument to allow exclusion of file descriptors from closing

* Fix clang error

* Address review comments

* Use close_range() if possible and replace macros with enums
This commit is contained in:
Dim-P 2023-01-19 12:01:02 +00:00 committed by GitHub
parent c1908d3163
commit 8ee7e8b261
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 86 additions and 17 deletions

View File

@ -251,6 +251,7 @@ AC_SEARCH_LIBS([clock_gettime], [rt posix4])
AC_CHECK_FUNCS([clock_gettime])
AC_CHECK_FUNCS([sched_setscheduler sched_getscheduler sched_getparam sched_get_priority_min sched_get_priority_max getpriority setpriority nice])
AC_CHECK_FUNCS([recvmmsg])
AC_CHECK_FUNCS([close_range])
AC_TYPE_INT8_T
AC_TYPE_INT16_T

View File

@ -1700,15 +1700,12 @@ int main(int argc, char **argv) {
}
}
#ifdef _SC_OPEN_MAX
if (close_open_fds == true) {
// close all open file descriptors, except the standard ones
// the caller may have left open files (lxc-attach has this issue)
for(int fd = (int) (sysconf(_SC_OPEN_MAX) - 1); fd > 2; fd--)
if(fd_is_valid(fd))
close(fd);
for_each_open_fd(OPEN_FD_ACTION_CLOSE, OPEN_FD_EXCLUDE_STDIN | OPEN_FD_EXCLUDE_STDOUT | OPEN_FD_EXCLUDE_STDERR);
}
#endif
if(!config_loaded) {
load_netdata_conf(NULL, 0);

View File

@ -1948,3 +1948,70 @@ bool run_command_and_copy_output_to_stdout(const char *command, int max_line_len
netdata_pclose(NULL, fp, pid);
return true;
}
void for_each_open_fd(OPEN_FD_ACTION action, OPEN_FD_EXCLUDE excluded_fds){
int fd;
switch(action){
case OPEN_FD_ACTION_CLOSE:
if(!(excluded_fds & OPEN_FD_EXCLUDE_STDIN)) (void)close(STDIN_FILENO);
if(!(excluded_fds & OPEN_FD_EXCLUDE_STDOUT)) (void)close(STDOUT_FILENO);
if(!(excluded_fds & OPEN_FD_EXCLUDE_STDERR)) (void)close(STDERR_FILENO);
break;
case OPEN_FD_ACTION_FD_CLOEXEC:
if(!(excluded_fds & OPEN_FD_EXCLUDE_STDIN)) (void)fcntl(STDIN_FILENO, F_SETFD, FD_CLOEXEC);
if(!(excluded_fds & OPEN_FD_EXCLUDE_STDOUT)) (void)fcntl(STDOUT_FILENO, F_SETFD, FD_CLOEXEC);
if(!(excluded_fds & OPEN_FD_EXCLUDE_STDERR)) (void)fcntl(STDERR_FILENO, F_SETFD, FD_CLOEXEC);
break;
default:
break; // do nothing
}
#if defined(HAVE_CLOSE_RANGE)
if(close_range(STDERR_FILENO + 1, ~0U, (action == OPEN_FD_ACTION_FD_CLOEXEC ? CLOSE_RANGE_CLOEXEC : 0)) == 0) return;
error("close_range() failed, will try to close fds manually");
#endif
DIR *dir = opendir("/proc/self/fd");
if (dir == NULL) {
struct rlimit rl;
int open_max = -1;
if(getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY) open_max = rl.rlim_max;
#ifdef _SC_OPEN_MAX
else open_max = sysconf(_SC_OPEN_MAX);
#endif
if (open_max == -1) open_max = 65535; // 65535 arbitrary default if everything else fails
for (fd = STDERR_FILENO + 1; fd < open_max; fd++) {
switch(action){
case OPEN_FD_ACTION_CLOSE:
if(fd_is_valid(fd)) (void)close(fd);
break;
case OPEN_FD_ACTION_FD_CLOEXEC:
(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
break;
default:
break; // do nothing
}
}
} else {
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
fd = str2i(entry->d_name);
if(unlikely((fd == STDIN_FILENO ) || (fd == STDOUT_FILENO) || (fd == STDERR_FILENO) )) continue;
switch(action){
case OPEN_FD_ACTION_CLOSE:
if(fd_is_valid(fd)) (void)close(fd);
break;
case OPEN_FD_ACTION_FD_CLOEXEC:
(void)fcntl(fd, F_SETFD, FD_CLOEXEC);
break;
default:
break; // do nothing
}
}
closedir(dir);
}
}

View File

@ -432,6 +432,17 @@ static inline char *get_word(char **words, size_t num_words, size_t index) {
bool run_command_and_copy_output_to_stdout(const char *command, int max_line_length);
typedef enum {
OPEN_FD_ACTION_CLOSE,
OPEN_FD_ACTION_FD_CLOEXEC
} OPEN_FD_ACTION;
typedef enum {
OPEN_FD_EXCLUDE_STDIN = 0x01,
OPEN_FD_EXCLUDE_STDOUT = 0x02,
OPEN_FD_EXCLUDE_STDERR = 0x04
} OPEN_FD_EXCLUDE;
void for_each_open_fd(OPEN_FD_ACTION action, OPEN_FD_EXCLUDE excluded_fds);
void netdata_cleanup_and_exit(int ret) NORETURN;
void send_statistics(const char *action, const char *action_result, const char *action_data);
extern char *netdata_configured_host_prefix;

View File

@ -163,8 +163,7 @@ static int popene_internal(volatile pid_t *pidptr, char **env, uint8_t flags, FI
posix_spawnattr_t attr;
posix_spawn_file_actions_t fa;
int stdin_fd_to_exclude_from_closing = -1;
int stdout_fd_to_exclude_from_closing = -1;
unsigned int fds_to_exclude_from_closing = OPEN_FD_EXCLUDE_STDERR;
if(posix_spawn_file_actions_init(&fa)) {
error("POPEN: posix_spawn_file_actions_init() failed.");
@ -195,7 +194,7 @@ static int popene_internal(volatile pid_t *pidptr, char **env, uint8_t flags, FI
if (posix_spawn_file_actions_addopen(&fa, STDIN_FILENO, "/dev/null", O_RDONLY, 0)) {
error("POPEN: posix_spawn_file_actions_addopen() on stdin to /dev/null failed.");
// this is not a fatal error
stdin_fd_to_exclude_from_closing = STDIN_FILENO;
fds_to_exclude_from_closing |= OPEN_FD_EXCLUDE_STDIN;
}
}
@ -222,16 +221,13 @@ static int popene_internal(volatile pid_t *pidptr, char **env, uint8_t flags, FI
if (posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, "/dev/null", O_WRONLY, 0)) {
error("POPEN: posix_spawn_file_actions_addopen() on stdout to /dev/null failed.");
// this is not a fatal error
stdout_fd_to_exclude_from_closing = STDOUT_FILENO;
fds_to_exclude_from_closing |= OPEN_FD_EXCLUDE_STDOUT;
}
}
if(flags & POPEN_FLAG_CLOSE_FD) {
// Mark all files to be closed by the exec() stage of posix_spawn()
for(int i = (int)(sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--) {
if(likely(i != STDERR_FILENO && i != stdin_fd_to_exclude_from_closing && i != stdout_fd_to_exclude_from_closing))
(void)fcntl(i, F_SETFD, FD_CLOEXEC);
}
for_each_open_fd(OPEN_FD_ACTION_FD_CLOEXEC, fds_to_exclude_from_closing);
}
attr_rc = posix_spawnattr_init(&attr);

View File

@ -317,10 +317,7 @@ void spawn_server(void)
// close all open file descriptors, except the standard ones
// the caller may have left open files (lxc-attach has this issue)
int fd;
for(fd = (int)(sysconf(_SC_OPEN_MAX) - 1) ; fd > 2 ; --fd)
if(fd_is_valid(fd))
close(fd);
for_each_open_fd(OPEN_FD_ACTION_CLOSE, OPEN_FD_EXCLUDE_STDIN | OPEN_FD_EXCLUDE_STDOUT | OPEN_FD_EXCLUDE_STDERR);
// Have the libuv IPC pipe be closed when forking child processes
(void) fcntl(0, F_SETFD, FD_CLOEXEC);