1 /*
2 * Copyright (c) 1999, 2021 Tanuki Software, Ltd.
3 * http://www.tanukisoftware.com
4 * All rights reserved.
5 *
6 * This software is the proprietary information of Tanuki Software.
7 * You shall use it only in accordance with the terms of the
8 * license agreement you entered into with Tanuki Software.
9 * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html
10 *
11 *
12 * Portions of the Software have been derived from source code
13 * developed by Silver Egg Technology under the following license:
14 *
15 * Copyright (c) 2001 Silver Egg Technology
16 *
17 * Permission is hereby granted, free of charge, to any person
18 * obtaining a copy of this software and associated documentation
19 * files (the "Software"), to deal in the Software without
20 * restriction, including without limitation the rights to use,
21 * copy, modify, merge, publish, distribute, sub-license, and/or
22 * sell copies of the Software, and to permit persons to whom the
23 * Software is furnished to do so, subject to the following
24 * conditions:
25 *
26 * The above copyright notice and this permission notice shall be
27 * included in all copies or substantial portions of the Software.
28 */
29
30 #ifndef WIN32
31 #include <errno.h>
32 #include "logger.h"
33 #include "property.h"
34 #include "wrapper.h"
35 #include "wrapper_ulimit.h"
36
37 #if defined(LINUX) && defined(__USE_FILE_OFFSET64)
38 #define WRAPPER_RLIM_INFINITY ((unsigned long int)(~0UL))
39 #else
40 #define WRAPPER_RLIM_INFINITY RLIM_INFINITY
41 #endif
42
getResourceProperty(Properties * properties,const TCHAR * propertyName,const int multiplier)43 PResourceLimit getResourceProperty(Properties *properties, const TCHAR *propertyName, const int multiplier) {
44 const TCHAR* value;
45 PResourceLimit result;
46
47 result = malloc(sizeof(ResourceLimit));
48 if (!result) {
49 outOfMemoryQueued(TEXT("GRSP"), 1);
50 } else {
51 result->useCurrent = FALSE;
52 result->useHard = FALSE;
53 result->isValid = FALSE;
54
55 value = getStringProperty(properties, propertyName, TEXT("current"));
56
57 if ((strcmpIgnoreCase(value, TEXT("current")) == 0) || (value[0] == 0)) {
58 result->value = 0;
59 result->useCurrent = TRUE;
60 result->isValid = TRUE;
61 } else if (strcmpIgnoreCase(value, TEXT("hard")) == 0) {
62 result->value = 0;
63 if (_tcsstr(propertyName, TEXT("soft"))) {
64 result->useHard = TRUE;
65 } else {
66 result->useCurrent = TRUE;
67 }
68 result->isValid = TRUE;
69 } else if (strcmpIgnoreCase(value, TEXT("unlimited")) == 0) {
70 result->value = WRAPPER_RLIM_INFINITY;
71 result->isValid = TRUE;
72 } else if (value[0] == TEXT('-')) {
73 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Encountered an invalid value '%s' in the %s property."), value, propertyName);
74 } else {
75 result->value = (rlim_t)(_tcstoul(value, NULL, 10) * multiplier);
76 if (((result->value == 0) && (errno != 0))) {
77 /* Failed to convert to an integer. */
78 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Encountered an invalid value '%s' in the %s property."), value, propertyName);
79 } else {
80 result->isValid = TRUE;
81 }
82 }
83 }
84 return result;
85 }
86
getResourcePropertyPair(Properties * properties,const TCHAR * propertyBaseName,const int multiplier)87 PResourceLimits getResourcePropertyPair(Properties *properties, const TCHAR *propertyBaseName, const int multiplier) {
88 TCHAR propSoft[MAX_PROPERTY_NAME_LENGTH];
89 TCHAR propHard[MAX_PROPERTY_NAME_LENGTH];
90 PResourceLimit softLimit;
91 PResourceLimit hardLimit;
92 PResourceLimits result = NULL;
93
94 _sntprintf(propSoft, MAX_PROPERTY_NAME_LENGTH, TEXT("%s.soft"), propertyBaseName);
95 propSoft[MAX_PROPERTY_NAME_LENGTH-1] = 0;
96
97 _sntprintf(propHard, MAX_PROPERTY_NAME_LENGTH, TEXT("%s.hard"), propertyBaseName);
98 propHard[MAX_PROPERTY_NAME_LENGTH-1] = 0;
99
100 softLimit = getResourceProperty(properties, propSoft, multiplier);
101 hardLimit = getResourceProperty(properties, propHard, multiplier);
102
103 if (softLimit && softLimit->isValid && hardLimit && hardLimit->isValid) {
104 result = malloc(sizeof(ResourceLimits));
105 if (!result) {
106 outOfMemoryQueued(TEXT("GRSPP"), 1);
107 } else {
108 result->rlim_cur = softLimit;
109 result->rlim_max = hardLimit;
110 }
111 } else {
112 if (softLimit) {
113 free(softLimit);
114 }
115 if (hardLimit) {
116 free(hardLimit);
117 }
118 }
119
120 return result;
121 }
122
disposeResourceLimits(PResourceLimits limits)123 void disposeResourceLimits(PResourceLimits limits) {
124 if (limits) {
125 if (limits->rlim_cur) {
126 free(limits->rlim_cur);
127 }
128 if (limits->rlim_max) {
129 free(limits->rlim_max);
130 }
131 free(limits);
132 }
133 }
134
printRlim(rlim_t value,TCHAR * buffer,const int divisor)135 TCHAR* printRlim(rlim_t value, TCHAR* buffer, const int divisor) {
136 /* On Linux 32-bit, the value can be greater than (unsigned long int)(~0UL)
137 * when the compilation option __USE_FILE_OFFSET64 is used. */
138 if (value >= WRAPPER_RLIM_INFINITY) {
139 _sntprintf(buffer, 32, TEXT("unlimited"));
140 } else {
141 _sntprintf(buffer, 32, TEXT("%lu"), (unsigned long)(value/divisor));
142 }
143 buffer[31] = 0;
144 return buffer;
145 }
146
setResourceLimits(int resourceId,const TCHAR * resourceName,const TCHAR * propertyBaseName,PResourceLimits confLimits,int strict,const int divisor)147 int setResourceLimits(int resourceId, const TCHAR* resourceName, const TCHAR* propertyBaseName, PResourceLimits confLimits, int strict, const int divisor) {
148 struct rlimit oldLimits, newLimits, checkLimits;
149 TCHAR limBuf1[32];
150 TCHAR limBuf2[32];
151 TCHAR limBuf3[32];
152 TCHAR limBuf4[32];
153 int logLevel;
154 int errorNum = 0;
155 int setResult;
156
157 if (!confLimits->rlim_cur->useCurrent || !confLimits->rlim_max->useCurrent) {
158 /* The user has specified limits for the number of open file descriptors. */
159 if (!confLimits->rlim_cur->useCurrent && !confLimits->rlim_cur->useHard && !confLimits->rlim_max->useCurrent && (confLimits->rlim_max->value < confLimits->rlim_cur->value)) {
160 /* This is a configuration error, return 1 no matter we are strict or not. */
161 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The soft limit (%s) for %s is set higher than the hard limit (%s)."), printRlim(confLimits->rlim_cur->value, limBuf1, divisor), resourceName, printRlim(confLimits->rlim_max->value, limBuf2, divisor));
162 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Make sure to correctly set the values of the %s.soft and %s.hard properties."), propertyBaseName, propertyBaseName);
163 return 1;
164 }
165
166 /* Get the limits for the resource. */
167 if (getrlimit(resourceId, &oldLimits) != 0) {
168 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to get the limits for %s: (0x%x)"), resourceName, errno);
169 return 1;
170 }
171
172 /* Unless we fail to set the limits for some unknown reason, any error below will return 1 if we are strict, 0 otherwise. */
173 logLevel = strict ? LEVEL_FATAL : properties->logWarningLogLevel;
174
175 /* Resolve the hard limit. */
176 if (confLimits->rlim_max->useCurrent) {
177 /* Use the current value */
178 newLimits.rlim_max = oldLimits.rlim_max;
179 } else {
180 /* Use the configured value */
181 newLimits.rlim_max = confLimits->rlim_max->value;
182 }
183
184 /* Resolve the soft limit. */
185 if (confLimits->rlim_cur->useCurrent) {
186 /* Use the current value */
187 newLimits.rlim_cur = oldLimits.rlim_cur;
188 } else if (confLimits->rlim_cur->useHard) {
189 /* Use the hard value */
190 newLimits.rlim_cur = newLimits.rlim_max;
191 } else {
192 /* Use the configured value */
193 newLimits.rlim_cur = confLimits->rlim_cur->value;
194 }
195
196 /* Resolve cases where the soft limit is greater than the hard limit. */
197 if (newLimits.rlim_max < newLimits.rlim_cur) {
198 if (confLimits->rlim_max->useCurrent) {
199 /* The user has only set the SOFT limit. */
200 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The soft limit (%s) for %s is set higher than the current hard limit (%s)."), printRlim(confLimits->rlim_cur->value, limBuf1, divisor), resourceName, printRlim(oldLimits.rlim_max, limBuf2, divisor));
201 if (strict) {
202 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Make sure to correctly set the value of the %s.soft property."), propertyBaseName);
203 return 1;
204 }
205 newLimits.rlim_cur = oldLimits.rlim_max;
206 } else {
207 /* The user has only set the HARD limit. */
208 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The hard limit (%s) for %s is set lower than the current soft limit (%s)."), printRlim(confLimits->rlim_max->value, limBuf1, divisor), resourceName, printRlim(oldLimits.rlim_cur, limBuf2, divisor));
209 if (strict) {
210 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Make sure to correctly set the value of the %s.hard property."), propertyBaseName);
211 return 1;
212 }
213 newLimits.rlim_cur = newLimits.rlim_max;
214 }
215 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT(" Decreasing the soft limit to the value of the hard limit."));
216 }
217
218 /* Try to set the limits */
219 setResult = setrlimit(resourceId, &newLimits);
220 errorNum = errno;
221 if (setResult == 0) {
222 /* setrlimit() did not return an error but on some platforms, this doesn't mean that the configured values were set correctly.
223 * For example on freeBSD, the limits for the number of open file descriptors can't be raised to a value greater than 1677,
224 * but the function will silently increase the limits up to that maximum value. */
225 if (getrlimit(resourceId, &checkLimits) != 0) {
226 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to get the limits for %s: (0x%x)"), resourceName, errno);
227 return 1;
228 }
229 if (checkLimits.rlim_max < confLimits->rlim_max->value) {
230 /* Mark as an EINVAL error and continue. */
231 errorNum = EINVAL;
232 setResult = -1;
233 } else if (((checkLimits.rlim_max != newLimits.rlim_max) || (checkLimits.rlim_cur != newLimits.rlim_cur))
234 #if defined(LINUX) && defined(__USE_FILE_OFFSET64) && !defined(JSW64)
235 /* On Linux 32-bit, we defined WRAPPER_RLIM_INFINITY to use the max value of unsigned long
236 * (see comment near the definition of WRAPPER_RLIM_INFINITY). However, since we compile
237 * with __USE_FILE_OFFSET64, the real unlimited value is the max of unsigned long long.
238 * setrlimit() will set unlimited limits even with WRAPPER_RLIM_INFINITY being unsigned long,
239 * but getrlimit() will collect greater values (max of unsigned long long). The limits are
240 * set correctly, so just ignore this case. We will no longer need this when using C99. */
241 && !(newLimits.rlim_max == WRAPPER_RLIM_INFINITY && (checkLimits.rlim_max > newLimits.rlim_max))
242 && !(newLimits.rlim_cur == WRAPPER_RLIM_INFINITY && (checkLimits.rlim_cur > newLimits.rlim_cur))
243 #endif
244 ) {
245 /* This should never happen... */
246 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the limits for %s (HARD: expected %s, got %s; SOFT: expected %s, got %s)."),
247 resourceName,
248 printRlim(newLimits.rlim_max , limBuf1, divisor),
249 printRlim(checkLimits.rlim_max, limBuf2, divisor),
250 printRlim(newLimits.rlim_cur , limBuf3, divisor),
251 printRlim(checkLimits.rlim_cur, limBuf4, divisor));
252 return 1;
253 }
254 }
255
256 if (setResult != 0) {
257 /* Resolve cases where the configured hard limit is greater than the current hard limit. */
258 if ((oldLimits.rlim_max < confLimits->rlim_max->value) && ((errorNum == EPERM) || (errorNum == EINVAL))) {
259 if (errorNum == EPERM) {
260 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The process doesn't have sufficient privileges to raise the hard limit (from %s to %s) for %s."), printRlim(oldLimits.rlim_max, limBuf1, divisor), printRlim(confLimits->rlim_max->value, limBuf2, divisor), resourceName);
261 } else {
262 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("Could not raise the hard limit (from %s to %s) for %s."), printRlim(oldLimits.rlim_max, limBuf1, divisor), printRlim(confLimits->rlim_max->value, limBuf2, divisor), resourceName);
263 }
264 if (strict) {
265 if (errorNum == EPERM) {
266 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Please run the Wrapper with sufficient privileges or adjust the value of the %s.hard property."), propertyBaseName);
267 } else {
268 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Please adjust the value of the %s.hard property."), propertyBaseName);
269 }
270 return 1;
271 }
272 newLimits.rlim_max = oldLimits.rlim_max;
273 if (newLimits.rlim_max < newLimits.rlim_cur) {
274 newLimits.rlim_cur = newLimits.rlim_max;
275 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT(" Ignoring the configured hard limit. Decreasing the configured soft limit to the value of the hard limit."));
276 } else {
277 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT(" Ignoring the configured hard limit."));
278 }
279 /* Set again the limits. */
280 if (setrlimit(resourceId, &newLimits) != 0) {
281 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the limits for %s (0x%x)."), resourceName, errno);
282 return 1;
283 }
284 } else {
285 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the limits for %s (0x%x)."), resourceName, errno);
286 return 1;
287 }
288 }
289 }
290
291 return 0;
292 }
293
294 /**
295 * Set the soft and hard resource limits.
296 * For each resource, we can set the limits being strict or not.
297 * - strict: the Wrapper will stop if it is not possible to set the limit as defined in the configuration.
298 * - not strict: the Wrapper will try to adjust the hard and soft limits to be as close as possible to the
299 * configuration and show warnings whenever a property is resolved to a different value.
300 * The constraints are the following: - the soft limit can't be greater than the hard limit.
301 * - the hard limit can only be raised by the root user.
302 *
303 * Returns 0 if no error. Otherwise returns 1.
304 */
loadResourcesLimitsConfiguration()305 int loadResourcesLimitsConfiguration() {
306 PResourceLimits nofileLimits;
307 PResourceLimits dataLimits;
308 int nofileStrict;
309 int dataStrict;
310
311 /* number of open file descriptors */
312 nofileLimits = getResourcePropertyPair(properties, TEXT("wrapper.ulimit.nofile"), 1);
313 if (!nofileLimits) {
314 return 1;
315 }
316 nofileStrict = getBooleanProperty(properties, TEXT("wrapper.ulimit.nofile.strict"), TRUE);
317
318 if (setResourceLimits(RLIMIT_NOFILE, TEXT("the number of open file descriptors"), TEXT("wrapper.ulimit.nofile"), nofileLimits, nofileStrict, 1)) {
319 disposeResourceLimits(nofileLimits);
320 return 1;
321 }
322 disposeResourceLimits(nofileLimits);
323
324 /* size of a process's data segment */
325 dataLimits = getResourcePropertyPair(properties, TEXT("wrapper.ulimit.data"), 1024);
326 if (!dataLimits) {
327 return 1;
328 }
329 dataStrict = getBooleanProperty(properties, TEXT("wrapper.ulimit.data.strict"), TRUE);
330
331 if (setResourceLimits(RLIMIT_DATA, TEXT("the size of a process's data segment"), TEXT("wrapper.ulimit.data"), dataLimits, dataStrict, 1024)) {
332 disposeResourceLimits(dataLimits);
333 return 1;
334 }
335 disposeResourceLimits(dataLimits);
336
337 return 0;
338 }
339
340 /**
341 * Print out the soft and hard resource limits.
342 */
showResourceslimits()343 void showResourceslimits() {
344 struct rlimit limits;
345 TCHAR limBuf1[32];
346 TCHAR limBuf2[32];
347
348 int logLevel = getLogLevelForName(getStringProperty(properties, TEXT("wrapper.ulimit.loglevel"), TEXT("DEBUG")));
349
350 if ((getLowLogLevel() <= logLevel) && (logLevel != LEVEL_NONE)) {
351 if (getrlimit(RLIMIT_NOFILE, &limits) != 0) {
352 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to get the limits for the number of open file descriptors: (0x%x)"), errno);
353 } else {
354 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("Number of open file descriptors limits: %s (soft), %s (hard)."), printRlim(limits.rlim_cur, limBuf1, 1), printRlim(limits.rlim_max, limBuf2, 1));
355 }
356 if (getrlimit(RLIMIT_DATA, &limits) != 0) {
357 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to get the limits for the data segment size: (0x%x)"), errno);
358 } else {
359 log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("Data segment size limits: %s (soft), %s (hard)."), printRlim(limits.rlim_cur, limBuf1, 1024), printRlim(limits.rlim_max, limBuf2, 1024));
360 }
361 }
362 }
363 #endif
364