]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/rlimit-util.c
Merge pull request #30284 from YHNdnzj/fstab-wantedby-defaultdeps
[thirdparty/systemd.git] / src / basic / rlimit-util.c
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 "fileio.h"
10 #include "format-util.h"
11 #include "macro.h"
12 #include "missing_resource.h"
13 #include "process-util.h"
14 #include "rlimit-util.h"
15 #include "string-table.h"
16 #include "strv.h"
17 #include "time-util.h"
18
19 int setrlimit_closest(int resource, const struct rlimit *rlim) {
20 struct rlimit highest, fixed;
21
22 assert(rlim);
23
24 if (setrlimit(resource, rlim) >= 0)
25 return 0;
26
27 if (errno != EPERM)
28 return -errno;
29
30 /* So we failed to set the desired setrlimit, then let's try
31 * to get as close as we can */
32 if (getrlimit(resource, &highest) < 0)
33 return -errno;
34
35 /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
36 * then */
37 if (highest.rlim_max == RLIM_INFINITY)
38 return -EPERM;
39
40 fixed = (struct rlimit) {
41 .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
42 .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
43 };
44
45 /* Shortcut things if we wouldn't change anything. */
46 if (fixed.rlim_cur == highest.rlim_cur &&
47 fixed.rlim_max == highest.rlim_max)
48 return 0;
49
50 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);
51
52 return RET_NERRNO(setrlimit(resource, &fixed));
53 }
54
55 int setrlimit_closest_all(const struct rlimit *const *rlim, int *which_failed) {
56 int r;
57
58 assert(rlim);
59
60 /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
61
62 for (int i = 0; i < _RLIMIT_MAX; i++) {
63 if (!rlim[i])
64 continue;
65
66 r = setrlimit_closest(i, rlim[i]);
67 if (r < 0) {
68 if (which_failed)
69 *which_failed = i;
70
71 return r;
72 }
73 }
74
75 if (which_failed)
76 *which_failed = -1;
77
78 return 0;
79 }
80
81 static int rlimit_parse_u64(const char *val, rlim_t *ret) {
82 uint64_t u;
83 int r;
84
85 assert(val);
86 assert(ret);
87
88 if (streq(val, "infinity")) {
89 *ret = RLIM_INFINITY;
90 return 0;
91 }
92
93 /* setrlimit(2) suggests rlim_t is always 64-bit on Linux. */
94 assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
95
96 r = safe_atou64(val, &u);
97 if (r < 0)
98 return r;
99 if (u >= (uint64_t) RLIM_INFINITY)
100 return -ERANGE;
101
102 *ret = (rlim_t) u;
103 return 0;
104 }
105
106 static int rlimit_parse_size(const char *val, rlim_t *ret) {
107 uint64_t u;
108 int r;
109
110 assert(val);
111 assert(ret);
112
113 if (streq(val, "infinity")) {
114 *ret = RLIM_INFINITY;
115 return 0;
116 }
117
118 r = parse_size(val, 1024, &u);
119 if (r < 0)
120 return r;
121 if (u >= (uint64_t) RLIM_INFINITY)
122 return -ERANGE;
123
124 *ret = (rlim_t) u;
125 return 0;
126 }
127
128 static int rlimit_parse_sec(const char *val, rlim_t *ret) {
129 uint64_t u;
130 usec_t t;
131 int r;
132
133 assert(val);
134 assert(ret);
135
136 if (streq(val, "infinity")) {
137 *ret = RLIM_INFINITY;
138 return 0;
139 }
140
141 r = parse_sec(val, &t);
142 if (r < 0)
143 return r;
144 if (t == USEC_INFINITY) {
145 *ret = RLIM_INFINITY;
146 return 0;
147 }
148
149 u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
150 if (u >= (uint64_t) RLIM_INFINITY)
151 return -ERANGE;
152
153 *ret = (rlim_t) u;
154 return 0;
155 }
156
157 static int rlimit_parse_usec(const char *val, rlim_t *ret) {
158 usec_t t;
159 int r;
160
161 assert(val);
162 assert(ret);
163
164 if (streq(val, "infinity")) {
165 *ret = RLIM_INFINITY;
166 return 0;
167 }
168
169 r = parse_time(val, &t, 1);
170 if (r < 0)
171 return r;
172 if (t == USEC_INFINITY) {
173 *ret = RLIM_INFINITY;
174 return 0;
175 }
176
177 *ret = (rlim_t) t;
178 return 0;
179 }
180
181 static int rlimit_parse_nice(const char *val, rlim_t *ret) {
182 uint64_t rl;
183 int r;
184
185 /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
186 * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
187 * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
188 * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
189 * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
190 *
191 * Yeah, Linux is quality engineering sometimes... */
192
193 if (val[0] == '+') {
194
195 /* Prefixed with "+": Parse as positive user-friendly nice value */
196 r = safe_atou64(val + 1, &rl);
197 if (r < 0)
198 return r;
199
200 if (rl >= PRIO_MAX)
201 return -ERANGE;
202
203 rl = 20 - rl;
204
205 } else if (val[0] == '-') {
206
207 /* Prefixed with "-": Parse as negative user-friendly nice value */
208 r = safe_atou64(val + 1, &rl);
209 if (r < 0)
210 return r;
211
212 if (rl > (uint64_t) (-PRIO_MIN))
213 return -ERANGE;
214
215 rl = 20 + rl;
216 } else {
217
218 /* Not prefixed: parse as raw resource limit value */
219 r = safe_atou64(val, &rl);
220 if (r < 0)
221 return r;
222
223 if (rl > (uint64_t) (20 - PRIO_MIN))
224 return -ERANGE;
225 }
226
227 *ret = (rlim_t) rl;
228 return 0;
229 }
230
231 static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = {
232 [RLIMIT_CPU] = rlimit_parse_sec,
233 [RLIMIT_FSIZE] = rlimit_parse_size,
234 [RLIMIT_DATA] = rlimit_parse_size,
235 [RLIMIT_STACK] = rlimit_parse_size,
236 [RLIMIT_CORE] = rlimit_parse_size,
237 [RLIMIT_RSS] = rlimit_parse_size,
238 [RLIMIT_NOFILE] = rlimit_parse_u64,
239 [RLIMIT_AS] = rlimit_parse_size,
240 [RLIMIT_NPROC] = rlimit_parse_u64,
241 [RLIMIT_MEMLOCK] = rlimit_parse_size,
242 [RLIMIT_LOCKS] = rlimit_parse_u64,
243 [RLIMIT_SIGPENDING] = rlimit_parse_u64,
244 [RLIMIT_MSGQUEUE] = rlimit_parse_size,
245 [RLIMIT_NICE] = rlimit_parse_nice,
246 [RLIMIT_RTPRIO] = rlimit_parse_u64,
247 [RLIMIT_RTTIME] = rlimit_parse_usec,
248 };
249
250 int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
251 assert(val);
252 assert(ret);
253
254 if (resource < 0)
255 return -EINVAL;
256 if (resource >= _RLIMIT_MAX)
257 return -EINVAL;
258
259 return rlimit_parse_table[resource](val, ret);
260 }
261
262 int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
263 _cleanup_free_ char *hard = NULL, *soft = NULL;
264 rlim_t hl, sl;
265 int r;
266
267 assert(val);
268 assert(ret);
269
270 r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
271 if (r < 0)
272 return r;
273 if (r == 0)
274 return -EINVAL;
275
276 r = rlimit_parse_one(resource, soft, &sl);
277 if (r < 0)
278 return r;
279
280 r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
281 if (r < 0)
282 return r;
283 if (!isempty(val))
284 return -EINVAL;
285 if (r == 0)
286 hl = sl;
287 else {
288 r = rlimit_parse_one(resource, hard, &hl);
289 if (r < 0)
290 return r;
291 if (sl > hl)
292 return -EILSEQ;
293 }
294
295 *ret = (struct rlimit) {
296 .rlim_cur = sl,
297 .rlim_max = hl,
298 };
299
300 return 0;
301 }
302
303 int rlimit_format(const struct rlimit *rl, char **ret) {
304 _cleanup_free_ char *s = NULL;
305 int r;
306
307 assert(rl);
308 assert(ret);
309
310 if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
311 r = free_and_strdup(&s, "infinity");
312 else if (rl->rlim_cur >= RLIM_INFINITY)
313 r = asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
314 else if (rl->rlim_max >= RLIM_INFINITY)
315 r = asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
316 else if (rl->rlim_cur == rl->rlim_max)
317 r = asprintf(&s, RLIM_FMT, rl->rlim_cur);
318 else
319 r = asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
320 if (r < 0)
321 return -ENOMEM;
322
323 *ret = TAKE_PTR(s);
324 return 0;
325 }
326
327 static const char* const rlimit_table[_RLIMIT_MAX] = {
328 [RLIMIT_AS] = "AS",
329 [RLIMIT_CORE] = "CORE",
330 [RLIMIT_CPU] = "CPU",
331 [RLIMIT_DATA] = "DATA",
332 [RLIMIT_FSIZE] = "FSIZE",
333 [RLIMIT_LOCKS] = "LOCKS",
334 [RLIMIT_MEMLOCK] = "MEMLOCK",
335 [RLIMIT_MSGQUEUE] = "MSGQUEUE",
336 [RLIMIT_NICE] = "NICE",
337 [RLIMIT_NOFILE] = "NOFILE",
338 [RLIMIT_NPROC] = "NPROC",
339 [RLIMIT_RSS] = "RSS",
340 [RLIMIT_RTPRIO] = "RTPRIO",
341 [RLIMIT_RTTIME] = "RTTIME",
342 [RLIMIT_SIGPENDING] = "SIGPENDING",
343 [RLIMIT_STACK] = "STACK",
344 };
345
346 DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
347
348 int rlimit_from_string_harder(const char *s) {
349 const char *suffix;
350
351 /* The official prefix */
352 suffix = startswith(s, "RLIMIT_");
353 if (suffix)
354 return rlimit_from_string(suffix);
355
356 /* Our own unit file setting prefix */
357 suffix = startswith(s, "Limit");
358 if (suffix)
359 return rlimit_from_string(suffix);
360
361 return rlimit_from_string(s);
362 }
363
364 void rlimit_free_all(struct rlimit **rl) {
365 free_many((void**) rl, _RLIMIT_MAX);
366 }
367
368 int rlimit_copy_all(struct rlimit* target[static _RLIMIT_MAX], struct rlimit* const source[static _RLIMIT_MAX]) {
369 struct rlimit* copy[_RLIMIT_MAX] = {};
370
371 assert(target);
372 assert(source);
373
374 for (int i = 0; i < _RLIMIT_MAX; i++) {
375 if (!source[i])
376 continue;
377
378 copy[i] = newdup(struct rlimit, source[i], 1);
379 if (!copy[i]) {
380 rlimit_free_all(copy);
381 return -ENOMEM;
382 }
383 }
384
385 memcpy(target, copy, sizeof(struct rlimit*) * _RLIMIT_MAX);
386 return 0;
387 }
388
389 int rlimit_nofile_bump(int limit) {
390 int r;
391
392 /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
393 * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
394 * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
395 * (i.e. do not use select() — which chokes on fds >= 1024) */
396
397 if (limit < 0)
398 limit = read_nr_open();
399
400 if (limit < 3)
401 limit = 3;
402
403 r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
404 if (r < 0)
405 return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
406
407 return 0;
408 }
409
410 int rlimit_nofile_safe(void) {
411 struct rlimit rl;
412
413 /* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
414 * select() */
415
416 if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
417 return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
418
419 if (rl.rlim_cur <= FD_SETSIZE)
420 return 0;
421
422 /* So we might have inherited a hard limit that's larger than the kernel's maximum limit as stored in
423 * /proc/sys/fs/nr_open. If we pass this hard limit unmodified to setrlimit(), we'll get EPERM. To
424 * make sure that doesn't happen, let's limit our hard limit to the value from nr_open. */
425 rl.rlim_max = MIN(rl.rlim_max, (rlim_t) read_nr_open());
426 rl.rlim_cur = MIN((rlim_t) FD_SETSIZE, rl.rlim_max);
427 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
428 return log_debug_errno(errno, "Failed to lower RLIMIT_NOFILE's soft limit to " RLIM_FMT ": %m", rl.rlim_cur);
429
430 return 1;
431 }
432
433 int pid_getrlimit(pid_t pid, int resource, struct rlimit *ret) {
434
435 static const char * const prefix_table[_RLIMIT_MAX] = {
436 [RLIMIT_CPU] = "Max cpu time",
437 [RLIMIT_FSIZE] = "Max file size",
438 [RLIMIT_DATA] = "Max data size",
439 [RLIMIT_STACK] = "Max stack size",
440 [RLIMIT_CORE] = "Max core file size",
441 [RLIMIT_RSS] = "Max resident set",
442 [RLIMIT_NPROC] = "Max processes",
443 [RLIMIT_NOFILE] = "Max open files",
444 [RLIMIT_MEMLOCK] = "Max locked memory",
445 [RLIMIT_AS] = "Max address space",
446 [RLIMIT_LOCKS] = "Max file locks",
447 [RLIMIT_SIGPENDING] = "Max pending signals",
448 [RLIMIT_MSGQUEUE] = "Max msgqueue size",
449 [RLIMIT_NICE] = "Max nice priority",
450 [RLIMIT_RTPRIO] = "Max realtime priority",
451 [RLIMIT_RTTIME] = "Max realtime timeout",
452 };
453
454 int r;
455
456 assert(resource >= 0);
457 assert(resource < _RLIMIT_MAX);
458 assert(pid >= 0);
459 assert(ret);
460
461 if (pid == 0 || pid == getpid_cached())
462 return RET_NERRNO(getrlimit(resource, ret));
463
464 r = RET_NERRNO(prlimit(pid, resource, /* new_limit= */ NULL, ret));
465 if (!ERRNO_IS_NEG_PRIVILEGE(r))
466 return r;
467
468 /* We don't have access? Then try to go via /proc/$PID/limits. Weirdly that's world readable in
469 * contrast to querying the data via prlimit() */
470
471 const char *p = procfs_file_alloca(pid, "limits");
472 _cleanup_free_ char *limits = NULL;
473
474 r = read_full_virtual_file(p, &limits, NULL);
475 if (r < 0)
476 return -EPERM; /* propagate original permission error if we can't access the limits file */
477
478 _cleanup_strv_free_ char **l = NULL;
479 l = strv_split(limits, "\n");
480 if (!l)
481 return -ENOMEM;
482
483 STRV_FOREACH(i, strv_skip(l, 1)) {
484 _cleanup_free_ char *soft = NULL, *hard = NULL;
485 uint64_t sv, hv;
486 const char *e;
487
488 e = startswith(*i, prefix_table[resource]);
489 if (!e)
490 continue;
491
492 if (*e != ' ')
493 continue;
494
495 e += strspn(e, WHITESPACE);
496
497 size_t n;
498 n = strcspn(e, WHITESPACE);
499 if (n == 0)
500 continue;
501
502 soft = strndup(e, n);
503 if (!soft)
504 return -ENOMEM;
505
506 e += n;
507 if (*e != ' ')
508 continue;
509
510 e += strspn(e, WHITESPACE);
511 n = strcspn(e, WHITESPACE);
512 if (n == 0)
513 continue;
514
515 hard = strndup(e, n);
516 if (!hard)
517 return -ENOMEM;
518
519 if (streq(soft, "unlimited"))
520 sv = RLIM_INFINITY;
521 else {
522 r = safe_atou64(soft, &sv);
523 if (r < 0)
524 return r;
525 }
526
527 if (streq(hard, "unlimited"))
528 hv = RLIM_INFINITY;
529 else {
530 r = safe_atou64(hard, &hv);
531 if (r < 0)
532 return r;
533 }
534
535 *ret = (struct rlimit) {
536 .rlim_cur = sv,
537 .rlim_max = hv,
538 };
539
540 return 0;
541 }
542
543 return -ENOTRECOVERABLE;
544 }