2 * @brief Track leaked file descriptors.
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
23 #include "fdtracker.h"
25 #ifdef XAPIAN_TESTSUITE_TRACK_FDS
27 #include "safeunistd.h"
28 #include "safedirent.h"
33 #include <cstring> // For memcmp().
35 #include "errno_to_string.h"
37 #include "stringutils.h"
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"
49 # define FD_DIRECTORY "/dev/fd"
53 FDTracker::mark_fd(int fd
)
56 if (size_t(fd
) >= fds
.size())
57 fds
.resize((fd
&~ 31) + 32);
63 FDTracker::check_fd(int fd
) const
65 return size_t(fd
) < fds
.size() && fds
[fd
];
68 FDTracker::~FDTracker()
71 DIR * dir
= static_cast<DIR*>(dir_void
);
79 DIR * dir
= opendir(FD_DIRECTORY
);
80 // Not all platforms have such a directory.
82 dir_void
= static_cast<void*>(dir
);
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
89 struct dirent
* entry
= readdir(dir
);
93 cout
<< "readdir failed: " << errno_to_string(errno
) << '\n';
97 const char * name
= entry
->d_name
;
99 // Ignore at least '.' and '..'.
100 if (name
[0] < '0' || name
[0] > '9')
111 DIR * dir
= static_cast<DIR*>(dir_void
);
112 if (!dir
) return true;
119 struct dirent
* entry
= readdir(dir
);
123 cout
<< "readdir failed: " << errno_to_string(errno
) << '\n';
127 const char * name
= entry
->d_name
;
129 // Ignore at least '.' and '..'.
130 if (name
[0] < '0' || name
[0] > '9')
135 // This fd was already open before the testcase.
139 string proc_symlink
= FD_DIRECTORY
"/";
140 proc_symlink
+= name
;
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).
158 message
.append(buf
, res
);
161 // Mark the leaked fd as used so we don't report it for future tests.
168 #endif // XAPIAN_TESTSUITE_TRACK_FDS