1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 
5 #include "alloc-util.h"
6 #include "errno-util.h"
7 #include "extract-word.h"
8 #include "fd-util.h"
9 #include "format-util.h"
10 #include "macro.h"
11 #include "missing_resource.h"
12 #include "rlimit-util.h"
13 #include "string-table.h"
14 #include "time-util.h"
15 
setrlimit_closest(int resource,const struct rlimit * rlim)16 int setrlimit_closest(int resource, const struct rlimit *rlim) {
17         struct rlimit highest, fixed;
18 
19         assert(rlim);
20 
21         if (setrlimit(resource, rlim) >= 0)
22                 return 0;
23 
24         if (errno != EPERM)
25                 return -errno;
26 
27         /* So we failed to set the desired setrlimit, then let's try
28          * to get as close as we can */
29         if (getrlimit(resource, &highest) < 0)
30                 return -errno;
31 
32         /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
33          * then */
34         if (highest.rlim_max == RLIM_INFINITY)
35                 return -EPERM;
36 
37         fixed = (struct rlimit) {
38                 .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
39                 .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
40         };
41 
42         /* Shortcut things if we wouldn't change anything. */
43         if (fixed.rlim_cur == highest.rlim_cur &&
44             fixed.rlim_max == highest.rlim_max)
45                 return 0;
46 
47         log_debug("Failed at setting rlimit " RLIM_FMT " for resource RLIMIT_%s. Will attempt setting value " RLIM_FMT " instead.", rlim->rlim_max, rlimit_to_string(resource), fixed.rlim_max);
48 
49         return RET_NERRNO(setrlimit(resource, &fixed));
50 }
51 
setrlimit_closest_all(const struct rlimit * const * rlim,int * which_failed)52 int setrlimit_closest_all(const struct rlimit *const *rlim, int *which_failed) {
53         int r;
54 
55         assert(rlim);
56 
57         /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
58 
59         for (int i = 0; i < _RLIMIT_MAX; i++) {
60                 if (!rlim[i])
61                         continue;
62 
63                 r = setrlimit_closest(i, rlim[i]);
64                 if (r < 0) {
65                         if (which_failed)
66                                 *which_failed = i;
67 
68                         return r;
69                 }
70         }
71 
72         if (which_failed)
73                 *which_failed = -1;
74 
75         return 0;
76 }
77 
rlimit_parse_u64(const char * val,rlim_t * ret)78 static int rlimit_parse_u64(const char *val, rlim_t *ret) {
79         uint64_t u;
80         int r;
81 
82         assert(val);
83         assert(ret);
84 
85         if (streq(val, "infinity")) {
86                 *ret = RLIM_INFINITY;
87                 return 0;
88         }
89 
90         /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
91         assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
92 
93         r = safe_atou64(val, &u);
94         if (r < 0)
95                 return r;
96         if (u >= (uint64_t) RLIM_INFINITY)
97                 return -ERANGE;
98 
99         *ret = (rlim_t) u;
100         return 0;
101 }
102 
rlimit_parse_size(const char * val,rlim_t * ret)103 static int rlimit_parse_size(const char *val, rlim_t *ret) {
104         uint64_t u;
105         int r;
106 
107         assert(val);
108         assert(ret);
109 
110         if (streq(val, "infinity")) {
111                 *ret = RLIM_INFINITY;
112                 return 0;
113         }
114 
115         r = parse_size(val, 1024, &u);
116         if (r < 0)
117                 return r;
118         if (u >= (uint64_t) RLIM_INFINITY)
119                 return -ERANGE;
120 
121         *ret = (rlim_t) u;
122         return 0;
123 }
124 
rlimit_parse_sec(const char * val,rlim_t * ret)125 static int rlimit_parse_sec(const char *val, rlim_t *ret) {
126         uint64_t u;
127         usec_t t;
128         int r;
129 
130         assert(val);
131         assert(ret);
132 
133         if (streq(val, "infinity")) {
134                 *ret = RLIM_INFINITY;
135                 return 0;
136         }
137 
138         r = parse_sec(val, &t);
139         if (r < 0)
140                 return r;
141         if (t == USEC_INFINITY) {
142                 *ret = RLIM_INFINITY;
143                 return 0;
144         }
145 
146         u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
147         if (u >= (uint64_t) RLIM_INFINITY)
148                 return -ERANGE;
149 
150         *ret = (rlim_t) u;
151         return 0;
152 }
153 
rlimit_parse_usec(const char * val,rlim_t * ret)154 static int rlimit_parse_usec(const char *val, rlim_t *ret) {
155         usec_t t;
156         int r;
157 
158         assert(val);
159         assert(ret);
160 
161         if (streq(val, "infinity")) {
162                 *ret = RLIM_INFINITY;
163                 return 0;
164         }
165 
166         r = parse_time(val, &t, 1);
167         if (r < 0)
168                 return r;
169         if (t == USEC_INFINITY) {
170                 *ret = RLIM_INFINITY;
171                 return 0;
172         }
173 
174         *ret = (rlim_t) t;
175         return 0;
176 }
177 
rlimit_parse_nice(const char * val,rlim_t * ret)178 static int rlimit_parse_nice(const char *val, rlim_t *ret) {
179         uint64_t rl;
180         int r;
181 
182         /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
183          * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
184          * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
185          * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
186          * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
187          *
188          * Yeah, Linux is quality engineering sometimes... */
189 
190         if (val[0] == '+') {
191 
192                 /* Prefixed with "+": Parse as positive user-friendly nice value */
193                 r = safe_atou64(val + 1, &rl);
194                 if (r < 0)
195                         return r;
196 
197                 if (rl >= PRIO_MAX)
198                         return -ERANGE;
199 
200                 rl = 20 - rl;
201 
202         } else if (val[0] == '-') {
203 
204                 /* Prefixed with "-": Parse as negative user-friendly nice value */
205                 r = safe_atou64(val + 1, &rl);
206                 if (r < 0)
207                         return r;
208 
209                 if (rl > (uint64_t) (-PRIO_MIN))
210                         return -ERANGE;
211 
212                 rl = 20 + rl;
213         } else {
214 
215                 /* Not prefixed: parse as raw resource limit value */
216                 r = safe_atou64(val, &rl);
217                 if (r < 0)
218                         return r;
219 
220                 if (rl > (uint64_t) (20 - PRIO_MIN))
221                         return -ERANGE;
222         }
223 
224         *ret = (rlim_t) rl;
225         return 0;
226 }
227 
228 static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = {
229         [RLIMIT_CPU] = rlimit_parse_sec,
230         [RLIMIT_FSIZE] = rlimit_parse_size,
231         [RLIMIT_DATA] = rlimit_parse_size,
232         [RLIMIT_STACK] = rlimit_parse_size,
233         [RLIMIT_CORE] = rlimit_parse_size,
234         [RLIMIT_RSS] = rlimit_parse_size,
235         [RLIMIT_NOFILE] = rlimit_parse_u64,
236         [RLIMIT_AS] = rlimit_parse_size,
237         [RLIMIT_NPROC] = rlimit_parse_u64,
238         [RLIMIT_MEMLOCK] = rlimit_parse_size,
239         [RLIMIT_LOCKS] = rlimit_parse_u64,
240         [RLIMIT_SIGPENDING] = rlimit_parse_u64,
241         [RLIMIT_MSGQUEUE] = rlimit_parse_size,
242         [RLIMIT_NICE] = rlimit_parse_nice,
243         [RLIMIT_RTPRIO] = rlimit_parse_u64,
244         [RLIMIT_RTTIME] = rlimit_parse_usec,
245 };
246 
rlimit_parse_one(int resource,const char * val,rlim_t * ret)247 int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
248         assert(val);
249         assert(ret);
250 
251         if (resource < 0)
252                 return -EINVAL;
253         if (resource >= _RLIMIT_MAX)
254                 return -EINVAL;
255 
256         return rlimit_parse_table[resource](val, ret);
257 }
258 
rlimit_parse(int resource,const char * val,struct rlimit * ret)259 int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
260         _cleanup_free_ char *hard = NULL, *soft = NULL;
261         rlim_t hl, sl;
262         int r;
263 
264         assert(val);
265         assert(ret);
266 
267         r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
268         if (r < 0)
269                 return r;
270         if (r == 0)
271                 return -EINVAL;
272 
273         r = rlimit_parse_one(resource, soft, &sl);
274         if (r < 0)
275                 return r;
276 
277         r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
278         if (r < 0)
279                 return r;
280         if (!isempty(val))
281                 return -EINVAL;
282         if (r == 0)
283                 hl = sl;
284         else {
285                 r = rlimit_parse_one(resource, hard, &hl);
286                 if (r < 0)
287                         return r;
288                 if (sl > hl)
289                         return -EILSEQ;
290         }
291 
292         *ret = (struct rlimit) {
293                 .rlim_cur = sl,
294                 .rlim_max = hl,
295         };
296 
297         return 0;
298 }
299 
rlimit_format(const struct rlimit * rl,char ** ret)300 int rlimit_format(const struct rlimit *rl, char **ret) {
301         _cleanup_free_ char *s = NULL;
302         int r;
303 
304         assert(rl);
305         assert(ret);
306 
307         if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
308                 r = free_and_strdup(&s, "infinity");
309         else if (rl->rlim_cur >= RLIM_INFINITY)
310                 r = asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
311         else if (rl->rlim_max >= RLIM_INFINITY)
312                 r = asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
313         else if (rl->rlim_cur == rl->rlim_max)
314                 r = asprintf(&s, RLIM_FMT, rl->rlim_cur);
315         else
316                 r = asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
317         if (r < 0)
318                 return -ENOMEM;
319 
320         *ret = TAKE_PTR(s);
321         return 0;
322 }
323 
324 static const char* const rlimit_table[_RLIMIT_MAX] = {
325         [RLIMIT_AS]         = "AS",
326         [RLIMIT_CORE]       = "CORE",
327         [RLIMIT_CPU]        = "CPU",
328         [RLIMIT_DATA]       = "DATA",
329         [RLIMIT_FSIZE]      = "FSIZE",
330         [RLIMIT_LOCKS]      = "LOCKS",
331         [RLIMIT_MEMLOCK]    = "MEMLOCK",
332         [RLIMIT_MSGQUEUE]   = "MSGQUEUE",
333         [RLIMIT_NICE]       = "NICE",
334         [RLIMIT_NOFILE]     = "NOFILE",
335         [RLIMIT_NPROC]      = "NPROC",
336         [RLIMIT_RSS]        = "RSS",
337         [RLIMIT_RTPRIO]     = "RTPRIO",
338         [RLIMIT_RTTIME]     = "RTTIME",
339         [RLIMIT_SIGPENDING] = "SIGPENDING",
340         [RLIMIT_STACK]      = "STACK",
341 };
342 
343 DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
344 
rlimit_from_string_harder(const char * s)345 int rlimit_from_string_harder(const char *s) {
346         const char *suffix;
347 
348         /* The official prefix */
349         suffix = startswith(s, "RLIMIT_");
350         if (suffix)
351                 return rlimit_from_string(suffix);
352 
353         /* Our own unit file setting prefix */
354         suffix = startswith(s, "Limit");
355         if (suffix)
356                 return rlimit_from_string(suffix);
357 
358         return rlimit_from_string(s);
359 }
360 
rlimit_free_all(struct rlimit ** rl)361 void rlimit_free_all(struct rlimit **rl) {
362         int i;
363 
364         if (!rl)
365                 return;
366 
367         for (i = 0; i < _RLIMIT_MAX; i++)
368                 rl[i] = mfree(rl[i]);
369 }
370 
rlimit_nofile_bump(int limit)371 int rlimit_nofile_bump(int limit) {
372         int r;
373 
374         /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
375          * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
376          * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
377          * (i.e. do not use select() — which chokes on fds >= 1024) */
378 
379         if (limit < 0)
380                 limit = read_nr_open();
381 
382         if (limit < 3)
383                 limit = 3;
384 
385         r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
386         if (r < 0)
387                 return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
388 
389         return 0;
390 }
391 
rlimit_nofile_safe(void)392 int rlimit_nofile_safe(void) {
393         struct rlimit rl;
394 
395         /* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
396          * select() */
397 
398         if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
399                 return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
400 
401         if (rl.rlim_cur <= FD_SETSIZE)
402                 return 0;
403 
404         rl.rlim_cur = FD_SETSIZE;
405         if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
406                 return log_debug_errno(errno, "Failed to lower RLIMIT_NOFILE's soft limit to " RLIM_FMT ": %m", rl.rlim_cur);
407 
408         return 1;
409 }
410