Branch data 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 : 451 : int setrlimit_closest(int resource, const struct rlimit *rlim) {
17 : : struct rlimit highest, fixed;
18 : :
19 [ - + ]: 451 : assert(rlim);
20 : :
21 [ + + ]: 451 : if (setrlimit(resource, rlim) >= 0)
22 : 443 : return 0;
23 : :
24 [ + + ]: 8 : if (errno != EPERM)
25 : 4 : 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 [ - + ]: 4 : 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 [ - + ]: 4 : if (highest.rlim_max == RLIM_INFINITY)
35 : 0 : return -EPERM;
36 : :
37 : 4 : fixed = (struct rlimit) {
38 : 4 : .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
39 : 4 : .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
40 : : };
41 : :
42 : : /* Shortcut things if we wouldn't change anything. */
43 [ - + ]: 4 : if (fixed.rlim_cur == highest.rlim_cur &&
44 [ # # ]: 0 : fixed.rlim_max == highest.rlim_max)
45 : 0 : return 0;
46 : :
47 [ - + ]: 4 : if (setrlimit(resource, &fixed) < 0)
48 : 0 : return -errno;
49 : :
50 : 4 : 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 : 124 : static int rlimit_parse_u64(const char *val, rlim_t *ret) {
80 : : uint64_t u;
81 : : int r;
82 : :
83 [ - + ]: 124 : assert(val);
84 [ - + ]: 124 : assert(ret);
85 : :
86 [ + + ]: 124 : if (streq(val, "infinity")) {
87 : 40 : *ret = RLIM_INFINITY;
88 : 40 : 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 : 84 : r = safe_atou64(val, &u);
95 [ + + ]: 84 : if (r < 0)
96 : 12 : return r;
97 [ - + ]: 72 : if (u >= (uint64_t) RLIM_INFINITY)
98 : 0 : return -ERANGE;
99 : :
100 : 72 : *ret = (rlim_t) u;
101 : 72 : 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 : 40 : static int rlimit_parse_sec(const char *val, rlim_t *ret) {
127 : : uint64_t u;
128 : : usec_t t;
129 : : int r;
130 : :
131 [ - + ]: 40 : assert(val);
132 [ - + ]: 40 : assert(ret);
133 : :
134 [ + + ]: 40 : if (streq(val, "infinity")) {
135 : 4 : *ret = RLIM_INFINITY;
136 : 4 : return 0;
137 : : }
138 : :
139 : 36 : r = parse_sec(val, &t);
140 [ - + ]: 36 : if (r < 0)
141 : 0 : return r;
142 [ - + ]: 36 : if (t == USEC_INFINITY) {
143 : 0 : *ret = RLIM_INFINITY;
144 : 0 : return 0;
145 : : }
146 : :
147 : 36 : u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
148 [ - + ]: 36 : if (u >= (uint64_t) RLIM_INFINITY)
149 : 0 : return -ERANGE;
150 : :
151 : 36 : *ret = (rlim_t) u;
152 : 36 : return 0;
153 : : }
154 : :
155 : 40 : static int rlimit_parse_usec(const char *val, rlim_t *ret) {
156 : : usec_t t;
157 : : int r;
158 : :
159 [ - + ]: 40 : assert(val);
160 [ - + ]: 40 : assert(ret);
161 : :
162 [ + + ]: 40 : if (streq(val, "infinity")) {
163 : 12 : *ret = RLIM_INFINITY;
164 : 12 : return 0;
165 : : }
166 : :
167 : 28 : r = parse_time(val, &t, 1);
168 [ - + ]: 28 : if (r < 0)
169 : 0 : return r;
170 [ - + ]: 28 : if (t == USEC_INFINITY) {
171 : 0 : *ret = RLIM_INFINITY;
172 : 0 : return 0;
173 : : }
174 : :
175 : 28 : *ret = (rlim_t) t;
176 : 28 : return 0;
177 : : }
178 : :
179 : 84 : 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 [ + + ]: 84 : if (val[0] == '+') {
192 : :
193 : : /* Prefixed with "+": Parse as positive user-friendly nice value */
194 : 16 : r = safe_atou64(val + 1, &rl);
195 [ - + ]: 16 : if (r < 0)
196 : 0 : return r;
197 : :
198 [ + + ]: 16 : if (rl >= PRIO_MAX)
199 : 4 : return -ERANGE;
200 : :
201 : 12 : rl = 20 - rl;
202 : :
203 [ + + ]: 68 : } else if (val[0] == '-') {
204 : :
205 : : /* Prefixed with "-": Parse as negative user-friendly nice value */
206 : 16 : r = safe_atou64(val + 1, &rl);
207 [ - + ]: 16 : if (r < 0)
208 : 0 : return r;
209 : :
210 [ + + ]: 16 : if (rl > (uint64_t) (-PRIO_MIN))
211 : 4 : return -ERANGE;
212 : :
213 : 12 : rl = 20 + rl;
214 : : } else {
215 : :
216 : : /* Not prefixed: parse as raw resource limit value */
217 : 52 : r = safe_atou64(val, &rl);
218 [ - + ]: 52 : if (r < 0)
219 : 0 : return r;
220 : :
221 [ + + ]: 52 : if (rl > (uint64_t) (20 - PRIO_MIN))
222 : 4 : return -ERANGE;
223 : : }
224 : :
225 : 72 : *ret = (rlim_t) rl;
226 : 72 : 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 : 288 : int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
249 [ - + ]: 288 : assert(val);
250 [ - + ]: 288 : assert(ret);
251 : :
252 [ - + ]: 288 : if (resource < 0)
253 : 0 : return -EINVAL;
254 [ - + ]: 288 : if (resource >= _RLIMIT_MAX)
255 : 0 : return -EINVAL;
256 : :
257 : 288 : return rlimit_parse_table[resource](val, ret);
258 : : }
259 : :
260 : 224 : int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
261 : 224 : _cleanup_free_ char *hard = NULL, *soft = NULL;
262 : : rlim_t hl, sl;
263 : : int r;
264 : :
265 [ - + ]: 224 : assert(val);
266 [ - + ]: 224 : assert(ret);
267 : :
268 : 224 : r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
269 [ - + ]: 224 : if (r < 0)
270 : 0 : return r;
271 [ - + ]: 224 : if (r == 0)
272 : 0 : return -EINVAL;
273 : :
274 : 224 : r = rlimit_parse_one(resource, soft, &sl);
275 [ + + ]: 224 : if (r < 0)
276 : 20 : return r;
277 : :
278 : 204 : r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
279 [ - + ]: 204 : if (r < 0)
280 : 0 : return r;
281 [ + + ]: 204 : if (!isempty(val))
282 : 8 : return -EINVAL;
283 [ + + ]: 196 : if (r == 0)
284 : 132 : hl = sl;
285 : : else {
286 : 64 : r = rlimit_parse_one(resource, hard, &hl);
287 [ + + ]: 64 : if (r < 0)
288 : 4 : return r;
289 [ + + ]: 60 : if (sl > hl)
290 : 8 : return -EILSEQ;
291 : : }
292 : :
293 : 184 : *ret = (struct rlimit) {
294 : : .rlim_cur = sl,
295 : : .rlim_max = hl,
296 : : };
297 : :
298 : 184 : return 0;
299 : : }
300 : :
301 : 60 : int rlimit_format(const struct rlimit *rl, char **ret) {
302 : 60 : char *s = NULL;
303 : :
304 [ - + ]: 60 : assert(rl);
305 [ - + ]: 60 : assert(ret);
306 : :
307 [ + + + - ]: 60 : if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
308 : 8 : s = strdup("infinity");
309 [ - + ]: 52 : else if (rl->rlim_cur >= RLIM_INFINITY)
310 : 0 : (void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
311 [ + + ]: 52 : else if (rl->rlim_max >= RLIM_INFINITY)
312 : 4 : (void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
313 [ + + ]: 48 : else if (rl->rlim_cur == rl->rlim_max)
314 : 40 : (void) asprintf(&s, RLIM_FMT, rl->rlim_cur);
315 : : else
316 : 8 : (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
317 : :
318 [ - + ]: 60 : if (!s)
319 : 0 : return -ENOMEM;
320 : :
321 : 60 : *ret = s;
322 : 60 : 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 [ + + + + ]: 640 : DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
345 : :
346 : 212 : int rlimit_from_string_harder(const char *s) {
347 : : const char *suffix;
348 : :
349 : : /* The official prefix */
350 : 212 : suffix = startswith(s, "RLIMIT_");
351 [ + + ]: 212 : if (suffix)
352 : 68 : return rlimit_from_string(suffix);
353 : :
354 : : /* Our own unit file setting prefix */
355 : 144 : suffix = startswith(s, "Limit");
356 [ + + ]: 144 : if (suffix)
357 : 68 : return rlimit_from_string(suffix);
358 : :
359 : 76 : return rlimit_from_string(s);
360 : : }
361 : :
362 : 2308 : void rlimit_free_all(struct rlimit **rl) {
363 : : int i;
364 : :
365 [ - + ]: 2308 : if (!rl)
366 : 0 : return;
367 : :
368 [ + + ]: 39236 : for (i = 0; i < _RLIMIT_MAX; i++)
369 : 36928 : rl[i] = mfree(rl[i]);
370 : : }
371 : :
372 : 439 : 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 [ - + ]: 439 : if (limit < 0)
381 : 0 : limit = read_nr_open();
382 : :
383 [ - + ]: 439 : if (limit < 3)
384 : 0 : limit = 3;
385 : :
386 : 439 : r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
387 [ - + ]: 439 : if (r < 0)
388 [ # # ]: 0 : return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
389 : :
390 : 439 : return 0;
391 : : }
392 : :
393 : 84 : 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 [ - + ]: 84 : if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
400 [ # # ]: 0 : return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
401 : :
402 [ + - ]: 84 : if (rl.rlim_cur <= FD_SETSIZE)
403 : 84 : 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 : : }
|