]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lib/fileutils: add close_all_fds()
authorKarel Zak <kzak@redhat.com>
Thu, 17 Oct 2019 08:36:27 +0000 (10:36 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 17 Oct 2019 08:44:57 +0000 (10:44 +0200)
The classic way which is based on file-descriptors table size is
pretty expensive (due to table size) and forces code to do many
unnecessary close() calls. It seems better to use /proc/self/fds and
close used descriptors only.

Addresses: https://github.com/karelzak/util-linux/issues/883
Signed-off-by: Karel Zak <kzak@redhat.com>
include/fileutils.h
lib/fileutils.c

index 043f2ca44b43b4ae3e059eb5d173b827ba453c4a..479ad15cfea8725b96598df7a8bbaff579abc0c7 100644 (file)
@@ -72,4 +72,6 @@ static inline struct dirent *xreaddir(DIR *dp)
        return d;
 }
 
+extern void close_all_fds(const int exclude[], size_t exsz);
+
 #endif /* UTIL_LINUX_FILEUTILS */
index 822ca9676bd1e774ebfecde749a3eed137987b00..3ca43c1fb537ae6c0ea6d28b88d3fc177c81ac13 100644 (file)
@@ -124,16 +124,76 @@ int get_fd_tabsize(void)
        return m;
 }
 
+static inline int in_set(int x, const int set[], size_t setsz)
+{
+       size_t i;
+
+       for (i = 0; i < setsz; i++) {
+               if (set[i] == x)
+                       return 1;
+       }
+       return 0;
+}
+
+void close_all_fds(const int exclude[], size_t exsz)
+{
+       struct dirent *d;
+       DIR *dir;
+
+       dir = opendir(_PATH_PROC_FDDIR);
+       if (dir) {
+               while ((d = xreaddir(dir))) {
+                       char *end;
+                       int fd;
+
+                       errno = 0;
+                       fd = strtol(d->d_name, &end, 10);
+
+                       if (errno || end == d->d_name || !end || *end)
+                               continue;
+                       if (dirfd(dir) == fd)
+                               continue;
+                       if (in_set(fd, exclude, exsz))
+                               continue;
+                       close(fd);
+               }
+               closedir(dir);
+       } else {
+               int fd, tbsz = get_fd_tabsize();
+
+               for (fd = 0; fd < tbsz; fd++) {
+                       if (!in_set(fd, exclude, exsz))
+                               close(fd);
+               }
+       }
+}
+
 #ifdef TEST_PROGRAM_FILEUTILS
-int main(void)
+int main(int argc, char *argv[])
 {
-       FILE *f;
-       char *tmpname;
-       f = xfmkstemp(&tmpname, NULL, "test");
-       unlink(tmpname);
-       free(tmpname);
-       fclose(f);
-       return EXIT_FAILURE;
+       if (argc < 2)
+               errx(EXIT_FAILURE, "Usage %s --{mkstemp,close-fds}", argv[0]);
+
+       if (strcmp(argv[1], "--mkstemp") == 0) {
+               FILE *f;
+               char *tmpname;
+               f = xfmkstemp(&tmpname, NULL, "test");
+               unlink(tmpname);
+               free(tmpname);
+               fclose(f);
+
+       } else if (strcmp(argv[1], "--close-fds") == 0) {
+               static const int wanted_fds[] = {
+                       STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO
+               };
+
+               ignore_result( dup(STDIN_FILENO) );
+               ignore_result( dup(STDIN_FILENO) );
+               ignore_result( dup(STDIN_FILENO) );
+
+               close_all_fds(wanted_fds, ARRAY_SIZE(wanted_fds));
+       }
+       return EXIT_SUCCESS;
 }
 #endif