1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "errno-util.h"
7 #include "extract-word.h"
10 #include "format-util.h"
12 #include "missing_resource.h"
13 #include "process-util.h"
14 #include "rlimit-util.h"
15 #include "string-table.h"
17 #include "time-util.h"
19 int setrlimit_closest(int resource
, const struct rlimit
*rlim
) {
20 struct rlimit highest
, fixed
;
24 if (setrlimit(resource
, rlim
) >= 0)
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)
35 /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
37 if (highest
.rlim_max
== RLIM_INFINITY
)
40 fixed
= (struct rlimit
) {
41 .rlim_cur
= MIN(rlim
->rlim_cur
, highest
.rlim_max
),
42 .rlim_max
= MIN(rlim
->rlim_max
, highest
.rlim_max
),
45 /* Shortcut things if we wouldn't change anything. */
46 if (fixed
.rlim_cur
== highest
.rlim_cur
&&
47 fixed
.rlim_max
== highest
.rlim_max
)
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
);
52 return RET_NERRNO(setrlimit(resource
, &fixed
));
55 int setrlimit_closest_all(const struct rlimit
*const *rlim
, int *which_failed
) {
60 /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
62 for (int i
= 0; i
< _RLIMIT_MAX
; i
++) {
66 r
= setrlimit_closest(i
, rlim
[i
]);
81 static int rlimit_parse_u64(const char *val
, rlim_t
*ret
) {
88 if (streq(val
, "infinity")) {
93 /* setrlimit(2) suggests rlim_t is always 64-bit on Linux. */
94 assert_cc(sizeof(rlim_t
) == sizeof(uint64_t));
96 r
= safe_atou64(val
, &u
);
99 if (u
>= (uint64_t) RLIM_INFINITY
)
106 static int rlimit_parse_size(const char *val
, rlim_t
*ret
) {
113 if (streq(val
, "infinity")) {
114 *ret
= RLIM_INFINITY
;
118 r
= parse_size(val
, 1024, &u
);
121 if (u
>= (uint64_t) RLIM_INFINITY
)
128 static int rlimit_parse_sec(const char *val
, rlim_t
*ret
) {
136 if (streq(val
, "infinity")) {
137 *ret
= RLIM_INFINITY
;
141 r
= parse_sec(val
, &t
);
144 if (t
== USEC_INFINITY
) {
145 *ret
= RLIM_INFINITY
;
149 u
= (uint64_t) DIV_ROUND_UP(t
, USEC_PER_SEC
);
150 if (u
>= (uint64_t) RLIM_INFINITY
)
157 static int rlimit_parse_usec(const char *val
, rlim_t
*ret
) {
164 if (streq(val
, "infinity")) {
165 *ret
= RLIM_INFINITY
;
169 r
= parse_time(val
, &t
, 1);
172 if (t
== USEC_INFINITY
) {
173 *ret
= RLIM_INFINITY
;
181 static int rlimit_parse_nice(const char *val
, rlim_t
*ret
) {
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.
191 * Yeah, Linux is quality engineering sometimes... */
195 /* Prefixed with "+": Parse as positive user-friendly nice value */
196 r
= safe_atou64(val
+ 1, &rl
);
205 } else if (val
[0] == '-') {
207 /* Prefixed with "-": Parse as negative user-friendly nice value */
208 r
= safe_atou64(val
+ 1, &rl
);
212 if (rl
> (uint64_t) (-PRIO_MIN
))
218 /* Not prefixed: parse as raw resource limit value */
219 r
= safe_atou64(val
, &rl
);
223 if (rl
> (uint64_t) (20 - PRIO_MIN
))
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
,
250 int rlimit_parse_one(int resource
, const char *val
, rlim_t
*ret
) {
256 if (resource
>= _RLIMIT_MAX
)
259 return rlimit_parse_table
[resource
](val
, ret
);
262 int rlimit_parse(int resource
, const char *val
, struct rlimit
*ret
) {
263 _cleanup_free_
char *hard
= NULL
, *soft
= NULL
;
270 r
= extract_first_word(&val
, &soft
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
276 r
= rlimit_parse_one(resource
, soft
, &sl
);
280 r
= extract_first_word(&val
, &hard
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
288 r
= rlimit_parse_one(resource
, hard
, &hl
);
295 *ret
= (struct rlimit
) {
303 int rlimit_format(const struct rlimit
*rl
, char **ret
) {
304 _cleanup_free_
char *s
= NULL
;
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
);
319 r
= asprintf(&s
, RLIM_FMT
":" RLIM_FMT
, rl
->rlim_cur
, rl
->rlim_max
);
327 static const char* const rlimit_table
[_RLIMIT_MAX
] = {
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",
346 DEFINE_STRING_TABLE_LOOKUP(rlimit
, int);
348 int rlimit_from_string_harder(const char *s
) {
351 /* The official prefix */
352 suffix
= startswith(s
, "RLIMIT_");
354 return rlimit_from_string(suffix
);
356 /* Our own unit file setting prefix */
357 suffix
= startswith(s
, "Limit");
359 return rlimit_from_string(suffix
);
361 return rlimit_from_string(s
);
364 void rlimit_free_all(struct rlimit
**rl
) {
365 free_many((void**) rl
, _RLIMIT_MAX
);
368 int rlimit_copy_all(struct rlimit
* target
[static _RLIMIT_MAX
], struct rlimit
* const source
[static _RLIMIT_MAX
]) {
369 struct rlimit
* copy
[_RLIMIT_MAX
] = {};
374 for (int i
= 0; i
< _RLIMIT_MAX
; i
++) {
378 copy
[i
] = newdup(struct rlimit
, source
[i
], 1);
380 rlimit_free_all(copy
);
385 memcpy(target
, copy
, sizeof(struct rlimit
*) * _RLIMIT_MAX
);
389 int rlimit_nofile_bump(int limit
) {
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) */
398 limit
= read_nr_open();
403 r
= setrlimit_closest(RLIMIT_NOFILE
, &RLIMIT_MAKE_CONST(limit
));
405 return log_debug_errno(r
, "Failed to set RLIMIT_NOFILE: %m");
410 int rlimit_nofile_safe(void) {
413 /* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
416 if (getrlimit(RLIMIT_NOFILE
, &rl
) < 0)
417 return log_debug_errno(errno
, "Failed to query RLIMIT_NOFILE: %m");
419 if (rl
.rlim_cur
<= FD_SETSIZE
)
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
);
433 int pid_getrlimit(pid_t pid
, int resource
, struct rlimit
*ret
) {
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",
456 assert(resource
>= 0);
457 assert(resource
< _RLIMIT_MAX
);
461 if (pid
== 0 || pid
== getpid_cached())
462 return RET_NERRNO(getrlimit(resource
, ret
));
464 r
= RET_NERRNO(prlimit(pid
, resource
, /* new_limit= */ NULL
, ret
));
465 if (!ERRNO_IS_NEG_PRIVILEGE(r
))
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() */
471 const char *p
= procfs_file_alloca(pid
, "limits");
472 _cleanup_free_
char *limits
= NULL
;
474 r
= read_full_virtual_file(p
, &limits
, NULL
);
476 return -EPERM
; /* propagate original permission error if we can't access the limits file */
478 _cleanup_strv_free_
char **l
= NULL
;
479 l
= strv_split(limits
, "\n");
483 STRV_FOREACH(i
, strv_skip(l
, 1)) {
484 _cleanup_free_
char *soft
= NULL
, *hard
= NULL
;
488 e
= startswith(*i
, prefix_table
[resource
]);
495 e
+= strspn(e
, WHITESPACE
);
498 n
= strcspn(e
, WHITESPACE
);
502 soft
= strndup(e
, n
);
510 e
+= strspn(e
, WHITESPACE
);
511 n
= strcspn(e
, WHITESPACE
);
515 hard
= strndup(e
, n
);
519 if (streq(soft
, "unlimited"))
522 r
= safe_atou64(soft
, &sv
);
527 if (streq(hard
, "unlimited"))
530 r
= safe_atou64(hard
, &hv
);
535 *ret
= (struct rlimit
) {
543 return -ENOTRECOVERABLE
;