1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4
5 #include "alloc-util.h"
6 #include "errno-util.h"
7 #include "extract-word.h"
8 #include "fd-util.h"
9 #include "format-util.h"
10 #include "macro.h"
11 #include "missing_resource.h"
12 #include "rlimit-util.h"
13 #include "string-table.h"
14 #include "time-util.h"
15
setrlimit_closest(int resource,const struct rlimit * rlim)16 int setrlimit_closest(int resource, const struct rlimit *rlim) {
17 struct rlimit highest, fixed;
18
19 assert(rlim);
20
21 if (setrlimit(resource, rlim) >= 0)
22 return 0;
23
24 if (errno != EPERM)
25 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 if (getrlimit(resource, &highest) < 0)
30 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 if (highest.rlim_max == RLIM_INFINITY)
35 return -EPERM;
36
37 fixed = (struct rlimit) {
38 .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
39 .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
40 };
41
42 /* Shortcut things if we wouldn't change anything. */
43 if (fixed.rlim_cur == highest.rlim_cur &&
44 fixed.rlim_max == highest.rlim_max)
45 return 0;
46
47 log_debug("Failed at setting rlimit " RLIM_FMT " for resource RLIMIT_%s. Will attempt setting value " RLIM_FMT " instead.", rlim->rlim_max, rlimit_to_string(resource), fixed.rlim_max);
48
49 return RET_NERRNO(setrlimit(resource, &fixed));
50 }
51
setrlimit_closest_all(const struct rlimit * const * rlim,int * which_failed)52 int setrlimit_closest_all(const struct rlimit *const *rlim, int *which_failed) {
53 int r;
54
55 assert(rlim);
56
57 /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
58
59 for (int i = 0; i < _RLIMIT_MAX; i++) {
60 if (!rlim[i])
61 continue;
62
63 r = setrlimit_closest(i, rlim[i]);
64 if (r < 0) {
65 if (which_failed)
66 *which_failed = i;
67
68 return r;
69 }
70 }
71
72 if (which_failed)
73 *which_failed = -1;
74
75 return 0;
76 }
77
rlimit_parse_u64(const char * val,rlim_t * ret)78 static int rlimit_parse_u64(const char *val, rlim_t *ret) {
79 uint64_t u;
80 int r;
81
82 assert(val);
83 assert(ret);
84
85 if (streq(val, "infinity")) {
86 *ret = RLIM_INFINITY;
87 return 0;
88 }
89
90 /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
91 assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
92
93 r = safe_atou64(val, &u);
94 if (r < 0)
95 return r;
96 if (u >= (uint64_t) RLIM_INFINITY)
97 return -ERANGE;
98
99 *ret = (rlim_t) u;
100 return 0;
101 }
102
rlimit_parse_size(const char * val,rlim_t * ret)103 static int rlimit_parse_size(const char *val, rlim_t *ret) {
104 uint64_t u;
105 int r;
106
107 assert(val);
108 assert(ret);
109
110 if (streq(val, "infinity")) {
111 *ret = RLIM_INFINITY;
112 return 0;
113 }
114
115 r = parse_size(val, 1024, &u);
116 if (r < 0)
117 return r;
118 if (u >= (uint64_t) RLIM_INFINITY)
119 return -ERANGE;
120
121 *ret = (rlim_t) u;
122 return 0;
123 }
124
rlimit_parse_sec(const char * val,rlim_t * ret)125 static int rlimit_parse_sec(const char *val, rlim_t *ret) {
126 uint64_t u;
127 usec_t t;
128 int r;
129
130 assert(val);
131 assert(ret);
132
133 if (streq(val, "infinity")) {
134 *ret = RLIM_INFINITY;
135 return 0;
136 }
137
138 r = parse_sec(val, &t);
139 if (r < 0)
140 return r;
141 if (t == USEC_INFINITY) {
142 *ret = RLIM_INFINITY;
143 return 0;
144 }
145
146 u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
147 if (u >= (uint64_t) RLIM_INFINITY)
148 return -ERANGE;
149
150 *ret = (rlim_t) u;
151 return 0;
152 }
153
rlimit_parse_usec(const char * val,rlim_t * ret)154 static int rlimit_parse_usec(const char *val, rlim_t *ret) {
155 usec_t t;
156 int r;
157
158 assert(val);
159 assert(ret);
160
161 if (streq(val, "infinity")) {
162 *ret = RLIM_INFINITY;
163 return 0;
164 }
165
166 r = parse_time(val, &t, 1);
167 if (r < 0)
168 return r;
169 if (t == USEC_INFINITY) {
170 *ret = RLIM_INFINITY;
171 return 0;
172 }
173
174 *ret = (rlim_t) t;
175 return 0;
176 }
177
rlimit_parse_nice(const char * val,rlim_t * ret)178 static int rlimit_parse_nice(const char *val, rlim_t *ret) {
179 uint64_t rl;
180 int r;
181
182 /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
183 * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
184 * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
185 * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
186 * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
187 *
188 * Yeah, Linux is quality engineering sometimes... */
189
190 if (val[0] == '+') {
191
192 /* Prefixed with "+": Parse as positive user-friendly nice value */
193 r = safe_atou64(val + 1, &rl);
194 if (r < 0)
195 return r;
196
197 if (rl >= PRIO_MAX)
198 return -ERANGE;
199
200 rl = 20 - rl;
201
202 } else if (val[0] == '-') {
203
204 /* Prefixed with "-": Parse as negative user-friendly nice value */
205 r = safe_atou64(val + 1, &rl);
206 if (r < 0)
207 return r;
208
209 if (rl > (uint64_t) (-PRIO_MIN))
210 return -ERANGE;
211
212 rl = 20 + rl;
213 } else {
214
215 /* Not prefixed: parse as raw resource limit value */
216 r = safe_atou64(val, &rl);
217 if (r < 0)
218 return r;
219
220 if (rl > (uint64_t) (20 - PRIO_MIN))
221 return -ERANGE;
222 }
223
224 *ret = (rlim_t) rl;
225 return 0;
226 }
227
228 static int (*const rlimit_parse_table[_RLIMIT_MAX])(const char *val, rlim_t *ret) = {
229 [RLIMIT_CPU] = rlimit_parse_sec,
230 [RLIMIT_FSIZE] = rlimit_parse_size,
231 [RLIMIT_DATA] = rlimit_parse_size,
232 [RLIMIT_STACK] = rlimit_parse_size,
233 [RLIMIT_CORE] = rlimit_parse_size,
234 [RLIMIT_RSS] = rlimit_parse_size,
235 [RLIMIT_NOFILE] = rlimit_parse_u64,
236 [RLIMIT_AS] = rlimit_parse_size,
237 [RLIMIT_NPROC] = rlimit_parse_u64,
238 [RLIMIT_MEMLOCK] = rlimit_parse_size,
239 [RLIMIT_LOCKS] = rlimit_parse_u64,
240 [RLIMIT_SIGPENDING] = rlimit_parse_u64,
241 [RLIMIT_MSGQUEUE] = rlimit_parse_size,
242 [RLIMIT_NICE] = rlimit_parse_nice,
243 [RLIMIT_RTPRIO] = rlimit_parse_u64,
244 [RLIMIT_RTTIME] = rlimit_parse_usec,
245 };
246
rlimit_parse_one(int resource,const char * val,rlim_t * ret)247 int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
248 assert(val);
249 assert(ret);
250
251 if (resource < 0)
252 return -EINVAL;
253 if (resource >= _RLIMIT_MAX)
254 return -EINVAL;
255
256 return rlimit_parse_table[resource](val, ret);
257 }
258
rlimit_parse(int resource,const char * val,struct rlimit * ret)259 int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
260 _cleanup_free_ char *hard = NULL, *soft = NULL;
261 rlim_t hl, sl;
262 int r;
263
264 assert(val);
265 assert(ret);
266
267 r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
268 if (r < 0)
269 return r;
270 if (r == 0)
271 return -EINVAL;
272
273 r = rlimit_parse_one(resource, soft, &sl);
274 if (r < 0)
275 return r;
276
277 r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
278 if (r < 0)
279 return r;
280 if (!isempty(val))
281 return -EINVAL;
282 if (r == 0)
283 hl = sl;
284 else {
285 r = rlimit_parse_one(resource, hard, &hl);
286 if (r < 0)
287 return r;
288 if (sl > hl)
289 return -EILSEQ;
290 }
291
292 *ret = (struct rlimit) {
293 .rlim_cur = sl,
294 .rlim_max = hl,
295 };
296
297 return 0;
298 }
299
rlimit_format(const struct rlimit * rl,char ** ret)300 int rlimit_format(const struct rlimit *rl, char **ret) {
301 _cleanup_free_ char *s = NULL;
302 int r;
303
304 assert(rl);
305 assert(ret);
306
307 if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
308 r = free_and_strdup(&s, "infinity");
309 else if (rl->rlim_cur >= RLIM_INFINITY)
310 r = asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
311 else if (rl->rlim_max >= RLIM_INFINITY)
312 r = asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
313 else if (rl->rlim_cur == rl->rlim_max)
314 r = asprintf(&s, RLIM_FMT, rl->rlim_cur);
315 else
316 r = asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
317 if (r < 0)
318 return -ENOMEM;
319
320 *ret = TAKE_PTR(s);
321 return 0;
322 }
323
324 static const char* const rlimit_table[_RLIMIT_MAX] = {
325 [RLIMIT_AS] = "AS",
326 [RLIMIT_CORE] = "CORE",
327 [RLIMIT_CPU] = "CPU",
328 [RLIMIT_DATA] = "DATA",
329 [RLIMIT_FSIZE] = "FSIZE",
330 [RLIMIT_LOCKS] = "LOCKS",
331 [RLIMIT_MEMLOCK] = "MEMLOCK",
332 [RLIMIT_MSGQUEUE] = "MSGQUEUE",
333 [RLIMIT_NICE] = "NICE",
334 [RLIMIT_NOFILE] = "NOFILE",
335 [RLIMIT_NPROC] = "NPROC",
336 [RLIMIT_RSS] = "RSS",
337 [RLIMIT_RTPRIO] = "RTPRIO",
338 [RLIMIT_RTTIME] = "RTTIME",
339 [RLIMIT_SIGPENDING] = "SIGPENDING",
340 [RLIMIT_STACK] = "STACK",
341 };
342
343 DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
344
rlimit_from_string_harder(const char * s)345 int rlimit_from_string_harder(const char *s) {
346 const char *suffix;
347
348 /* The official prefix */
349 suffix = startswith(s, "RLIMIT_");
350 if (suffix)
351 return rlimit_from_string(suffix);
352
353 /* Our own unit file setting prefix */
354 suffix = startswith(s, "Limit");
355 if (suffix)
356 return rlimit_from_string(suffix);
357
358 return rlimit_from_string(s);
359 }
360
rlimit_free_all(struct rlimit ** rl)361 void rlimit_free_all(struct rlimit **rl) {
362 int i;
363
364 if (!rl)
365 return;
366
367 for (i = 0; i < _RLIMIT_MAX; i++)
368 rl[i] = mfree(rl[i]);
369 }
370
rlimit_nofile_bump(int limit)371 int rlimit_nofile_bump(int limit) {
372 int r;
373
374 /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
375 * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
376 * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
377 * (i.e. do not use select() — which chokes on fds >= 1024) */
378
379 if (limit < 0)
380 limit = read_nr_open();
381
382 if (limit < 3)
383 limit = 3;
384
385 r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
386 if (r < 0)
387 return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
388
389 return 0;
390 }
391
rlimit_nofile_safe(void)392 int rlimit_nofile_safe(void) {
393 struct rlimit rl;
394
395 /* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
396 * select() */
397
398 if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
399 return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
400
401 if (rl.rlim_cur <= FD_SETSIZE)
402 return 0;
403
404 rl.rlim_cur = FD_SETSIZE;
405 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
406 return log_debug_errno(errno, "Failed to lower RLIMIT_NOFILE's soft limit to " RLIM_FMT ": %m", rl.rlim_cur);
407
408 return 1;
409 }
410