]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/rlimit-util.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / basic / rlimit-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
78f22b97
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6
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.
11
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.
16
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/>.
19***/
20
11c3a366
TA
21#include <errno.h>
22#include <sys/resource.h>
23
d0a7c5f6
LP
24#include "alloc-util.h"
25#include "extract-word.h"
f97b34a6 26#include "format-util.h"
93cc7779 27#include "macro.h"
78f22b97
LP
28#include "missing.h"
29#include "rlimit-util.h"
8b43440b 30#include "string-table.h"
d0a7c5f6 31#include "time-util.h"
78f22b97
LP
32
33int setrlimit_closest(int resource, const struct rlimit *rlim) {
34 struct rlimit highest, fixed;
35
36 assert(rlim);
37
38 if (setrlimit(resource, rlim) >= 0)
39 return 0;
40
41 if (errno != EPERM)
42 return -errno;
43
44 /* So we failed to set the desired setrlimit, then let's try
45 * to get as close as we can */
c4ad3f43
LP
46 if (getrlimit(resource, &highest) < 0)
47 return -errno;
78f22b97
LP
48
49 fixed.rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max);
50 fixed.rlim_max = MIN(rlim->rlim_max, highest.rlim_max);
51
52 if (setrlimit(resource, &fixed) < 0)
53 return -errno;
54
55 return 0;
56}
57
d0a7c5f6
LP
58static int rlimit_parse_u64(const char *val, rlim_t *ret) {
59 uint64_t u;
60 int r;
61
62 assert(val);
63 assert(ret);
64
65 if (streq(val, "infinity")) {
66 *ret = RLIM_INFINITY;
67 return 0;
68 }
69
70 /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
71 assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
72
73 r = safe_atou64(val, &u);
74 if (r < 0)
75 return r;
76 if (u >= (uint64_t) RLIM_INFINITY)
77 return -ERANGE;
78
79 *ret = (rlim_t) u;
80 return 0;
81}
82
83static int rlimit_parse_size(const char *val, rlim_t *ret) {
84 uint64_t u;
85 int r;
86
87 assert(val);
88 assert(ret);
89
90 if (streq(val, "infinity")) {
91 *ret = RLIM_INFINITY;
92 return 0;
93 }
94
95 r = parse_size(val, 1024, &u);
96 if (r < 0)
97 return r;
98 if (u >= (uint64_t) RLIM_INFINITY)
99 return -ERANGE;
100
101 *ret = (rlim_t) u;
102 return 0;
103}
104
105static int rlimit_parse_sec(const char *val, rlim_t *ret) {
106 uint64_t u;
107 usec_t t;
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_sec(val, &t);
119 if (r < 0)
120 return r;
121 if (t == USEC_INFINITY) {
122 *ret = RLIM_INFINITY;
123 return 0;
124 }
125
126 u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
127 if (u >= (uint64_t) RLIM_INFINITY)
128 return -ERANGE;
129
130 *ret = (rlim_t) u;
131 return 0;
132}
133
134static int rlimit_parse_usec(const char *val, rlim_t *ret) {
135 usec_t t;
136 int r;
137
138 assert(val);
139 assert(ret);
140
141 if (streq(val, "infinity")) {
142 *ret = RLIM_INFINITY;
143 return 0;
144 }
145
146 r = parse_time(val, &t, 1);
147 if (r < 0)
148 return r;
149 if (t == USEC_INFINITY) {
150 *ret = RLIM_INFINITY;
151 return 0;
152 }
153
154 *ret = (rlim_t) t;
155 return 0;
156}
157
29857001
LP
158static int rlimit_parse_nice(const char *val, rlim_t *ret) {
159 uint64_t rl;
160 int r;
161
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.
167 *
168 * Yeah, Linux is quality engineering sometimes... */
169
170 if (val[0] == '+') {
171
172 /* Prefixed with "+": Parse as positive user-friendly nice value */
173 r = safe_atou64(val + 1, &rl);
174 if (r < 0)
175 return r;
176
177 if (rl >= PRIO_MAX)
178 return -ERANGE;
179
180 rl = 20 - rl;
181
182 } else if (val[0] == '-') {
183
184 /* Prefixed with "-": Parse as negative user-friendly nice value */
185 r = safe_atou64(val + 1, &rl);
186 if (r < 0)
187 return r;
188
189 if (rl > (uint64_t) (-PRIO_MIN))
190 return -ERANGE;
191
192 rl = 20 + rl;
193 } else {
194
195 /* Not prefixed: parse as raw resource limit value */
196 r = safe_atou64(val, &rl);
197 if (r < 0)
198 return r;
199
200 if (rl > (uint64_t) (20 - PRIO_MIN))
201 return -ERANGE;
202 }
203
204 *ret = (rlim_t) rl;
205 return 0;
206}
207
d0a7c5f6
LP
208static 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,
29857001 222 [RLIMIT_NICE] = rlimit_parse_nice,
d0a7c5f6
LP
223 [RLIMIT_RTPRIO] = rlimit_parse_u64,
224 [RLIMIT_RTTIME] = rlimit_parse_usec,
225};
226
227int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
228 assert(val);
229 assert(ret);
230
231 if (resource < 0)
232 return -EINVAL;
233 if (resource >= _RLIMIT_MAX)
234 return -EINVAL;
235
236 return rlimit_parse_table[resource](val, ret);
237}
238
239int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
240 _cleanup_free_ char *hard = NULL, *soft = NULL;
241 rlim_t hl, sl;
242 int r;
243
244 assert(val);
245 assert(ret);
246
247 r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
248 if (r < 0)
249 return r;
250 if (r == 0)
251 return -EINVAL;
252
253 r = rlimit_parse_one(resource, soft, &sl);
254 if (r < 0)
255 return r;
256
257 r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
258 if (r < 0)
259 return r;
260 if (!isempty(val))
261 return -EINVAL;
262 if (r == 0)
263 hl = sl;
264 else {
265 r = rlimit_parse_one(resource, hard, &hl);
266 if (r < 0)
267 return r;
268 if (sl > hl)
269 return -EILSEQ;
270 }
271
272 *ret = (struct rlimit) {
273 .rlim_cur = sl,
274 .rlim_max = hl,
275 };
276
277 return 0;
278}
279
99d4f5e5
LP
280int rlimit_format(const struct rlimit *rl, char **ret) {
281 char *s = NULL;
282
283 assert(rl);
284 assert(ret);
285
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);
294 else
295 (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
296
297 if (!s)
298 return -ENOMEM;
299
300 *ret = s;
301 return 0;
302}
303
78f22b97
LP
304static 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"
321};
322
323DEFINE_STRING_TABLE_LOOKUP(rlimit, int);