xapian-core  1.4.27
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 
35 #include "errno_to_string.h"
36 #include "str.h"
37 #include "stringutils.h"
38 
39 using namespace std;
40 
41 // Directory to try to read open fds from. If this directory doesn't exist
42 // then fd tracking will just be disabled. It seems "/dev/fd" is the more
43 // common name for this. On Linux and Cygwin, "/dev/fd" is usually a symlink
44 // to "/proc/self/fd", but that symlink can sometimes be missing so prefer
45 // the latter on these platforms.
46 #if defined __linux__ || defined __CYGWIN__
47 # define FD_DIRECTORY "/proc/self/fd"
48 #else
49 # define FD_DIRECTORY "/dev/fd"
50 #endif
51 
52 void
54 {
55  if (fd >= 0) {
56  if (size_t(fd) >= fds.size())
57  fds.resize((fd &~ 31) + 32);
58  fds[fd] = true;
59  }
60 }
61 
62 bool
63 FDTracker::check_fd(int fd) const
64 {
65  return size_t(fd) < fds.size() && fds[fd];
66 }
67 
69 {
70  if (dir_void) {
71  DIR * dir = static_cast<DIR*>(dir_void);
72  closedir(dir);
73  }
74 }
75 
76 void
78 {
79  DIR * dir = opendir(FD_DIRECTORY);
80  // Not all platforms have such a directory.
81  if (!dir) return;
82  dir_void = static_cast<void*>(dir);
83 
84  // The list of fds we get will include the fd inside dir, but that's OK as
85  // we keep dir open while the testcase runs and use it to recheck the open
86  // fds at the end.
87  while (true) {
88  errno = 0;
89  struct dirent * entry = readdir(dir);
90  if (!entry) {
91  if (errno == 0)
92  break;
93  cout << "readdir failed: " << errno_to_string(errno) << '\n';
94  exit(1);
95  }
96 
97  const char * name = entry->d_name;
98 
99  // Ignore at least '.' and '..'.
100  if (name[0] < '0' || name[0] > '9')
101  continue;
102 
103  mark_fd(atoi(name));
104  }
105 }
106 
107 bool
109 {
110  bool ok = true;
111  DIR * dir = static_cast<DIR*>(dir_void);
112  if (!dir) return true;
113  rewinddir(dir);
114 
115  message.resize(0);
116 
117  while (true) {
118  errno = 0;
119  struct dirent * entry = readdir(dir);
120  if (!entry) {
121  if (errno == 0)
122  break;
123  cout << "readdir failed: " << errno_to_string(errno) << '\n';
124  exit(1);
125  }
126 
127  const char * name = entry->d_name;
128 
129  // Ignore at least '.' and '..'.
130  if (name[0] < '0' || name[0] > '9')
131  continue;
132 
133  int fd = atoi(name);
134  if (check_fd(fd)) {
135  // This fd was already open before the testcase.
136  continue;
137  }
138 
139  string proc_symlink = FD_DIRECTORY "/";
140  proc_symlink += name;
141 
142  char buf[1024];
143  // On some systems (Solaris, AIX) the entries aren't symlinks, so
144  // don't complain if readlink() fails.
145  int res = readlink(proc_symlink.c_str(), buf, sizeof(buf));
146  if (res == CONST_STRLEN("/dev/urandom") &&
147  memcmp(buf, "/dev/urandom", CONST_STRLEN("/dev/urandom")) == 0) {
148  // /dev/urandom isn't a real leak - something in the C library
149  // opens it lazily (at least on Linux).
150  mark_fd(fd);
151  continue;
152  }
153 
154  message += ' ';
155  message += str(fd);
156  if (res > 0) {
157  message += " -> ";
158  message.append(buf, res);
159  }
160 
161  // Mark the leaked fd as used so we don't report it for future tests.
162  mark_fd(fd);
163  ok = false;
164  }
165  return ok;
166 }
167 
168 #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:68
#define FD_DIRECTORY
Definition: fdtracker.cc:49
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:77
<unistd.h>, but with compat.
Definition: header.h:151
bool check()
Definition: fdtracker.cc:108
bool check_fd(int fd) const
Definition: fdtracker.cc:63
void mark_fd(int fd)
Definition: fdtracker.cc:53