| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "../libnetdata.h" |
| 4 | |
| 5 | static int fd_is_valid(int fd) { |
| 6 | errno_clear(); |
| 7 | return fcntl(fd, F_GETFD) != -1 || errno != EBADF; |
| 8 | } |
| 9 | |
| 10 | static void setcloexec(int fd) { |
| 11 | int flags = fcntl(fd, F_GETFD); |
| 12 | if (flags != -1) |
| 13 | (void) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); |
| 14 | } |
| 15 | |
| 16 | int os_get_fd_open_max(void) { |
| 17 | static int fd_open_max = CLOSE_RANGE_FD_MAX; |
| 18 | |
| 19 | if(fd_open_max != CLOSE_RANGE_FD_MAX) |
| 20 | return fd_open_max; |
| 21 | |
| 22 | if(fd_open_max == CLOSE_RANGE_FD_MAX || fd_open_max == -1) { |
| 23 | struct rlimit rl; |
| 24 | if (getrlimit(RLIMIT_NOFILE, &rl) == 0 && rl.rlim_max != RLIM_INFINITY) |
| 25 | fd_open_max = rl.rlim_max; |
| 26 | } |
| 27 | |
| 28 | #ifdef _SC_OPEN_MAX |
| 29 | if(fd_open_max == CLOSE_RANGE_FD_MAX || fd_open_max == -1) { |
| 30 | fd_open_max = sysconf(_SC_OPEN_MAX); |
| 31 | } |
| 32 | #endif |
| 33 | |
| 34 | if(fd_open_max == CLOSE_RANGE_FD_MAX || fd_open_max == -1) { |
| 35 | // Arbitrary default if everything else fails |
| 36 | fd_open_max = 65535; |
| 37 | } |
| 38 | |
| 39 | return fd_open_max; |
| 40 | } |
| 41 | |
| 42 | void os_close_range(int first, int last, int flags) { |
| 43 | #if defined(HAVE_CLOSE_RANGE) |
| 44 | if(close_range(first, last, flags) == 0) return; |
| 45 | #endif |
| 46 | |
| 47 | #if defined(OS_LINUX) |
| 48 | DIR *dir = opendir("/proc/self/fd"); |
| 49 | if (dir != NULL) { |
| 50 | struct dirent *entry; |
| 51 | while ((entry = readdir(dir)) != NULL) { |
| 52 | int fd = str2i(entry->d_name); |
| 53 | if (fd >= first && (last == CLOSE_RANGE_FD_MAX || fd <= last) && fd_is_valid(fd)) { |
| 54 | if(flags & CLOSE_RANGE_CLOEXEC) |
| 55 | setcloexec(fd); |
| 56 | else |
| 57 | (void)close(fd); |
| 58 | } |
| 59 | } |
| 60 | closedir(dir); |
| 61 | return; |
| 62 | } |
| 63 | #endif |
| 64 | |
| 65 | // Fallback to looping through all file descriptors if necessary |
| 66 | if (last == CLOSE_RANGE_FD_MAX) |
| 67 | last = os_get_fd_open_max(); |
| 68 | |
| 69 | for (int fd = first; fd <= last; fd++) { |
| 70 | if (fd_is_valid(fd)) { |
| 71 | if(flags & CLOSE_RANGE_CLOEXEC) |
| 72 | setcloexec(fd); |
| 73 | else |
| 74 | (void)close(fd); |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | static int compare_ints(const void *a, const void *b) { |
| 80 | int int_a = *((int*)a); |
| 81 | int int_b = *((int*)b); |
| 82 | return (int_a > int_b) - (int_a < int_b); |
| 83 | } |
| 84 | |
| 85 | void os_close_all_non_std_open_fds_except(const int fds[], size_t fds_num, int flags) { |
| 86 | if (fds_num == 0 || fds == NULL) { |
| 87 | os_close_range(STDERR_FILENO + 1, CLOSE_RANGE_FD_MAX, flags); |
| 88 | return; |
| 89 | } |
| 90 | |
| 91 | // copy the fds array to ensure we will not alter them |
| 92 | int *fds_copy = mallocz(fds_num * sizeof(*fds_copy)); |
| 93 | memcpy(fds_copy, fds, fds_num * sizeof(*fds_copy)); |
| 94 | |
| 95 | qsort(fds_copy, fds_num, sizeof(int), compare_ints); |
| 96 | |
| 97 | int start = STDERR_FILENO + 1; |
| 98 | size_t i = 0; |
| 99 | |
| 100 | // filter out all fds with a number smaller than our start |
| 101 | for (; i < fds_num; i++) |
| 102 | if(fds_copy[i] >= start) break; |
| 103 | |
| 104 | // call os_close_range() as many times as needed |
| 105 | for (; i < fds_num; i++) { |
| 106 | if (fds_copy[i] > start) |
| 107 | os_close_range(start, fds_copy[i] - 1, flags); |
| 108 | |
| 109 | start = fds_copy[i] + 1; |
| 110 | } |
| 111 | |
| 112 | os_close_range(start, CLOSE_RANGE_FD_MAX, flags); |
| 113 | freez(fds_copy); |
| 114 | } |