Don't allow file descriptors to go above 64k, when we're closing them in a fork(2).
authorTilghman Lesher <tilghman@meg.abyt.es>
Fri, 30 Apr 2010 06:19:35 +0000 (06:19 +0000)
committerTilghman Lesher <tilghman@meg.abyt.es>
Fri, 30 Apr 2010 06:19:35 +0000 (06:19 +0000)
This saves time, when, even though the system allows the process limit to be
that high, the practical limit is much lower.  Also introduce an additional
optimization, in the form of using the CLOEXEC flag to close descriptors at
the right time.

(closes issue #17223)
 Reported by: dbackeberg
 Patches:
       20100423__issue17223.diff.txt uploaded by tilghman (license 14)
 Tested by: dbackeberg

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@260292 65c4cc65-6c06-0410-ace0-fbb531ad65f3

main/strcompat.c

index b1d8b0d..923e935 100644 (file)
@@ -26,6 +26,8 @@
 #include <sys/resource.h>   /* for getrlimit(2) */
 #include <sys/types.h>      /* for opendir(3) */
 #include <dirent.h>         /* for opendir(3) */
+#include <unistd.h>         /* for fcntl(2) */
+#include <fcntl.h>          /* for fcntl(2) */
 
 #ifndef HAVE_STRSEP
 char *strsep(char **str, const char *delims)
@@ -420,14 +422,44 @@ void closefrom(int n)
                                continue;
                        }
                        if ((x = strtol(entry->d_name, &result, 10)) && x >= n) {
+#ifdef STRICT_COMPAT
                                close(x);
+#else
+                               /* This isn't strictly compatible, but it's actually faster
+                                * for our purposes to set the CLOEXEC flag than to close
+                                * file descriptors.
+                                */
+                               long flags = fcntl(x, F_GETFD);
+                               if (flags == -1 && errno == EBADF) {
+                                       continue;
+                               }
+                               fcntl(x, F_SETFD, flags | FD_CLOEXEC);
+#endif
                        }
                }
                closedir(dir);
        } else {
                getrlimit(RLIMIT_NOFILE, &rl);
+               if (rl.rlim_cur > 65535) {
+                       /* A more reasonable value.  Consider that the primary source of
+                        * file descriptors in Asterisk are UDP sockets, of which we are
+                        * limited to 65,535 per address.  We additionally limit that down
+                        * to about 10,000 sockets per protocol.  While the kernel will
+                        * allow us to set the fileno limit higher (up to 4.2 billion),
+                        * there really is no practical reason for it to be that high.
+                        */
+                       rl.rlim_cur = 65535;
+               }
                for (x = n; x < rl.rlim_cur; x++) {
+#ifdef STRICT_COMPAT
                        close(x);
+#else
+                       long flags = fcntl(x, F_GETFD);
+                       if (flags == -1 && errno == EBADF) {
+                               continue;
+                       }
+                       fcntl(x, F_SETFD, flags | FD_CLOEXEC);
+#endif
                }
        }
 }