Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <errno.h>
4 : #include <sys/resource.h>
5 :
6 : #include "alloc-util.h"
7 : #include "extract-word.h"
8 : #include "fd-util.h"
9 : #include "format-util.h"
10 : #include "macro.h"
11 : #include "missing.h"
12 : #include "rlimit-util.h"
13 : #include "string-table.h"
14 : #include "time-util.h"
15 :
16 113 : int setrlimit_closest(int resource, const struct rlimit *rlim) {
17 : struct rlimit highest, fixed;
18 :
19 113 : assert(rlim);
20 :
21 113 : if (setrlimit(resource, rlim) >= 0)
22 111 : return 0;
23 :
24 2 : if (errno != EPERM)
25 1 : return -errno;
26 :
27 : /* So we failed to set the desired setrlimit, then let's try
28 : * to get as close as we can */
29 1 : if (getrlimit(resource, &highest) < 0)
30 0 : return -errno;
31 :
32 : /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
33 : * then */
34 1 : if (highest.rlim_max == RLIM_INFINITY)
35 0 : return -EPERM;
36 :
37 1 : fixed = (struct rlimit) {
38 1 : .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
39 1 : .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
40 : };
41 :
42 : /* Shortcut things if we wouldn't change anything. */
43 1 : if (fixed.rlim_cur == highest.rlim_cur &&
44 0 : fixed.rlim_max == highest.rlim_max)
45 0 : return 0;
46 :
47 1 : if (setrlimit(resource, &fixed) < 0)
48 0 : return -errno;
49 :
50 1 : return 0;
51 : }
52 :
53 0 : int setrlimit_closest_all(const struct rlimit *const *rlim, int *which_failed) {
54 : int i, r;
55 :
56 0 : assert(rlim);
57 :
58 : /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
59 :
60 0 : for (i = 0; i < _RLIMIT_MAX; i++) {
61 0 : if (!rlim[i])
62 0 : continue;
63 :
64 0 : r = setrlimit_closest(i, rlim[i]);
65 0 : if (r < 0) {
66 0 : if (which_failed)
67 0 : *which_failed = i;
68 :
69 0 : return r;
70 : }
71 : }
72 :
73 0 : if (which_failed)
74 0 : *which_failed = -1;
75 :
76 0 : return 0;
77 : }
78 :
79 31 : static int rlimit_parse_u64(const char *val, rlim_t *ret) {
80 : uint64_t u;
81 : int r;
82 :
83 31 : assert(val);
84 31 : assert(ret);
85 :
86 31 : if (streq(val, "infinity")) {
87 10 : *ret = RLIM_INFINITY;
88 10 : return 0;
89 : }
90 :
91 : /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
92 : assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
93 :
94 21 : r = safe_atou64(val, &u);
95 21 : if (r < 0)
96 3 : return r;
97 18 : if (u >= (uint64_t) RLIM_INFINITY)
98 0 : return -ERANGE;
99 :
100 18 : *ret = (rlim_t) u;
101 18 : return 0;
102 : }
103 :
104 0 : static int rlimit_parse_size(const char *val, rlim_t *ret) {
105 : uint64_t u;
106 : int r;
107 :
108 0 : assert(val);
109 0 : assert(ret);
110 :
111 0 : if (streq(val, "infinity")) {
112 0 : *ret = RLIM_INFINITY;
113 0 : return 0;
114 : }
115 :
116 0 : r = parse_size(val, 1024, &u);
117 0 : if (r < 0)
118 0 : return r;
119 0 : if (u >= (uint64_t) RLIM_INFINITY)
120 0 : return -ERANGE;
121 :
122 0 : *ret = (rlim_t) u;
123 0 : return 0;
124 : }
125 :
126 10 : static int rlimit_parse_sec(const char *val, rlim_t *ret) {
127 : uint64_t u;
128 : usec_t t;
129 : int r;
130 :
131 10 : assert(val);
132 10 : assert(ret);
133 :
134 10 : if (streq(val, "infinity")) {
135 1 : *ret = RLIM_INFINITY;
136 1 : return 0;
137 : }
138 :
139 9 : r = parse_sec(val, &t);
140 9 : if (r < 0)
141 0 : return r;
142 9 : if (t == USEC_INFINITY) {
143 0 : *ret = RLIM_INFINITY;
144 0 : return 0;
145 : }
146 :
147 9 : u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
148 9 : if (u >= (uint64_t) RLIM_INFINITY)
149 0 : return -ERANGE;
150 :
151 9 : *ret = (rlim_t) u;
152 9 : return 0;
153 : }
154 :
155 10 : static int rlimit_parse_usec(const char *val, rlim_t *ret) {
156 : usec_t t;
157 : int r;
158 :
159 10 : assert(val);
160 10 : assert(ret);
161 :
162 10 : if (streq(val, "infinity")) {
163 3 : *ret = RLIM_INFINITY;
164 3 : return 0;
165 : }
166 :
167 7 : r = parse_time(val, &t, 1);
168 7 : if (r < 0)
169 0 : return r;
170 7 : if (t == USEC_INFINITY) {
171 0 : *ret = RLIM_INFINITY;
172 0 : return 0;
173 : }
174 :
175 7 : *ret = (rlim_t) t;
176 7 : return 0;
177 : }
178 :
179 21 : static int rlimit_parse_nice(const char *val, rlim_t *ret) {
180 : uint64_t rl;
181 : int r;
182 :
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.
188 : *
189 : * Yeah, Linux is quality engineering sometimes... */
190 :
191 21 : if (val[0] == '+') {
192 :
193 : /* Prefixed with "+": Parse as positive user-friendly nice value */
194 4 : r = safe_atou64(val + 1, &rl);
195 4 : if (r < 0)
196 0 : return r;
197 :
198 4 : if (rl >= PRIO_MAX)
199 1 : return -ERANGE;
200 :
201 3 : rl = 20 - rl;
202 :
203 17 : } else if (val[0] == '-') {
204 :
205 : /* Prefixed with "-": Parse as negative user-friendly nice value */
206 4 : r = safe_atou64(val + 1, &rl);
207 4 : if (r < 0)
208 0 : return r;
209 :
210 4 : if (rl > (uint64_t) (-PRIO_MIN))
211 1 : return -ERANGE;
212 :
213 3 : rl = 20 + rl;
214 : } else {
215 :
216 : /* Not prefixed: parse as raw resource limit value */
217 13 : r = safe_atou64(val, &rl);
218 13 : if (r < 0)
219 0 : return r;
220 :
221 13 : if (rl > (uint64_t) (20 - PRIO_MIN))
222 1 : return -ERANGE;
223 : }
224 :
225 18 : *ret = (rlim_t) rl;
226 18 : return 0;
227 : }
228 :
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,
246 : };
247 :
248 72 : int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
249 72 : assert(val);
250 72 : assert(ret);
251 :
252 72 : if (resource < 0)
253 0 : return -EINVAL;
254 72 : if (resource >= _RLIMIT_MAX)
255 0 : return -EINVAL;
256 :
257 72 : return rlimit_parse_table[resource](val, ret);
258 : }
259 :
260 56 : int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
261 56 : _cleanup_free_ char *hard = NULL, *soft = NULL;
262 : rlim_t hl, sl;
263 : int r;
264 :
265 56 : assert(val);
266 56 : assert(ret);
267 :
268 56 : r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
269 56 : if (r < 0)
270 0 : return r;
271 56 : if (r == 0)
272 0 : return -EINVAL;
273 :
274 56 : r = rlimit_parse_one(resource, soft, &sl);
275 56 : if (r < 0)
276 5 : return r;
277 :
278 51 : r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
279 51 : if (r < 0)
280 0 : return r;
281 51 : if (!isempty(val))
282 2 : return -EINVAL;
283 49 : if (r == 0)
284 33 : hl = sl;
285 : else {
286 16 : r = rlimit_parse_one(resource, hard, &hl);
287 16 : if (r < 0)
288 1 : return r;
289 15 : if (sl > hl)
290 2 : return -EILSEQ;
291 : }
292 :
293 46 : *ret = (struct rlimit) {
294 : .rlim_cur = sl,
295 : .rlim_max = hl,
296 : };
297 :
298 46 : return 0;
299 : }
300 :
301 15 : int rlimit_format(const struct rlimit *rl, char **ret) {
302 15 : char *s = NULL;
303 :
304 15 : assert(rl);
305 15 : assert(ret);
306 :
307 15 : if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
308 2 : s = strdup("infinity");
309 13 : else if (rl->rlim_cur >= RLIM_INFINITY)
310 0 : (void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
311 13 : else if (rl->rlim_max >= RLIM_INFINITY)
312 1 : (void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
313 12 : else if (rl->rlim_cur == rl->rlim_max)
314 10 : (void) asprintf(&s, RLIM_FMT, rl->rlim_cur);
315 : else
316 2 : (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
317 :
318 15 : if (!s)
319 0 : return -ENOMEM;
320 :
321 15 : *ret = s;
322 15 : return 0;
323 : }
324 :
325 : static const char* const rlimit_table[_RLIMIT_MAX] = {
326 : [RLIMIT_AS] = "AS",
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",
342 : };
343 :
344 160 : DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
345 :
346 53 : int rlimit_from_string_harder(const char *s) {
347 : const char *suffix;
348 :
349 : /* The official prefix */
350 53 : suffix = startswith(s, "RLIMIT_");
351 53 : if (suffix)
352 17 : return rlimit_from_string(suffix);
353 :
354 : /* Our own unit file setting prefix */
355 36 : suffix = startswith(s, "Limit");
356 36 : if (suffix)
357 17 : return rlimit_from_string(suffix);
358 :
359 19 : return rlimit_from_string(s);
360 : }
361 :
362 577 : void rlimit_free_all(struct rlimit **rl) {
363 : int i;
364 :
365 577 : if (!rl)
366 0 : return;
367 :
368 9809 : for (i = 0; i < _RLIMIT_MAX; i++)
369 9232 : rl[i] = mfree(rl[i]);
370 : }
371 :
372 110 : int rlimit_nofile_bump(int limit) {
373 : int r;
374 :
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) */
379 :
380 110 : if (limit < 0)
381 0 : limit = read_nr_open();
382 :
383 110 : if (limit < 3)
384 0 : limit = 3;
385 :
386 110 : r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
387 110 : if (r < 0)
388 0 : return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
389 :
390 110 : return 0;
391 : }
392 :
393 21 : int rlimit_nofile_safe(void) {
394 : struct rlimit rl;
395 :
396 : /* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
397 : * select() */
398 :
399 21 : if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
400 0 : return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
401 :
402 21 : if (rl.rlim_cur <= FD_SETSIZE)
403 21 : return 0;
404 :
405 0 : rl.rlim_cur = FD_SETSIZE;
406 0 : if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
407 0 : return log_debug_errno(errno, "Failed to lower RLIMIT_NOFILE's soft limit to " RLIM_FMT ": %m", rl.rlim_cur);
408 :
409 0 : return 1;
410 : }
|