findutils-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Findutils-patches] [PATCH] Fix file descriptor leaks.


From: James Youngman
Subject: [Findutils-patches] [PATCH] Fix file descriptor leaks.
Date: Mon, 29 Mar 2010 11:29:47 +0100

* import-gnulib.config (gnulib_version): Update to more recent
version of gnulib.  Import the cloexec module too.
* lib/fdleak.c: New file; a module for detecting file descriptor
leaks.   Also define an open_cloexec() function.
* lib/fdleak.h: Declare the global functions defined in fdleak.c.
* lib/Makefile.am (libfind_a_SOURCES): Add fdleak.c.
* find/find.c (main): Call remember_non_cloexec_fds in order to
detect file descriptor leaks.
(main): set FD_CLOEXEC on starting_desc.
(safely_chdir_lstat): Likewise for dotfd.
(safely_chdir_nofollow): Likewise for fd.
* find/ftsfind.c (inside_dir): set FD_CLOEXEC on curr_fd.
(main): Call remember_non_cloexec_fds in order to detect file
descriptor leaks.
(main): set FD_CLOEXEC on starting_desc.
* find/pred.c (launch): before we exec the child, call
complain_about_leaky_fds in order to complain about non-FD_CLOEXEC
file descriptors that weren't open when the program was run.
* find/sharefile.c (sharefile_fopen): Set FD_CLOEXEC on the files
we open.
* gnulib-local/lib/save-cwd.c.diff: Patch gnulib to set FD_CLOEXEC
on the file descriptors opened by save_cwd.
* gnulib-local/modules/save-cwd.diff: Hence the save-cwd module
depends on the cloexec module.
* import-gnulib.sh (run_gnulib_tool): Pass the --local-dir option
in order to apply these patches.

Signed-off-by: James Youngman <address@hidden>
---
 ChangeLog                          |   31 ++++
 NEWS                               |    2 +
 find/find.c                        |   19 ++-
 find/ftsfind.c                     |   37 ++---
 find/pred.c                        |    6 +
 find/sharefile.c                   |    2 +
 find/util.c                        |    3 +
 gnulib-local/lib/save-cwd.c.diff   |   21 +++
 gnulib-local/modules/save-cwd.diff |   15 ++
 import-gnulib.config               |    3 +-
 import-gnulib.sh                   |    2 +-
 lib/Makefile.am                    |    2 +-
 lib/dircallback.c                  |    2 +-
 lib/fdleak.c                       |  346 ++++++++++++++++++++++++++++++++++++
 lib/fdleak.h                       |   23 +++
 15 files changed, 479 insertions(+), 35 deletions(-)
 create mode 100644 gnulib-local/lib/save-cwd.c.diff
 create mode 100644 gnulib-local/modules/save-cwd.diff
 create mode 100644 lib/fdleak.c
 create mode 100644 lib/fdleak.h

diff --git a/ChangeLog b/ChangeLog
index 03fffbc..8409fe4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2010-03-29  James Youngman  <address@hidden>
+
+       Fix file descriptor leaks.  This fixes Savannah bug bug #27375.
+       * import-gnulib.config (gnulib_version): Update to more recent
+       version of gnulib.  Import the cloexec module too.
+       * lib/fdleak.c: New file; a module for detecting file descriptor
+       leaks.   Also define an open_cloexec() function.
+       * lib/fdleak.h: Declare the global functions defined in fdleak.c.
+       * lib/Makefile.am (libfind_a_SOURCES): Add fdleak.c.
+       * find/find.c (main): Call remember_non_cloexec_fds in order to
+       detect file descriptor leaks.
+       (main): set FD_CLOEXEC on starting_desc.
+       (safely_chdir_lstat): Likewise for dotfd.
+       (safely_chdir_nofollow): Likewise for fd.
+       * find/ftsfind.c (inside_dir): set FD_CLOEXEC on curr_fd.
+       (main): Call remember_non_cloexec_fds in order to detect file
+       descriptor leaks.
+       (main): set FD_CLOEXEC on starting_desc.
+       * find/pred.c (launch): before we exec the child, call
+       complain_about_leaky_fds in order to complain about non-FD_CLOEXEC
+       file descriptors that weren't open when the program was run.
+       * find/sharefile.c (sharefile_fopen): Set FD_CLOEXEC on the files
+       we open.
+       * gnulib-local/lib/save-cwd.c.diff: Patch gnulib to set FD_CLOEXEC
+       on the file descriptors opened by save_cwd.
+       * gnulib-local/modules/save-cwd.diff: Hence the save-cwd module
+       depends on the cloexec module.
+       * import-gnulib.sh (run_gnulib_tool): Pass the --local-dir option
+       in order to apply these patches.
+       * NEWS: Mention this change.
+
 2010-02-25  James Youngman  <address@hidden>
 
        Explain the problems with "-name a/b" and "-path foo/".
