Coverage Report

Created: 2019-06-19 13:33

/src/systemd/src/basic/rlimit-util.c
Line
Count
Source (jump to first uncovered line)
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
0
int setrlimit_closest(int resource, const struct rlimit *rlim) {
17
0
        struct rlimit highest, fixed;
18
0
19
0
        assert(rlim);
20
0
21
0
        if (setrlimit(resource, rlim) >= 0)
22
0
                return 0;
23
0
24
0
        if (errno != EPERM)
25
0
                return -errno;
26
0
27
0
        /* So we failed to set the desired setrlimit, then let's try
28
0
         * to get as close as we can */
29
0
        if (getrlimit(resource, &highest) < 0)
30
0
                return -errno;
31
0
32
0
        /* If the hard limit is unbounded anyway, then the EPERM had other reasons, let's propagate the original EPERM
33
0
         * then */
34
0
        if (highest.rlim_max == RLIM_INFINITY)
35
0
                return -EPERM;
36
0
37
0
        fixed = (struct rlimit) {
38
0
                .rlim_cur = MIN(rlim->rlim_cur, highest.rlim_max),
39
0
                .rlim_max = MIN(rlim->rlim_max, highest.rlim_max),
40
0
        };
41
0
42
0
        /* Shortcut things if we wouldn't change anything. */
43
0
        if (fixed.rlim_cur == highest.rlim_cur &&
44
0
            fixed.rlim_max == highest.rlim_max)
45
0
                return 0;
46
0
47
0
        if (setrlimit(resource, &fixed) < 0)
48
0
                return -errno;
49
0
50
0
        return 0;
51
0
}
52
53
0
int setrlimit_closest_all(const struct rlimit *const *rlim, int *which_failed) {
54
0
        int i, r;
55
0
56
0
        assert(rlim);
57
0
58
0
        /* On failure returns the limit's index that failed in *which_failed, but only if non-NULL */
59
0
60
0
        for (i = 0; i < _RLIMIT_MAX; i++) {
61
0
                if (!rlim[i])
62
0
                        continue;
63
0
64
0
                r = setrlimit_closest(i, rlim[i]);
65
0
                if (r < 0) {
66
0
                        if (which_failed)
67
0
                                *which_failed = i;
68
0
69
0
                        return r;
70
0
                }
71
0
        }
72
0
73
0
        if (which_failed)
74
0
                *which_failed = -1;
75
0
76
0
        return 0;
77
0
}
78
79
2.40k
static int rlimit_parse_u64(const char *val, rlim_t *ret) {
80
2.40k
        uint64_t u;
81
2.40k
        int r;
82
2.40k
83
2.40k
        assert(val);
84
2.40k
        assert(ret);
85
2.40k
86
2.40k
        if (streq(val, "infinity")) {
87
388
                *ret = RLIM_INFINITY;
88
388
                return 0;
89
388
        }
90
2.01k
91
2.01k
        /* setrlimit(2) suggests rlim_t is always 64bit on Linux. */
92
2.01k
        assert_cc(sizeof(rlim_t) == sizeof(uint64_t));
93
2.01k
94
2.01k
        r = safe_atou64(val, &u);
95
2.01k
        if (r < 0)
96
551
                return r;
97
1.46k
        if (u >= (uint64_t) RLIM_INFINITY)
98
1.46k
                return -ERANGE;
99
1.06k
100
1.06k
        *ret = (rlim_t) u;
101
1.06k
        return 0;
102
1.06k
}
103
104
6.48k
static int rlimit_parse_size(const char *val, rlim_t *ret) {
105
6.48k
        uint64_t u;
106
6.48k
        int r;
107
6.48k
108
6.48k
        assert(val);
109
6.48k
        assert(ret);
110
6.48k
111
6.48k
        if (streq(val, "infinity")) {
112
388
                *ret = RLIM_INFINITY;
113
388
                return 0;
114
388
        }
115
6.09k
116
6.09k
        r = parse_size(val, 1024, &u);
117
6.09k
        if (r < 0)
118
3.34k
                return r;
119
2.74k
        if (u >= (uint64_t) RLIM_INFINITY)
120
2.74k
                return -ERANGE;
121
2.35k
122
2.35k
        *ret = (rlim_t) u;
123
2.35k
        return 0;
124
2.35k
}
125
126
7.28k
static int rlimit_parse_sec(const char *val, rlim_t *ret) {
127
7.28k
        uint64_t u;
128
7.28k
        usec_t t;
129
7.28k
        int r;
130
7.28k
131
7.28k
        assert(val);
132
7.28k
        assert(ret);
133
7.28k
134
7.28k
        if (streq(val, "infinity")) {
135
395
                *ret = RLIM_INFINITY;
136
395
                return 0;
137
395
        }
138
6.89k
139
6.89k
        r = parse_sec(val, &t);
140
6.89k
        if (r < 0)
141
4.65k
                return r;
142
2.24k
        if (t == USEC_INFINITY) {
143
388
                *ret = RLIM_INFINITY;
144
388
                return 0;
145
388
        }
146
1.85k
147
1.85k
        u = (uint64_t) DIV_ROUND_UP(t, USEC_PER_SEC);
148
1.85k
        if (u >= (uint64_t) RLIM_INFINITY)
149
1.85k
                return -ERANGE;
150
1.85k
151
1.85k
        *ret = (rlim_t) u;
152
1.85k
        return 0;
153
1.85k
}
154
155
3.01k
static int rlimit_parse_usec(const char *val, rlim_t *ret) {
156
3.01k
        usec_t t;
157
3.01k
        int r;
158
3.01k
159
3.01k
        assert(val);
160
3.01k
        assert(ret);
161
3.01k
162
3.01k
        if (streq(val, "infinity")) {
163
403
                *ret = RLIM_INFINITY;
164
403
                return 0;
165
403
        }
166
2.61k
167
2.61k
        r = parse_time(val, &t, 1);
168
2.61k
        if (r < 0)
169
1.12k
                return r;
170
1.48k
        if (t == USEC_INFINITY) {
171
466
                *ret = RLIM_INFINITY;
172
466
                return 0;
173
466
        }
174
1.01k
175
1.01k
        *ret = (rlim_t) t;
176
1.01k
        return 0;
177
1.01k
}
178
179
6.82k
static int rlimit_parse_nice(const char *val, rlim_t *ret) {
180
6.82k
        uint64_t rl;
181
6.82k
        int r;
182
6.82k
183
6.82k
        /* So, Linux is weird. The range for RLIMIT_NICE is 40..1, mapping to the nice levels -20..19. However, the
184
6.82k
         * RLIMIT_NICE limit defaults to 0 by the kernel, i.e. a value that maps to nice level 20, which of course is
185
6.82k
         * bogus and does not exist. In order to permit parsing the RLIMIT_NICE of 0 here we hence implement a slight
186
6.82k
         * asymmetry: when parsing as positive nice level we permit 0..19. When parsing as negative nice level, we
187
6.82k
         * permit -20..0. But when parsing as raw resource limit value then we also allow the special value 0.
188
6.82k
         *
189
6.82k
         * Yeah, Linux is quality engineering sometimes... */
190
6.82k
191
6.82k
        if (val[0] == '+') {
192
1.97k
193
1.97k
                /* Prefixed with "+": Parse as positive user-friendly nice value */
194
1.97k
                r = safe_atou64(val + 1, &rl);
195
1.97k
                if (r < 0)
196
715
                        return r;
197
1.26k
198
1.26k
                if (rl >= PRIO_MAX)
199
1.26k
                        return -ERANGE;
200
571
201
571
                rl = 20 - rl;
202
571
203
4.84k
        } else if (val[0] == '-') {
204
1.78k
205
1.78k
                /* Prefixed with "-": Parse as negative user-friendly nice value */
206
1.78k
                r = safe_atou64(val + 1, &rl);
207
1.78k
                if (r < 0)
208
637
                        return r;
209
1.14k
210
1.14k
                if (rl > (uint64_t) (-PRIO_MIN))
211
578
                        return -ERANGE;
212
568
213
568
                rl = 20 + rl;
214
3.06k
        } else {
215
3.06k
216
3.06k
                /* Not prefixed: parse as raw resource limit value */
217
3.06k
                r = safe_atou64(val, &rl);
218
3.06k
                if (r < 0)
219
755
                        return r;
220
2.30k
221
2.30k
                if (rl > (uint64_t) (20 - PRIO_MIN))
222
579
                        return -ERANGE;
223
2.86k
        }
224
2.86k
225
2.86k
        *ret = (rlim_t) rl;
226
2.86k
        return 0;
227
2.86k
}
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
26.0k
int rlimit_parse_one(int resource, const char *val, rlim_t *ret) {
249
26.0k
        assert(val);
250
26.0k
        assert(ret);
251
26.0k
252
26.0k
        if (resource < 0)
253
0
                return -EINVAL;
254
26.0k
        if (resource >= _RLIMIT_MAX)
255
26.0k
                return -EINVAL;
256
26.0k
257
26.0k
        return rlimit_parse_table[resource](val, ret);
258
26.0k
}
259
260
21.4k
int rlimit_parse(int resource, const char *val, struct rlimit *ret) {
261
21.4k
        _cleanup_free_ char *hard = NULL, *soft = NULL;
262
21.4k
        rlim_t hl, sl;
263
21.4k
        int r;
264
21.4k
265
21.4k
        assert(val);
266
21.4k
        assert(ret);
267
21.4k
268
21.4k
        r = extract_first_word(&val, &soft, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
269
21.4k
        if (r < 0)
270
397
                return r;
271
21.0k
        if (r == 0)
272
0
                return -EINVAL;
273
21.0k
274
21.0k
        r = rlimit_parse_one(resource, soft, &sl);
275
21.0k
        if (r < 0)
276
12.0k
                return r;
277
9.05k
278
9.05k
        r = extract_first_word(&val, &hard, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
279
9.05k
        if (r < 0)
280
388
                return r;
281
8.66k
        if (!isempty(val))
282
716
                return -EINVAL;
283
7.95k
        if (r == 0)
284
3.03k
                hl = sl;
285
4.91k
        else {
286
4.91k
                r = rlimit_parse_one(resource, hard, &hl);
287
4.91k
                if (r < 0)
288
2.38k
                        return r;
289
2.52k
                if (sl > hl)
290
1.20k
                        return -EILSEQ;
291
4.35k
        }
292
4.35k
293
4.35k
        *ret = (struct rlimit) {
294
4.35k
                .rlim_cur = sl,
295
4.35k
                .rlim_max = hl,
296
4.35k
        };
297
4.35k
298
4.35k
        return 0;
299
4.35k
}
300
301
0
int rlimit_format(const struct rlimit *rl, char **ret) {
302
0
        char *s = NULL;
303
0
304
0
        assert(rl);
305
0
        assert(ret);
306
0
307
0
        if (rl->rlim_cur >= RLIM_INFINITY && rl->rlim_max >= RLIM_INFINITY)
308
0
                s = strdup("infinity");
309
0
        else if (rl->rlim_cur >= RLIM_INFINITY)
310
0
                (void) asprintf(&s, "infinity:" RLIM_FMT, rl->rlim_max);
311
0
        else if (rl->rlim_max >= RLIM_INFINITY)
312
0
                (void) asprintf(&s, RLIM_FMT ":infinity", rl->rlim_cur);
313
0
        else if (rl->rlim_cur == rl->rlim_max)
314
0
                (void) asprintf(&s, RLIM_FMT, rl->rlim_cur);
315
0
        else
316
0
                (void) asprintf(&s, RLIM_FMT ":" RLIM_FMT, rl->rlim_cur, rl->rlim_max);
317
0
318
0
        if (!s)
319
0
                return -ENOMEM;
320
0
321
0
        *ret = s;
322
0
        return 0;
323
0
}
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
DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
345
346
0
int rlimit_from_string_harder(const char *s) {
347
0
        const char *suffix;
348
0
349
0
        /* The official prefix */
350
0
        suffix = startswith(s, "RLIMIT_");
351
0
        if (suffix)
352
0
                return rlimit_from_string(suffix);
353
0
354
0
        /* Our own unit file setting prefix */
355
0
        suffix = startswith(s, "Limit");
356
0
        if (suffix)
357
0
                return rlimit_from_string(suffix);
358
0
359
0
        return rlimit_from_string(s);
360
0
}
361
362
48.3k
void rlimit_free_all(struct rlimit **rl) {
363
48.3k
        int i;
364
48.3k
365
48.3k
        if (!rl)
366
0
                return;
367
48.3k
368
821k
        for (i = 0; i < _RLIMIT_MAX; i++)
369
773k
                rl[i] = mfree(rl[i]);
370
48.3k
}
371
372
0
int rlimit_nofile_bump(int limit) {
373
0
        int r;
374
0
375
0
        /* Bumps the (soft) RLIMIT_NOFILE resource limit as close as possible to the specified limit. If a negative
376
0
         * limit is specified, bumps it to the maximum the kernel and the hard resource limit allows. This call should
377
0
         * be used by all our programs that might need a lot of fds, and that know how to deal with high fd numbers
378
0
         * (i.e. do not use select() — which chokes on fds >= 1024) */
379
0
380
0
        if (limit < 0)
381
0
                limit = read_nr_open();
382
0
383
0
        if (limit < 3)
384
0
                limit = 3;
385
0
386
0
        r = setrlimit_closest(RLIMIT_NOFILE, &RLIMIT_MAKE_CONST(limit));
387
0
        if (r < 0)
388
0
                return log_debug_errno(r, "Failed to set RLIMIT_NOFILE: %m");
389
0
390
0
        return 0;
391
0
}
392
393
0
int rlimit_nofile_safe(void) {
394
0
        struct rlimit rl;
395
0
396
0
        /* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
397
0
         * select() */
398
0
399
0
        if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
400
0
                return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
401
0
402
0
        if (rl.rlim_cur <= FD_SETSIZE)
403
0
                return 0;
404
0
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
0
409
0
        return 1;
410
0
}