xapian-core  1.4.20
fdtracker.cc
Go to the documentation of this file.
1 
4 /* Copyright (C) 2010,2014,2015,2018 Olly Betts
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include <config.h>
22 
23 #include "fdtracker.h"
24 
25 #ifdef XAPIAN_TESTSUITE_TRACK_FDS
26 
27 #include "safeunistd.h"
28 #include "safedirent.h"
29 
30 #include <iostream>
31 #include <cerrno>
32 #include <cstdlib>
33 #include <cstring> // For memcmp().
34 #include <set>
35 
36 #include "errno_to_string.h"
37 #include "str.h"
38 #include "stringutils.h"
39 
40 using namespace std;
41 
42 // Directory to try to read open fds from. If this directory doesn't exist
43 // then fd tracking will just be disabled. It seems "/dev/fd" is the more
44 // common name for this. On Linux and Cygwin, "/dev/fd" is usually a symlink
45 // to "/proc/self/fd", but that symlink can sometimes be missing so prefer
46 // the latter on these platforms.
47 #if defined __linux__ || defined __CYGWIN__
48 # define FD_DIRECTORY "/proc/self/fd"
49 #else
50 # define FD_DIRECTORY "/dev/fd"
51 #endif
52 
53 void
55 {
56  if (fd >= 0) {
57  if (size_t(fd) >= fds.size())
58  fds.resize((fd &~ 31) + 32);
59  fds[fd] = true;
60  }
61 }
62 
63 bool
64 FDTracker::check_fd(int fd) const
65 {
66  return size_t(fd) < fds.size() && fds[fd];
67 }
68 
70 {
71  if (dir_void) {
72  DIR * dir = static_cast<DIR*>(dir_void);
73  closedir(dir);
74  }
75 }
76 
77 void
79 {
80  DIR * dir = opendir(FD_DIRECTORY);
81  // Not all platforms have such a directory.
82  if (!dir) return;
83  dir_void = static_cast<void*>(dir);
84 
85  // The list of fds we get will include the fd inside dir, but that's OK as
86  // we keep dir open while the testcase runs and use it to recheck the open
87  // fds at the end.
88  while (true) {
89  errno = 0;
90  struct dirent * entry = readdir(dir);
91  if (!entry) {
92  if (errno == 0)
93  break;
94  cout << "readdir failed: " << errno_to_string(errno) << endl;
95  exit(1);
96  }
97 
98  const char * name = entry->d_name;
99 
100  // Ignore at least '.' and '..'.
101  if (name[0] < '0' || name[0] > '9')
102  continue;
103 
104  mark_fd(atoi(name));
105  }
106 }
107 
108 bool
110 {
111  bool ok = true;
112  DIR * dir = static_cast<DIR*>(dir_void);
113  if (!dir) return true;
114  rewinddir(dir);
115 
116  message.resize(0);
117 
118  while (true) {
119  errno = 0;
120  struct dirent * entry = readdir(dir);
121  if (!entry) {
122  if (errno == 0)
123  break;
124  cout << "readdir failed: " << errno_to_string(errno) << endl;
125  exit(1);
126  }
127 
128  const char * name = entry->d_name;
129 
130  // Ignore at least '.' and '..'.
131  if (name[0] < '0' || name[0] > '9')
132  continue;
133 
134  int fd = atoi(name);
135  if (check_fd(fd)) {
136  // This fd was already open before the testcase.
137  continue;
138  }
139 
140  string proc_symlink = FD_DIRECTORY "/";
141  proc_symlink += name;
142 
143  char buf[1024];
144  // On some systems (Solaris, AIX) the entries aren't symlinks, so
145  // don't complain if readlink() fails.
146  int res = readlink(proc_symlink.c_str(), buf, sizeof(buf));
147  if (res == CONST_STRLEN("/dev/urandom") &&
148  memcmp(buf, "/dev/urandom", CONST_STRLEN("/dev/urandom")) == 0) {
149  // /dev/urandom isn't a real leak - something in the C library
150  // opens it lazily (at least on Linux).
151  mark_fd(fd);
152  continue;
153  }
154 
155  message += ' ';
156  message += str(fd);
157  if (res > 0) {
158  message += " -> ";
159  message.append(buf, res);
160  }
161 
162  // Mark the leaked fd as used so we don't report it for future tests.
163  mark_fd(fd);
164  ok = false;
165  }
166  return ok;
167 }
168 
169 #endif // XAPIAN_TESTSUITE_TRACK_FDS
int closedir(DIR *)
Track leaked file descriptors.
Convert errno value to std::string, thread-safe if possible.
struct dirent * readdir(DIR *)
~FDTracker()
Definition: fdtracker.cc:69
#define FD_DIRECTORY
Definition: fdtracker.cc:50
STL namespace.
void rewinddir(DIR *)
include <dirent.h>, with alternative implementation for windows.
Convert types to std::string.
DIR * opendir(const char *)
void errno_to_string(int e, string &s)
char * d_name
Definition: msvc_dirent.h:36
string str(int value)
Convert int to std::string.
Definition: str.cc:90
#define CONST_STRLEN(S)
Returns the length of a string constant.
Definition: stringutils.h:43
struct DIR DIR
Definition: msvc_dirent.h:32
char name[9]
Definition: dbcheck.cc:55
Various handy helpers which std::string really should provide.
void init()
Definition: fdtracker.cc:78
<unistd.h>, but with compat.
Definition: header.h:151
bool check()
Definition: fdtracker.cc:109
bool check_fd(int fd) const
Definition: fdtracker.cc:64
void mark_fd(int fd)
Definition: fdtracker.cc:54