1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/resource.h>
24 #include "alloc-util.h"
25 #include "extract-word.h"
26 #include "format-util.h"
29 #include "rlimit-util.h"
30 #include "string-table.h"
31 #include "time-util.h"
33 int setrlimit_closest(int resource
, const struct rlimit
*rlim
) {
34 struct rlimit highest
, fixed
;
38 if (setrlimit(resource
, rlim
) >= 0)
44 /* So we failed to set the desired setrlimit, then let's try
45 * to get as close as we can */
46 if (getrlimit(resource
, &highest
) < 0)
49 fixed
.rlim_cur
= MIN(rlim
->rlim_cur
, highest
.rlim_max
);
50 fixed
.rlim_max
= MIN(rlim
->rlim_max
, highest
.rlim_max
);
52 if (setrlimit(resource
, &fixed
) < 0)
58 static int rlimit_parse_u64(const char *val
, rlim_t
*ret
) {
65 if (streq(val
, "infinity")) {
70 /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
71 assert_cc(sizeof(rlim_t
) == sizeof(uint64_t));
73 r
= safe_atou64(val
, &u
);
76 if (u
>= (uint64_t) RLIM_INFINITY
)
83 static int rlimit_parse_size(const char *val
, rlim_t
*ret
) {
90 if (streq(val
, "infinity")) {
95 r
= parse_size(val
, 1024, &u
);
98 if (u
>= (uint64_t) RLIM_INFINITY
)
105 static int rlimit_parse_sec(const char *val
, rlim_t
*ret
) {
113 if (streq(val
, "infinity")) {
114 *ret
= RLIM_INFINITY
;
118 r
= parse_sec(val
, &t
);
121 if (t
== USEC_INFINITY
) {
122 *ret
= RLIM_INFINITY
;
126 u
= (uint64_t) DIV_ROUND_UP(t
, USEC_PER_SEC
);
127 if (u
>= (uint64_t) RLIM_INFINITY
)
134 static int rlimit_parse_usec(const char *val
, rlim_t
*ret
) {
141 if (streq(val
, "infinity")) {
142 *ret
= RLIM_INFINITY
;
146 r
= parse_time(val
, &t
, 1);
149 if (t
== USEC_INFINITY
) {
150 *ret
= RLIM_INFINITY
;
158 static int rlimit_parse_nice(const char *val
, rlim_t
*ret
) {
162 /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
163 * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
164 * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
165 * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
166 * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
168 * Yeah, Linux is quality engineering sometimes... */
172 /* Prefixed with "+": Parse as positive user-friendly nice value */
173 r
= safe_atou64(val
+ 1, &rl
);
182 } else if (val
[0] == '-') {
184 /* Prefixed with "-": Parse as negative user-friendly nice value */
185 r
= safe_atou64(val
+ 1, &rl
);
189 if (rl
> (uint64_t) (-PRIO_MIN
))
195 /* Not prefixed: parse as raw resource limit value */
196 r
= safe_atou64(val
, &rl
);
200 if (rl
> (uint64_t) (20 - PRIO_MIN
))
208 static int (*const rlimit_parse_table
[_RLIMIT_MAX
])(const char *val
, rlim_t
*ret
) = {
209 [RLIMIT_CPU
] = rlimit_parse_sec
,
210 [RLIMIT_FSIZE
] = rlimit_parse_size
,
211 [RLIMIT_DATA
] = rlimit_parse_size
,
212 [RLIMIT_STACK
] = rlimit_parse_size
,
213 [RLIMIT_CORE
] = rlimit_parse_size
,
214 [RLIMIT_RSS
] = rlimit_parse_size
,
215 [RLIMIT_NOFILE
] = rlimit_parse_u64
,
216 [RLIMIT_AS
] = rlimit_parse_size
,
217 [RLIMIT_NPROC
] = rlimit_parse_u64
,
218 [RLIMIT_MEMLOCK
] = rlimit_parse_size
,
219 [RLIMIT_LOCKS
] = rlimit_parse_u64
,
220 [RLIMIT_SIGPENDING
] = rlimit_parse_u64
,
221 [RLIMIT_MSGQUEUE
] = rlimit_parse_size
,
222 [RLIMIT_NICE
] = rlimit_parse_nice
,
223 [RLIMIT_RTPRIO
] = rlimit_parse_u64
,
224 [RLIMIT_RTTIME
] = rlimit_parse_usec
,
227 int rlimit_parse_one(int resource
, const char *val
, rlim_t
*ret
) {
233 if (resource
>= _RLIMIT_MAX
)
236 return rlimit_parse_table
[resource
](val
, ret
);
239 int rlimit_parse(int resource
, const char *val
, struct rlimit
*ret
) {
240 _cleanup_free_
char *hard
= NULL
, *soft
= NULL
;
247 r
= extract_first_word(&val
, &soft
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
253 r
= rlimit_parse_one(resource
, soft
, &sl
);
257 r
= extract_first_word(&val
, &hard
, ":", EXTRACT_DONT_COALESCE_SEPARATORS
);
265 r
= rlimit_parse_one(resource
, hard
, &hl
);
272 *ret
= (struct rlimit
) {
280 int rlimit_format(const struct rlimit
*rl
, char **ret
) {
286 if (rl
->rlim_cur
>= RLIM_INFINITY
&& rl
->rlim_max
>= RLIM_INFINITY
)
287 s
= strdup("infinity");
288 else if (rl
->rlim_cur
>= RLIM_INFINITY
)
289 (void) asprintf(&s
, "infinity:" RLIM_FMT
, rl
->rlim_max
);
290 else if (rl
->rlim_max
>= RLIM_INFINITY
)
291 (void) asprintf(&s
, RLIM_FMT
":infinity", rl
->rlim_cur
);
292 else if (rl
->rlim_cur
== rl
->rlim_max
)
293 (void) asprintf(&s
, RLIM_FMT
, rl
->rlim_cur
);
295 (void) asprintf(&s
, RLIM_FMT
":" RLIM_FMT
, rl
->rlim_cur
, rl
->rlim_max
);
304 static const char* const rlimit_table
[_RLIMIT_MAX
] = {
305 [RLIMIT_CPU
] = "LimitCPU",
306 [RLIMIT_FSIZE
] = "LimitFSIZE",
307 [RLIMIT_DATA
] = "LimitDATA",
308 [RLIMIT_STACK
] = "LimitSTACK",
309 [RLIMIT_CORE
] = "LimitCORE",
310 [RLIMIT_RSS
] = "LimitRSS",
311 [RLIMIT_NOFILE
] = "LimitNOFILE",
312 [RLIMIT_AS
] = "LimitAS",
313 [RLIMIT_NPROC
] = "LimitNPROC",
314 [RLIMIT_MEMLOCK
] = "LimitMEMLOCK",
315 [RLIMIT_LOCKS
] = "LimitLOCKS",
316 [RLIMIT_SIGPENDING
] = "LimitSIGPENDING",
317 [RLIMIT_MSGQUEUE
] = "LimitMSGQUEUE",
318 [RLIMIT_NICE
] = "LimitNICE",
319 [RLIMIT_RTPRIO
] = "LimitRTPRIO",
320 [RLIMIT_RTTIME
] = "LimitRTTIME"
323 DEFINE_STRING_TABLE_LOOKUP(rlimit
, int);