1 /* SPDX-License-Identifier: LGPL-2.1+ */
4 #include <sys/resource.h>
6 #include "alloc-util.h"
7 #include "extract-word.h"
9 #include "format-util.h"
12 #include "rlimit-util.h"
13 #include "string-table.h"
14 #include "time-util.h"
16 int setrlimit_closest(int resource
, const struct rlimit
*rlim
) {
17 struct rlimit highest
, fixed
;
21 if (setrlimit(resource
, rlim
) >= 0)
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)
32 /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
34 if (highest
.rlim_max
== RLIM_INFINITY
)
37 fixed
= (struct rlimit
) {
38 .rlim_cur
= MIN(rlim
->rlim_cur
, highest
.rlim_max
),
39 .rlim_max
= MIN(rlim
->rlim_max
, highest
.rlim_max
),
42 /* Shortcut things if we wouldn't change anything. */
43 if (fixed
.rlim_cur
== highest
.rlim_cur
&&
44 fixed
.rlim_max
== highest
.rlim_max
)
47 if (setrlimit(resource
, &fixed
) < 0)
53 int setrlimit_closest_all(const struct rlimit
*const *rlim
, int *which_failed
) {
58 /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
60 for (i
= 0; i
< _RLIMIT_MAX
; i
++) {
64 r
= setrlimit_closest(i
, rlim
[i
]);
79 static int rlimit_parse_u64(const char *val
, rlim_t
*ret
) {
86 if (streq(val
, "infinity")) {
91 /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
92 assert_cc(sizeof(rlim_t
) == sizeof(uint64_t));
94 r
= safe_atou64(val
, &u
);
97 if (u
>= (uint64_t) RLIM_INFINITY
)
104 static int rlimit_parse_size(const char *val
, rlim_t
*ret
) {
111 if (streq(val
, "infinity")) {
112 *ret
= RLIM_INFINITY
;
116 r
= parse_size(val
, 1024, &u
);
119 if (u
>= (uint64_t) RLIM_INFINITY
)
126 static int rlimit_parse_sec(const char *val
, rlim_t
*ret
) {
134 if (streq(val
, "infinity")) {
135 *ret
= RLIM_INFINITY
;
139 r
= parse_sec(val
, &t
);
142 if (t
== USEC_INFINITY
) {
143 *ret
= RLIM_INFINITY
;
147 u
= (uint64_t) DIV_ROUND_UP(t
, USEC_PER_SEC
);
148 if (u
>= (uint64_t) RLIM_INFINITY
)
155 static int rlimit_parse_usec(const char *val
, rlim_t
*ret
) {
162 if (streq(val
, "infinity")) {
163 *ret
= RLIM_INFINITY
;
167 r
= parse_time(val
, &t
, 1);
170 if (t
== USEC_INFINITY
) {
171 *ret
= RLIM_INFINITY
;
179 static int rlimit_parse_nice(const char *val
, rlim_t
*ret
) {
183 /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
184 * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
185 * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
186 * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
187 * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
189 * Yeah, Linux is quality engineering sometimes... */
193 /* Prefixed with "+": Parse as positive user-friendly nice value */
194 r
= safe_atou64(val
+ 1, &rl
);
203 } else if (val
[0] == '-') {
205 /* Prefixed with "-": Parse as negative user-friendly nice value */
206 r
= safe_atou64(val
+ 1, &rl
);
210 if (rl
> (uint64_t) (-PRIO_MIN
))
216 /* Not prefixed: parse as raw resource limit value */
217 r
= safe_atou64(val
, &rl
);
221 if (rl
> (uint64_t) (20 - PRIO_MIN
))
229 static int (*const rlimit_parse_table
[_RLIMIT_MAX
])(const char *val
, rlim_t
*ret
) = {
230 [RLIMIT_CPU
] = rlimit_parse_sec
,
231 [RLIMIT_FSIZE
] = rlimit_parse_size
,
232 [RLIMIT_DATA
] = rlimit_parse_size
,
233 [RLIMIT_STACK
] = rlimit_parse_size
,
234 [RLIMIT_CORE
] = rlimit_parse_size
,
235 [RLIMIT_RSS
] = rlimit_parse_size
,
236 [RLIMIT_NOFILE
] = rlimit_parse_u64
,
237 [RLIMIT_AS
] = rlimit_parse_size
,
238 [RLIMIT_NPROC
] = rlimit_parse_u64
,
239 [RLIMIT_MEMLOCK
] = rlimit_parse_size
,
240 [RLIMIT_LOCKS
] = rlimit_parse_u64
,
241 [RLIMIT_SIGPENDING
] = rlimit_parse_u64
,
242 [RLIMIT_MSGQUEUE
] = rlimit_parse_size
,
243 [RLIMIT_NICE
] = rlimit_parse_nice
,
244 [RLIMIT_RTPRIO
] = rlimit_parse_u64
,
245 [RLIMIT_RTTIME
] = rlimit_parse_usec
,
248 int rlimit_parse_one(int resource
, const char *val
, rlim_t
*ret
) {
254 if (resource
>= _RLIMIT_MAX
)
257 return rlimit_parse_table
[resource
](val
, ret
);
260 int rlimit_parse(int resource
, const char *val
, struct rlimit
*ret
) {
261 _cleanup_free_
char *hard
= NULL
, *soft
= NULL
;
268 r
= extract_first_word(&val
, &soft
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
274 r
= rlimit_parse_one(resource
, soft
, &sl
);
278 r
= extract_first_word(&val
, &hard
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
286 r
= rlimit_parse_one(resource
, hard
, &hl
);
293 *ret
= (struct rlimit
) {
301 int rlimit_format(const struct rlimit
*rl
, char **ret
) {
307 if (rl
->rlim_cur
>= RLIM_INFINITY
&& rl
->rlim_max
>= RLIM_INFINITY
)
308 s
= strdup("infinity");
309 else if (rl
->rlim_cur
>= RLIM_INFINITY
)
310 (void) asprintf(&s
, "infinity:" RLIM_FMT
, rl
->rlim_max
);
311 else if (rl
->rlim_max
>= RLIM_INFINITY
)
312 (void) asprintf(&s
, RLIM_FMT
":infinity", rl
->rlim_cur
);
313 else if (rl
->rlim_cur
== rl
->rlim_max
)
314 (void) asprintf(&s
, RLIM_FMT
, rl
->rlim_cur
);
316 (void) asprintf(&s
, RLIM_FMT
":" RLIM_FMT
, rl
->rlim_cur
, rl
->rlim_max
);
325 static const char* const rlimit_table
[_RLIMIT_MAX
] = {
327 [RLIMIT_CORE
] = "CORE",
328 [RLIMIT_CPU
] = "CPU",
329 [RLIMIT_DATA
] = "DATA",
330 [RLIMIT_FSIZE
] = "FSIZE",
331 [RLIMIT_LOCKS
] = "LOCKS",
332 [RLIMIT_MEMLOCK
] = "MEMLOCK",
333 [RLIMIT_MSGQUEUE
] = "MSGQUEUE",
334 [RLIMIT_NICE
] = "NICE",
335 [RLIMIT_NOFILE
] = "NOFILE",
336 [RLIMIT_NPROC
] = "NPROC",
337 [RLIMIT_RSS
] = "RSS",
338 [RLIMIT_RTPRIO
] = "RTPRIO",
339 [RLIMIT_RTTIME
] = "RTTIME",
340 [RLIMIT_SIGPENDING
] = "SIGPENDING",
341 [RLIMIT_STACK
] = "STACK",
344 DEFINE_STRING_TABLE_LOOKUP(rlimit
, int);
346 int rlimit_from_string_harder(const char *s
) {
349 /* The official prefix */
350 suffix
= startswith(s
, "RLIMIT_");
352 return rlimit_from_string(suffix
);
354 /* Our own unit file setting prefix */
355 suffix
= startswith(s
, "Limit");
357 return rlimit_from_string(suffix
);
359 return rlimit_from_string(s
);
362 void rlimit_free_all(struct rlimit
**rl
) {
368 for (i
= 0; i
< _RLIMIT_MAX
; i
++)
369 rl
[i
] = mfree(rl
[i
]);
372 int rlimit_nofile_bump(int limit
) {
375 /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
376 * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
377 * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
378 * (i.e. do not use select() — which chokes on fds >= 1024) */
381 limit
= read_nr_open();
386 r
= setrlimit_closest(RLIMIT_NOFILE
, &RLIMIT_MAKE_CONST(limit
));
388 return log_debug_errno(r
, "Failed to set RLIMIT_NOFILE: %m");