diff --git a/NEWS b/NEWS
index 698d2df..29b78f9 100644
--- a/NEWS
+++ b/NEWS
@@ -15,6 +15,8 @@ GNU findutils NEWS - User visible changes.    -*- outline -*- 
(allout)
 
 #27846: Assertion failure in xargs.c on AIX.
 
+#27375: Open file descriptors leak into child processes.
+
 #27017: find -D opt / -fstype ext3 -print , -quit coredumps
 
 #27328: segfault if the initial exec for "find -exec" fails.
diff --git a/find/find.c b/find/find.c
index 0623680..d07bcb7 100644
--- a/find/find.c
+++ b/find/find.c
@@ -51,6 +51,7 @@
 #include "quotearg.h"
 #include "xgetcwd.h"
 #include "error.h"
+#include "fdleak.h"
 
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
@@ -130,6 +131,8 @@ main (int argc, char **argv)
   program_name = argv[0];
   state.exit_status = 0;
 
+  remember_non_cloexec_fds ();
+
   state.shared_files = sharefile_init("w");
   if (NULL == state.shared_files)
     {
@@ -188,11 +191,11 @@ main (int argc, char **argv)
     }
 
 
-  starting_desc = open (".", O_RDONLY
+  starting_desc = open_cloexec (".", O_RDONLY
 #if defined O_LARGEFILE
-                       |O_LARGEFILE
+                               |O_LARGEFILE
 #endif
-                       );
+                               );
   if (0 <= starting_desc && fchdir (starting_desc) != 0)
     {
       close (starting_desc);
@@ -205,6 +208,7 @@ main (int argc, char **argv)
       if (! starting_dir)
        error (1, errno, _("cannot get current directory"));
     }
+
   set_stat_placeholders(&starting_stat_buf);
   if ((*options.xstat) (".", &starting_stat_buf) != 0)
     error (1, errno, _("cannot stat current directory"));
