2 * @brief Implementation of closefrom() function.
4 /* Copyright (C) 2010,2011,2012,2016 Olly Betts
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (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 // We don't currently need closefrom() on __WIN32__.
24 #if !defined HAVE_CLOSEFROM && !defined __WIN32__
26 #include "closefrom.h"
28 #include "safeerrno.h"
29 #include "safefcntl.h"
30 #include "safeunistd.h"
32 #ifdef HAVE_SYS_RESOURCE_H
33 # include <sys/types.h>
34 # include <sys/resource.h>
37 #if defined __linux__ || defined __APPLE__
38 # include "safedirent.h"
47 // May only be supported by NetBSD, modern versions of which implement
48 // closefrom(). Leave this in so that if other platforms have it or add
49 // it they will benefit.
50 int maxfd
= fcntl(0, F_MAXFD
);
51 if (maxfd
>= 0) return maxfd
;
55 if (getrlimit(RLIMIT_NOFILE
, &rl
) == 0 &&
56 rl
.rlim_max
!= RLIM_INFINITY
) {
57 return static_cast<int>(rl
.rlim_max
) - 1;
60 return static_cast<int>(sysconf(_SC_OPEN_MAX
)) - 1;
63 // These platforms are known to provide closefrom():
64 // FreeBSD >= 8.0, NetBSD >= 3.0, OpenBSD >= 3.5, Solaris >= 9
66 // These platforms are known to support fcntl() with F_CLOSEM:
67 // AIX, IRIX, NetBSD >= 2.0
69 // These platforms have getdirentries() and a "magic" directory with an entry
70 // for each FD open in the current process:
73 // Other platforms just use a loop up to a limit obtained from
74 // fcntl(0, F_MAXFD), getrlimit(RLIMIT_NOFILE, ...), or sysconf(_SC_OPEN_MAX).
77 Xapian::Internal::closefrom(int fd
)
81 if (fcntl(fd
, F_CLOSEM
, 0) >= 0)
83 #elif defined __linux__ || defined __APPLE__
85 // Some platforms have /proc/<pid>/fd but not /proc/self - if any such
86 // platforms don't have either closefrom() or F_CLOSEM but do have
87 // getdirentries() then this code can be used. AIX is an example of
88 // a platform of the former, but apparently has F_CLOSEM.
89 char path
[6 + sizeof(pid_t
) * 3 + 4];
90 sprintf(path
, "/proc/%ld/fd", long(getpid()));
91 #elif defined __linux__
92 const char * path
= "/proc/self/fd";
93 #elif defined __APPLE__ // Mac OS X
94 const char * path
= "/dev/fd";
96 int dir
= open(path
, O_RDONLY
|O_DIRECTORY
);
102 // We use getdirentries() instead of opendir()/readdir() here
103 // because the latter can call malloc(), which isn't safe to do
104 // between fork() and exec() in a multi-threaded program.
105 ssize_t c
= getdirentries(dir
, buf
, sizeof(buf
), &base
);
111 // Fallback if getdirentries() fails.
115 for (ssize_t pos
= 0; pos
< c
; pos
+= d
->d_reclen
) {
116 d
= reinterpret_cast<struct dirent
*>(buf
+ pos
);
117 const char * leaf
= d
->d_name
;
118 if (leaf
[0] < '0' || leaf
[0] > '9') {
119 // Skip '.' and '..'.
124 // FD below threshold.
128 // Don't close the fd open on the directory.
132 // Running under valgrind causes some entries above the
133 // reported RLIMIT_NOFILE value to appear in
134 // /proc/self/fd - see:
135 // https://bugs.kde.org/show_bug.cgi?id=191758
137 // If we try to close these, valgrind issues a warning about
138 // trying to close an invalid file descriptor. These entries
139 // start at 1024, so we check that value first so we can
140 // usually avoid having to read the fd limit when we're not
141 // running under valgrind.
150 while (close(n
) < 0 && errno
== EINTR
) { }
158 while (fd
<= maxfd
) {
159 // Retry on EINTR; just ignore other errors (we'll get EBADF if fd
160 // isn't open so that's OK).
161 while (close(fd
) < 0 && errno
== EINTR
) { }