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