1 /*
2  * closefrom.c - closefrom() replacement
3  */
4 
5 /***********************************************************************
6  *  Copyright © 2006 Rémi Denis-Courmont.                              *
7  *  This program is free software; you can redistribute and/or modify  *
8  *  it under the terms of the GNU General Public License as published  *
9  *  by the Free Software Foundation; version 2 of the license, or (at  *
10  *  your option) any later version.                                    *
11  *                                                                     *
12  *  This program is distributed in the hope that it will be useful,    *
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               *
15  *  See the GNU General Public License for more details.               *
16  *                                                                     *
17  *  You should have received a copy of the GNU General Public License  *
18  *  along with this program; if not, you can get it from:              *
19  *  http://www.gnu.org/copyleft/gpl.html                               *
20  ***********************************************************************/
21 
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25 
26 #include <sys/types.h>
27 #include <sys/time.h> /* for <sys/resource.h> */
28 #include <sys/resource.h> /* getrlimit() */
29 #include <unistd.h>
30 #include <errno.h> /* errno */
31 #include <sys/select.h> /* FD_SETSIZE */
32 
33 /**
34  * BSD closefrom() replacement.
35  *
36  * We don't handle EINTR error properly;
37  * this replacement is obviously not atomic.
38  */
closefrom(int fd)39 extern int closefrom (int fd)
40 {
41 	struct rlimit lim;
42 	unsigned found = 0;
43 	int saved_errno;
44 
45 	if (getrlimit (RLIMIT_NOFILE, &lim))
46 		return -1;
47 
48 	saved_errno = errno;
49 
50 	/*
51 	 * Make sure closefrom() does not take ages if the file number limit
52 	 * is very big. closefrom() is not supposed to setrlimit(), but it is
53 	 * not standard, neither common (Darwin, Linux don't have it at the
54 	 * moment, and IIRC, FreeBSD and NetBSD neither).
55 	 *
56 	 * Rather than put some completely arbitrary limit, we use FD_SETSIZE.
57 	 * As such we can warranty that subsequent FD_SET() won't overflow.
58 	 * Miredo has a O(1) open file descriptors number behavior anyway. If
59 	 * you want to use this in another project, you should first consider
60 	 * using BSD kqueue/Linux epoll or a portable wrapper of these
61 	 * scalable I/O polling calls, and *THEN* use a higher limit here
62 	 * instead of FD_SETSIZE.
63 	 *
64 	 * Mac OS X returns (2^31 - 1) as its limit, and closefrom() is way
65 	 * too long (and intensive) in this case. Linux usually returns 1024,
66 	 * though root can raise the limit to 1048576.
67 	 */
68 	if (lim.rlim_max > FD_SETSIZE)
69 	{
70 		if (lim.rlim_cur > FD_SETSIZE)
71 			lim.rlim_cur = FD_SETSIZE;
72 		lim.rlim_max = FD_SETSIZE;
73 		setrlimit (RLIMIT_NOFILE, &lim);
74 	}
75 
76 	while ((unsigned)fd < lim.rlim_max)
77 		if (close (fd++) == 0)
78 			found++;
79 
80 	if (found == 0)
81 	{
82 		errno = EBADF;
83 		return -1;
84 	}
85 	errno = saved_errno;
86 	return 0;
87 }
88