1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7  *
8  * See the COPYRIGHT file distributed with this work for additional
9  * information regarding copyright ownership.
10  */
11 
12 #include <inttypes.h>
13 #include <stdbool.h>
14 #include <sys/resource.h>
15 #include <sys/time.h> /* Required on some systems for <sys/resource.h>. */
16 #include <sys/types.h>
17 
18 #include <isc/platform.h>
19 #include <isc/resource.h>
20 #include <isc/result.h>
21 #include <isc/util.h>
22 
23 #ifdef __linux__
24 #include <linux/fs.h> /* To get the large NR_OPEN. */
25 #endif		      /* ifdef __linux__ */
26 
27 #include "errno2result.h"
28 
29 static isc_result_t
resource2rlim(isc_resource_t resource,int * rlim_resource)30 resource2rlim(isc_resource_t resource, int *rlim_resource) {
31 	isc_result_t result = ISC_R_SUCCESS;
32 
33 	switch (resource) {
34 	case isc_resource_coresize:
35 		*rlim_resource = RLIMIT_CORE;
36 		break;
37 	case isc_resource_cputime:
38 		*rlim_resource = RLIMIT_CPU;
39 		break;
40 	case isc_resource_datasize:
41 		*rlim_resource = RLIMIT_DATA;
42 		break;
43 	case isc_resource_filesize:
44 		*rlim_resource = RLIMIT_FSIZE;
45 		break;
46 	case isc_resource_lockedmemory:
47 #ifdef RLIMIT_MEMLOCK
48 		*rlim_resource = RLIMIT_MEMLOCK;
49 #else  /* ifdef RLIMIT_MEMLOCK */
50 		result = ISC_R_NOTIMPLEMENTED;
51 #endif /* ifdef RLIMIT_MEMLOCK */
52 		break;
53 	case isc_resource_openfiles:
54 #ifdef RLIMIT_NOFILE
55 		*rlim_resource = RLIMIT_NOFILE;
56 #else  /* ifdef RLIMIT_NOFILE */
57 		result = ISC_R_NOTIMPLEMENTED;
58 #endif /* ifdef RLIMIT_NOFILE */
59 		break;
60 	case isc_resource_processes:
61 #ifdef RLIMIT_NPROC
62 		*rlim_resource = RLIMIT_NPROC;
63 #else  /* ifdef RLIMIT_NPROC */
64 		result = ISC_R_NOTIMPLEMENTED;
65 #endif /* ifdef RLIMIT_NPROC */
66 		break;
67 	case isc_resource_residentsize:
68 #ifdef RLIMIT_RSS
69 		*rlim_resource = RLIMIT_RSS;
70 #else  /* ifdef RLIMIT_RSS */
71 		result = ISC_R_NOTIMPLEMENTED;
72 #endif /* ifdef RLIMIT_RSS */
73 		break;
74 	case isc_resource_stacksize:
75 		*rlim_resource = RLIMIT_STACK;
76 		break;
77 	default:
78 		/*
79 		 * This test is not very robust if isc_resource_t
80 		 * changes, but generates a clear assertion message.
81 		 */
82 		REQUIRE(resource >= isc_resource_coresize &&
83 			resource <= isc_resource_stacksize);
84 
85 		result = ISC_R_RANGE;
86 		break;
87 	}
88 
89 	return (result);
90 }
91 
92 isc_result_t
isc_resource_setlimit(isc_resource_t resource,isc_resourcevalue_t value)93 isc_resource_setlimit(isc_resource_t resource, isc_resourcevalue_t value) {
94 	struct rlimit rl;
95 	rlim_t rlim_value;
96 	int unixresult;
97 	int unixresource;
98 	isc_result_t result;
99 
100 	result = resource2rlim(resource, &unixresource);
101 	if (result != ISC_R_SUCCESS) {
102 		return (result);
103 	}
104 
105 	if (value == ISC_RESOURCE_UNLIMITED) {
106 		rlim_value = RLIM_INFINITY;
107 	} else {
108 		/*
109 		 * isc_resourcevalue_t was chosen as an unsigned 64 bit
110 		 * integer so that it could contain the maximum range of
111 		 * reasonable values.  Unfortunately, this exceeds the typical
112 		 * range on Unix systems.  Ensure the range of
113 		 * rlim_t is not overflowed.
114 		 */
115 		isc_resourcevalue_t rlim_max;
116 		bool rlim_t_is_signed = (((double)(rlim_t)-1) < 0);
117 
118 		if (rlim_t_is_signed) {
119 			rlim_max = ~((rlim_t)1 << (sizeof(rlim_t) * 8 - 1));
120 		} else {
121 			rlim_max = (rlim_t)-1;
122 		}
123 
124 		if (value > rlim_max) {
125 			value = rlim_max;
126 		}
127 
128 		rlim_value = value;
129 	}
130 
131 	rl.rlim_cur = rl.rlim_max = rlim_value;
132 	unixresult = setrlimit(unixresource, &rl);
133 
134 	if (unixresult == 0) {
135 		return (ISC_R_SUCCESS);
136 	}
137 
138 #if defined(OPEN_MAX) && defined(__APPLE__)
139 	/*
140 	 * The Darwin kernel doesn't accept RLIM_INFINITY for rlim_cur; the
141 	 * maximum possible value is OPEN_MAX.  BIND8 used to use
142 	 * sysconf(_SC_OPEN_MAX) for such a case, but this value is much
143 	 * smaller than OPEN_MAX and is not really effective.
144 	 */
145 	if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
146 		rl.rlim_cur = OPEN_MAX;
147 		unixresult = setrlimit(unixresource, &rl);
148 		if (unixresult == 0) {
149 			return (ISC_R_SUCCESS);
150 		}
151 	}
152 #elif defined(__linux__)
153 #ifndef NR_OPEN
154 #define NR_OPEN (1024 * 1024)
155 #endif /* ifndef NR_OPEN */
156 
157 	/*
158 	 * Some Linux kernels don't accept RLIM_INFINIT; the maximum
159 	 * possible value is the NR_OPEN defined in linux/fs.h.
160 	 */
161 	if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
162 		rl.rlim_cur = rl.rlim_max = NR_OPEN;
163 		unixresult = setrlimit(unixresource, &rl);
164 		if (unixresult == 0) {
165 			return (ISC_R_SUCCESS);
166 		}
167 	}
168 #endif /* if defined(OPEN_MAX) && defined(__APPLE__) */
169 	if (resource == isc_resource_openfiles && rlim_value == RLIM_INFINITY) {
170 		if (getrlimit(unixresource, &rl) == 0) {
171 			rl.rlim_cur = rl.rlim_max;
172 			unixresult = setrlimit(unixresource, &rl);
173 			if (unixresult == 0) {
174 				return (ISC_R_SUCCESS);
175 			}
176 		}
177 	}
178 	return (isc__errno2result(errno));
179 }
180 
181 isc_result_t
isc_resource_getlimit(isc_resource_t resource,isc_resourcevalue_t * value)182 isc_resource_getlimit(isc_resource_t resource, isc_resourcevalue_t *value) {
183 	int unixresource;
184 	struct rlimit rl;
185 	isc_result_t result;
186 
187 	result = resource2rlim(resource, &unixresource);
188 	if (result != ISC_R_SUCCESS) {
189 		return (result);
190 	}
191 
192 	if (getrlimit(unixresource, &rl) != 0) {
193 		return (isc__errno2result(errno));
194 	}
195 
196 	*value = rl.rlim_max;
197 	return (ISC_R_SUCCESS);
198 }
199 
200 isc_result_t
isc_resource_getcurlimit(isc_resource_t resource,isc_resourcevalue_t * value)201 isc_resource_getcurlimit(isc_resource_t resource, isc_resourcevalue_t *value) {
202 	int unixresource;
203 	struct rlimit rl;
204 	isc_result_t result;
205 
206 	result = resource2rlim(resource, &unixresource);
207 	if (result != ISC_R_SUCCESS) {
208 		return (result);
209 	}
210 
211 	if (getrlimit(unixresource, &rl) != 0) {
212 		return (isc__errno2result(errno));
213 	}
214 
215 	*value = rl.rlim_cur;
216 	return (ISC_R_SUCCESS);
217 }
218