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