Make testsuite fd leak check work on more platforms
[xapian.git] / xapian-core / tests / harness / fdtracker.cc
blob00c9928d60f9e8dd69b4480c34ae9a298b0a0f5f
1 /** @file fdtracker.cc
2 * @brief Track leaked file descriptors.
3 */
4 /* Copyright (C) 2010,2014,2015,2018 Olly Betts
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.
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.
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
21 #include <config.h>
23 #include "fdtracker.h"
25 #ifdef XAPIAN_TESTSUITE_TRACK_FDS
27 #include "safeunistd.h"
28 #include "safedirent.h"
29 #include "safeerrno.h"
31 #include <iostream>
32 #include <cstdlib>
33 #include <cstring> // For strerror().
34 #include <set>
36 #include "str.h"
37 #include "stringutils.h"
39 using namespace std;
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
52 FDTracker::~FDTracker()
54 if (dir_void) {
55 DIR * dir = static_cast<DIR*>(dir_void);
56 closedir(dir);
60 void
61 FDTracker::init()
63 DIR * dir = opendir(FD_DIRECTORY);
64 // Not all platforms have such a directory.
65 if (!dir) return;
66 dir_void = static_cast<void*>(dir);
68 while (true) {
69 errno = 0;
70 struct dirent * entry = readdir(dir);
71 if (!entry) {
72 if (errno == 0)
73 break;
74 cout << "readdir failed: " << strerror(errno) << endl;
75 exit(1);
78 const char * name = entry->d_name;
80 // Ignore at least '.' and '..'.
81 if (name[0] < '0' || name[0] > '9')
82 continue;
84 int fd = atoi(name);
85 fds.insert(fd);
89 bool
90 FDTracker::check()
92 bool ok = true;
93 DIR * dir = static_cast<DIR*>(dir_void);
94 if (!dir) return true;
95 rewinddir(dir);
97 message.resize(0);
99 while (true) {
100 errno = 0;
101 struct dirent * entry = readdir(dir);
102 if (!entry) {
103 if (errno == 0)
104 break;
105 cout << "readdir failed: " << strerror(errno) << endl;
106 exit(1);
109 const char * name = entry->d_name;
111 // Ignore at least '.' and '..'.
112 if (name[0] < '0' || name[0] > '9')
113 continue;
115 int fd = atoi(name);
116 if (fds.find(fd) != fds.end()) continue;
118 string proc_symlink = FD_DIRECTORY "/";
119 proc_symlink += name;
121 char buf[1024];
122 // On some systems (Solaris, AIX) the entries aren't symlinks, so
123 // don't complain if readlink() fails.
124 int res = readlink(proc_symlink.c_str(), buf, sizeof(buf));
125 if (res == CONST_STRLEN("/dev/urandom") &&
126 memcmp(buf, "/dev/urandom", CONST_STRLEN("/dev/urandom")) == 0) {
127 // /dev/urandom isn't a real leak - something in the C library
128 // opens it lazily (at least on Linux).
129 fds.insert(fd);
130 continue;
133 message += ' ';
134 message += str(fd);
135 if (res > 0) {
136 message += " -> ";
137 message.append(buf, res);
140 // Insert the leaked fd so we don't report it for future tests.
141 fds.insert(fd);
142 ok = false;
144 return ok;
147 #endif // XAPIAN_TESTSUITE_TRACK_FDS