@@ -583,11 +587,11 @@ safely_chdir_lstat(const char *dest,
 
   saved_errno = errno = 0;
 
-  dotfd = open(".", O_RDONLY
+  dotfd = open_cloexec (".", O_RDONLY
 #if defined O_LARGEFILE
-              |O_LARGEFILE
+                       |O_LARGEFILE
 #endif
-              );
+                       );
 
   /* We jump back to here if wd_sanity_check()
    * recoverably triggers an alert.
@@ -825,6 +829,9 @@ safely_chdir_nofollow(const char *dest,
 #if defined O_LARGEFILE
            |O_LARGEFILE
 #endif
+#if defined O_CLOEXEC
+           |O_CLOEXEC
+#endif
            |extraflags);
   if (fd < 0)
     {
diff --git a/find/ftsfind.c b/find/ftsfind.c
index eb0c8c1..328d03c 100644
--- a/find/ftsfind.c
+++ b/find/ftsfind.c
@@ -53,6 +53,8 @@
 #include "xgetcwd.h"
 #include "error.h"
 #include "dircallback.h"
+#include "cloexec.h"
+#include "fdleak.h"
 
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
@@ -74,21 +76,6 @@
 #endif
 
 
-static void set_close_on_exec(int fd)
-{
-#if defined F_GETFD && defined FD_CLOEXEC
-  int flags;
-  flags = fcntl(fd, F_GETFD);
-  if (flags >= 0)
-    {
-      flags |= FD_CLOEXEC;
-      fcntl(fd, F_SETFD, flags);
-    }
-#endif
-}
-
-
-
 /* FTS_TIGHT_CYCLE_CHECK tries to work around Savannah bug #17877
  * (but actually using it doesn't fix the bug).
  */
@@ -151,8 +138,7 @@ static void inside_dir(int dir_fd)
            }
          else if (dir_fd >= 0)
            {
-             curr_fd = dup(dir_fd);
-             set_close_on_exec(curr_fd);
+             curr_fd = dup_cloexec (dir_fd);
            }
          else
            {
@@ -586,13 +572,12 @@ consider_visiting(FTS *p, FTSENT *ent)
 
 
 static void
-find(char *arg)
+find (char *arg)
 {
   char * arglist[2];
   FTS *p;
   FTSENT *ent;
 
-
   state.starting_path_length = strlen(arg);
   inside_dir(AT_FDCWD);
 
@@ -617,7 +602,7 @@ find(char *arg)
   if (options.stay_on_filesystem)
     ftsoptions |= FTS_XDEV;
 
-  p = fts_open(arglist, ftsoptions, NULL);
+  p = fts_open (arglist, ftsoptions, NULL);
   if (NULL == p)
     {
       error (0, errno, _("cannot search %s"),
@@ -647,7 +632,7 @@ process_all_startpoints(int argc, char *argv[])
   for (i = 0; i < argc && !looks_like_expression(argv[i], true); i++)
     {
       state.starting_path_length = strlen(argv[i]); /* TODO: is this 
redundant? */
-      find(argv[i]);
+      find (argv[i]);
     }
 
   if (i == 0)
@@ -677,6 +662,9 @@ main (int argc, char **argv)
   state.execdirs_outstanding = false;
   state.cwd_dir_fd = AT_FDCWD;
 
+  remember_non_cloexec_fds ();
+
+
   state.shared_files = sharefile_init("w");
   if (NULL == state.shared_files)
     {
@@ -732,11 +720,11 @@ main (int argc, char **argv)
     }
 
 
-  starting_desc = open (".", O_RDONLY
+  starting_desc = open_cloexec (".", O_RDONLY
 #if defined O_LARGEFILE
-                       |O_LARGEFILE
+                               |O_LARGEFILE
 #endif
-                       );
+                               );
   if (0 <= starting_desc && fchdir (starting_desc) != 0)
     {
       close (starting_desc);
@@ -749,7 +737,6 @@ main (int argc, char **argv)
        error (1, errno, _("cannot get current directory"));
     }
 
-
   process_all_startpoints(argc-end_of_leading_options, 
argv+end_of_leading_options);
 
   /* If "-exec ... {} +" has been used, there may be some
diff --git a/find/pred.c b/find/pred.c
index 087d7e2..1511751 100644
--- a/find/pred.c
+++ b/find/pred.c
@@ -46,6 +46,7 @@
 #include "dircallback.h"
 #include "error.h"
 #include "verify.h"
+#include "fdleak.h"
 
 #if ENABLE_NLS
 # include <libintl.h>
@@ -1988,6 +1989,11 @@ launch (struct buildcmd_control *ctl, void *usercontext, 
int argc, char **argv)
        {
          _exit(1);
        }
+      else
+       {
+         complain_about_leaky_fds ();
+       }
+
       if (bc_args_exceed_testing_limit (argv))
        errno = E2BIG;
       else
diff --git a/find/sharefile.c b/find/sharefile.c
index 8b48046..fa329d4 100644
--- a/find/sharefile.c
+++ b/find/sharefile.c
@@ -28,6 +28,7 @@
 #include "stdio-safer.h"
 #include "hash.h"
 #include "sharefile.h"
+#include "cloexec.h"
 #include "defs.h"
 
 
@@ -160,6 +161,7 @@ sharefile_fopen (sharefile_handle h, const char *filename)
       const int fd = fileno (new_entry->fp);
       assert (fd >= 0);
 
+      set_cloexec_flag (fd, true);
       if (fstat (fd, &st) < 0)
         {
          entry_free (new_entry);
diff --git a/find/util.c b/find/util.c
index 72220a6..62caeec 100644
--- a/find/util.c
+++ b/find/util.c
@@ -465,6 +465,9 @@ cleanup (void)
   if (eval_tree)
     traverse_tree(eval_tree, undangle_file_pointers);
 
+  complain_about_leaky_fds ();
+  forget_non_cloexec_fds ();
+
   if (fflush (stdout) == EOF)
     nonfatal_file_error ("standard output");
 }
diff --git a/gnulib-local/lib/save-cwd.c.diff b/gnulib-local/lib/save-cwd.c.diff
new file mode 100644
index 0000000..7cc6ffe
--- /dev/null
+++ b/gnulib-local/lib/save-cwd.c.diff
@@ -0,0 +1,21 @@
+diff --git a/lib/save-cwd.c b/lib/save-cwd.c
+index 7394d50..cf43a35 100644
+--- a/lib/save-cwd.c
++++ b/lib/save-cwd.c
+@@ -31,6 +31,7 @@
+ #include "chdir-long.h"
+ #include "unistd--.h"
+ #include "xgetcwd.h"
++#include "cloexec.h"
+
+ #if GNULIB_FCNTL_SAFER
+ # include "fcntl--.h"
+@@ -84,6 +85,7 @@ save_cwd (struct saved_cwd *cwd)
+       return cwd->name ? 0 : -1;
+     }
+
++  set_cloexec_flag (cwd->desc, true);
+   return 0;
+ }
+
+
diff --git a/gnulib-local/modules/save-cwd.diff 
b/gnulib-local/modules/save-cwd.diff
new file mode 100644
index 0000000..f4d8334
--- /dev/null
+++ b/gnulib-local/modules/save-cwd.diff
@@ -0,0 +1,15 @@
+diff --git a/modules/save-cwd b/modules/save-cwd
+index 46a1276..aab5e5e 100644
+--- a/modules/save-cwd
++++ b/modules/save-cwd
+@@ -8,0 +8,0 @@ m4/save-cwd.m4
+
+ Depends-on:
+ chdir-long
++cloexec
+ stdbool
+ unistd-safer
+ xgetcwd
+--
+1.5.6.5
+
diff --git a/import-gnulib.config b/import-gnulib.config
index 686a645..cb95383 100644
--- a/import-gnulib.config
+++ b/import-gnulib.config
@@ -1,7 +1,7 @@
 # findutils gnulib.config -*- sh -*-
 
 # What version of gnulib to use?
-gnulib_version="fa073b2cb2db320c5004cde6f473cc8b5fdbd7b3"
+gnulib_version="0771f626ab33a517372f630331d1887252521bcf"
 destdir="gnulib"
 
 # Random extra gnulib files needed for findutils.
@@ -28,6 +28,7 @@ argmatch
 assert
 c-strstr
 canonicalize
+cloexec
 closein
 closeout
 dirname
diff --git a/import-gnulib.sh b/import-gnulib.sh
index 2200714..1786a75 100755
--- a/import-gnulib.sh
+++ b/import-gnulib.sh
@@ -131,7 +131,7 @@ run_gnulib_tool() {
     fi
 
     set -x
-    if "$tool" --import --symlink --with-tests --dir=. --lib=libgnulib 
--source-base=gnulib/lib --m4-base=gnulib/m4 $modules
+    if "$tool" --import --symlink --with-tests --dir=. --lib=libgnulib 
--source-base=gnulib/lib --m4-base=gnulib/m4 --local-dir=gnulib-local $modules
     then
        set +x
     else
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 879f2ff..ce9f9e8 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -36,7 +36,7 @@ LDADD = ../gnulib/lib/libgnulib.a $(LIBINTL)
 libfind_a_SOURCES += modetype.h nextelem.h printquoted.h listfile.h \
        regextype.h dircallback.h
 libfind_a_SOURCES += listfile.c nextelem.c extendbuf.c buildcmd.c 
savedirinfo.c \
-       forcefindlib.c qmark.c printquoted.c regextype.c dircallback.c
+       forcefindlib.c qmark.c printquoted.c regextype.c dircallback.c fdleak.c
 
 EXTRA_DIST += waitpid.c forcefindlib.c
 TESTS_ENVIRONMENT = REGEXPROPS=regexprops$(EXEEXT)
diff --git a/lib/dircallback.c b/lib/dircallback.c
index aa5ecc5..b7bca0a 100644
--- a/lib/dircallback.c
+++ b/lib/dircallback.c
@@ -71,7 +71,7 @@ run_in_dir (int dir_fd, int (*callback)(void*), void 
*usercontext)
          saved_errno = errno;
          free_cwd (&saved_cwd);
          errno = saved_errno;
-      return -1;
+         return -1;
        }
 
       err = (*callback)(usercontext);
diff --git a/lib/fdleak.c b/lib/fdleak.c
new file mode 100644
index 0000000..d211310
--- /dev/null
+++ b/lib/fdleak.c
@@ -0,0 +1,346 @@
+/* fdleak.c -- detect file descriptor leaks
+   Copyright (C) 2010, Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <config.h>
+#include <unistd.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "dirent-safer.h"
+#include "extendbuf.h"
+#include "fdleak.h"
+#include "error.h"
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# define _(Text) Text
+#endif
+#ifdef gettext_noop
+# define N_(String) gettext_noop (String)
+#else
+/* See locate.c for explanation as to why not use (String) */
+# define N_(String) String
+#endif
+
+/* In order to detect FD leaks, we take a snapshot of the open
+ * file descriptors which are not FD_CLOEXEC when the program starts.
+ * When the program exits, we discover if there are any new
+ * file descriptors which aren't FD_CLOEXEC.
+ */
+static int *non_cloexec_fds;
+static size_t num_cloexec_fds;
+
+
+
+/* Determine the value of the largest open fd, on systems that
+ * offer /proc/self/fd. */
+static int
+get_proc_max_fd ()
+{
+  const char *path = "/proc/self/fd";
+  int maxfd = -1;
+  /* We don't use readdir, because we cannot trust pathconf
+   * to tell us the maximum possible length of a path in
+   * a given directory (the manpage for readdir_r claims this
+   * is the approved method, but the manpage for pathconf indicates
+   * that _PC_NAME_MAX is not an upper limit). */
+  DIR *dir = opendir_safer (path);
+  if (dir)
+    {
+      int good = 0;
+      struct dirent *dent;
+
+      while ((dent=readdir (dir)) != NULL)
+       {
+         int fd = -1;
+
+         if (1 == sscanf (dent->d_name, "%d", &fd))
+           {
+             good = 1;
+             if (fd > maxfd)
+               maxfd = fd;
+           }
+       }
+      closedir (dir);
+      if (good)
+       return maxfd;
+    }
+  return -1;
+}
+
+
+
+/* Estimate the value of the largest possible file descriptor */
+static int
+get_max_fd (void)
+{
+  struct rlimit fd_limit;
+  long open_max;
+
+  open_max = get_proc_max_fd ();
+  if (open_max >= 0)
+    return open_max;
+
+  open_max = sysconf (_SC_OPEN_MAX);
+  if (open_max == -1)
+    open_max = _POSIX_OPEN_MAX;        /* underestimate */
+
+  /* There are really only two cases here for the return value,
+     but we keep the conditions separate because a different thing is
+     going on in each case.
+  */
+  if (0 == getrlimit (RLIMIT_NOFILE, &fd_limit))
+    {
+      if (fd_limit.rlim_cur == RLIM_SAVED_MAX)
+       return open_max;
+      else if (fd_limit.rlim_cur == RLIM_SAVED_CUR)
+       return open_max;
+      else if (fd_limit.rlim_cur == RLIM_INFINITY)
+       return open_max;
+      else
+       return (int) fd_limit.rlim_cur;
+    }
+  else
+    {
+      /* cannot determine the limit's value */
+      return open_max;
+    }
+}
+
+
+static int
+visit_open_fds(int fd_min, int fd_max,
+              int (*callback)(int, void*), void *cb_context)
+{
+  enum { MAX_POLL = 64 };
+  struct pollfd pf[MAX_POLL];
+  int rv = 0;
+
+  while (fd_min < fd_max)
+    {
+      int i;
+      int limit = fd_max - fd_min;
+      if (limit > MAX_POLL)
+       limit = MAX_POLL;
+
+      for (i=0; i<limit; i++)
+       {
+         pf[i].events = POLLIN|POLLOUT;
+         pf[i].revents = 0;
+         pf[i].fd = fd_min + i;
+       }
+      rv = poll(pf, limit, 0);
+      if (-1 == rv)
+       {
+         return -1;
+       }
+      else
+       {
+         int i;
+         for (i=0; i<limit; i++)
+           {
+             if (pf[i].revents != POLLNVAL)
+               {
+                 if (0 != (rv = callback (pf[i].fd, cb_context)))
+                   return rv;
+               }
+           }
+       }
+      fd_min += limit;
+    }
+  return 0;
+}
+
+static int
+fd_is_cloexec (int fd)
+{
+  const int flags = fcntl (fd, F_GETFD);
+  return flags & FD_CLOEXEC;
+}
+
+
+/* Faking closures in C is a bit of a pain.  */
+struct remember_fd_context
+{
+  int *buf;
+  size_t used;
+  size_t allocated;
+};
+
+
+/* Record FD is it's not FD_CLOEXEC. */
+static int
+remember_fd_if_non_cloexec (int fd, void *context)
+{
+  if (fd_is_cloexec (fd))
+    {
+      return 0;
+    }
+  else
+    {
+      struct remember_fd_context * const p = context;
+      void *newbuf = extendbuf (p->buf,
+                               sizeof(p->buf[0])*(p->used+1),
+                               &(p->allocated));
+      if (newbuf)
+       {
+         p->buf = newbuf;
+         p->buf[p->used] = fd;
+         ++p->used;
+         return 0;
+       }
+      else
+       {
+         return -1;
+       }
+    }
+}
+
+void
+remember_non_cloexec_fds (void)
+{
+  int max_fd = get_max_fd ();
+  struct remember_fd_context cb_data;
+  cb_data.buf = NULL;
+  cb_data.used = cb_data.allocated = 0;
+
+  if (max_fd < INT_MAX)
+    ++max_fd;
+  visit_open_fds (3, max_fd, remember_fd_if_non_cloexec, &cb_data);
+
+  non_cloexec_fds = cb_data.buf;
+  num_cloexec_fds = cb_data.used;
+}
+
+
+struct fd_leak_context
+{
+  const int *prev_buf;
+  size_t used;
+  size_t lookup_pos;
+  int leaked_fd;
+};
+
+/* FD is open and not close-on-exec.
+ * If it's not in the list of non-cloexec file descriptors we saw before, it's 
a leak.
+ */
+static int
+find_first_leak_callback (int fd, void *context)
+{
+  if (!fd_is_cloexec (fd))
+    {
+      struct fd_leak_context *p = context;
+      while (p->lookup_pos < p->used)
+       {
+         if (p->prev_buf[p->lookup_pos] < fd)
+           {
+             ++p->lookup_pos;
+           }
+         else if (p->prev_buf[p->lookup_pos] == fd)
+           {
+             /* FD was open and still is, it's not a leak. */
+             return 0;
+           }
+         else
+           {
+             break;
+           }
+       }
+      /* We come here if p->prev_buf[p->lookup_pos] > fd, or
+        if we ran out of items in the lookup table.
+        Either way, this is a leak. */
+      p->leaked_fd = fd;
+      return -1;               /* No more callbacks needed. */
+    }
+  return 0;
+}
+
+
+static int
+find_first_leaked_fd (const int* prev_non_cloexec_fds, size_t n)
+{
+  struct fd_leak_context context;
+  int max_fd = get_max_fd ();
+
+  if (max_fd < INT_MAX)
+    ++max_fd;
+  context.prev_buf = prev_non_cloexec_fds;
+  context.used = n;
+  context.lookup_pos = 0;
+  context.leaked_fd = -1;
+  visit_open_fds (3, max_fd, find_first_leak_callback, &context);
+  return context.leaked_fd;
+}
+
+int
+open_cloexec (const char *path, int flags)
+{
+  int fd;
+
+  fd = open (path, flags
+#if defined O_CLOEXEC
+            |O_CLOEXEC
+#endif
+            );
+  if (fd < 0)
+    return fd;
+
+#if !defined O_CLOEXEC
+  make_fd_cloexec (fd);
+#endif
+  return fd;
+}
+
+void
+forget_non_cloexec_fds (void)
+{
+  free (non_cloexec_fds);
+  non_cloexec_fds = NULL;
+  num_cloexec_fds = 0;
+}
+
+
+void
+complain_about_leaky_fds (void)
+{
+  const int leaking_fd = find_first_leaked_fd (non_cloexec_fds, 
num_cloexec_fds);
+
+  if (leaking_fd >= 0)
+    {
+      error (0, 0,
+            _("File descriptor %d will leak; please report this as a bug, "
+              "remembering to include a detailed description of the simplest "
+              "way to reproduce this problem."),
+            leaking_fd);
+      if (0)
+       {
+         char * const args[] = {"/bin/ls", "-l", "/proc/self/fd",
+                                (char*)NULL };
+         execv("/bin/ls", args);
+         perror("exec");
+       }
+      assert (0);
+    }
+}
+
diff --git a/lib/fdleak.h b/lib/fdleak.h
new file mode 100644
index 0000000..47b01cb
--- /dev/null
+++ b/lib/fdleak.h
@@ -0,0 +1,23 @@
+/* fdleak.h -- detect file descriptor leaks
+   Copyright (C) 2010, Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+void remember_non_cloexec_fds (void);
+void forget_non_cloexec_fds (void);
+void complain_about_leaky_fds (void);
+
+int open_cloexec(const char *path, int flags);
+
-- 
1.5.6.5





reply via email to

[Prev in Thread] Current Thread [Next in Thread]