]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
spawn: Do not close every possible file descriptor
authorOlliver Schinagl <oliver@schinagl.nl>
Fri, 16 Jun 2023 13:25:36 +0000 (15:25 +0200)
committerFlole998 <Flole998@users.noreply.github.com>
Wed, 21 Jun 2023 09:16:07 +0000 (11:16 +0200)
When close is called with a non-existant file descriptor, it will happily
do so. Since we do not even check for the error code, trying to close a
non existant/not-open file descriptor does not even cause an error.

This in itself is not a problem at all, however, we try to close every
open file descriptor beyond stderr, upto whatever _SC_OPEN_MAX returns.

Some systems may have a very large ulimit set for `_SC_OPEN_MAX` and
thus `maxfd` may return something in the millions. This means that the
simple `for() close();` loop now has to iterate a million file
descriptors which may not even be open/exist.

Instead, we can use the power of `/proc/self/fd` to list our open
file descriptors, and only close open ones.

Signed-off-by: Olliver Schinagl <oliver@schinagl.nl>
src/spawn.c

index 516168f6a933e8962b74eb94794d3119ad453a09..a491c0690e566cd4fe84574d583f6ab1678ba9c7 100644 (file)
@@ -459,7 +459,7 @@ spawn_and_give_stdout(const char *prog, char *argv[], char *envp[],
                       int *rd, pid_t *pid, int redir_stderr)
 {
   pid_t p;
-  int fd[2], f, i, maxfd;
+  int fd[2], f, i;
   char bin[256];
   const char *local_argv[2] = { NULL, NULL };
   char **e, **e0, **e2, **e3, *p1, *p2;
@@ -507,8 +507,6 @@ spawn_and_give_stdout(const char *prog, char *argv[], char *envp[],
     *e0 = NULL;
   }
 
-  maxfd = sysconf(_SC_OPEN_MAX);
-
   tvh_mutex_lock(&fork_lock);
 
   if(pipe(fd) == -1) {
@@ -528,6 +526,9 @@ spawn_and_give_stdout(const char *prog, char *argv[], char *envp[],
   }
 
   if(p == 0) {
+    struct dirent *selfd_ent;
+    DIR *selfd;
+
     f = open("/dev/null", O_RDWR);
     if(f == -1) {
       spawn_error("pid %d cannot open /dev/null for redirect %s -- %s",
@@ -549,8 +550,25 @@ spawn_and_give_stdout(const char *prog, char *argv[], char *envp[],
 
     spawn_info("Executing \"%s\"\n", prog);
 
-    for (f = 3; f < maxfd; f++)
-      close(f);
+    if ((selfd = opendir("/dev/fd")) != NULL) {
+      while ((selfd_ent = readdir(selfd)) != NULL) {
+        int open_fd = atoi(selfd_ent->d_name);
+
+        if (open_fd >= 3) {
+          if (close(open_fd) == -1)
+            spawn_error("failed to close fd '%d': %s", open_fd, strerror(errno));
+        }
+      }
+      closedir(selfd);
+    } else {
+      int maxfd = sysconf(_SC_OPEN_MAX);;
+
+      spawn_error("unable to open '/dev/fd', falling back to closing all %d"
+                  "This may take a long time!", maxfd);
+
+      for (f = 3; f < maxfd; f++)
+        close(f);
+    }
 
     execve(prog, argv, e);
     spawn_error("pid %d cannot execute %s -- %s\n",