• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 23966986054

03 Apr 2026 09:22PM UTC coverage: 72.107% (-0.3%) from 72.362%
23966986054

push

github

daandemeyer
fd-util: Add missing assert()

1 of 1 new or added line in 1 file covered. (100.0%)

7891 existing lines in 120 files now uncovered.

318256 of 441368 relevant lines covered (72.11%)

1186882.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.71
/src/basic/parse-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <linux/ipv6.h>
4
#include <linux/netfilter/nf_tables.h>
5
#include <stdio.h>
6
#include <stdlib.h>
7
#include <sys/socket.h>
8

9
#include "alloc-util.h"
10
#include "capability-list.h"
11
#include "capability-util.h"
12
#include "errno-list.h"
13
#include "extract-word.h"
14
#include "locale-util.h"
15
#include "log.h"
16
#include "missing-network.h"
17
#include "parse-util.h"
18
#include "path-util.h"
19
#include "process-util.h"
20
#include "string-util.h"
21
#include "strv.h"
22

23
int parse_boolean(const char *v) {
800,122✔
24
        if (!v)
800,122✔
25
                return -EINVAL;
26

27
        if (STRCASE_IN_SET(v,
800,122✔
28
                           "1",
29
                           "yes",
30
                           "y",
31
                           "true",
32
                           "t",
33
                           "on"))
34
                return 1;
206,533✔
35

36
        if (STRCASE_IN_SET(v,
593,589✔
37
                           "0",
38
                           "no",
39
                           "n",
40
                           "false",
41
                           "f",
42
                           "off"))
43
                return 0;
556,103✔
44

45
        return -EINVAL;
37,486✔
46
}
47

48
int parse_tristate_full(const char *v, const char *third, int *ret) {
5,980✔
49
        int r;
5,980✔
50

51
        if (isempty(v) || streq_ptr(v, third)) { /* Empty string is always taken as the third/invalid/auto state */
11,960✔
52
                if (ret)
1✔
53
                        *ret = -1;
1✔
54
        } else {
55
                r = parse_boolean(v);
5,979✔
56
                if (r < 0)
5,979✔
57
                        return r;
58

59
                if (ret)
5,978✔
60
                        *ret = r;
5,978✔
61
        }
62

63
        return 0;
64
}
65

66
int parse_pid(const char *s, pid_t *ret) {
70,557✔
67
        unsigned long ul = 0;
70,557✔
68
        pid_t pid;
70,557✔
69
        int r;
70,557✔
70

71
        assert(s);
70,557✔
72

73
        r = safe_atolu(s, &ul);
70,557✔
74
        if (r < 0)
70,557✔
75
                return r;
70,557✔
76

77
        pid = (pid_t) ul;
70,393✔
78

79
        if ((unsigned long) pid != ul)
70,393✔
80
                return -ERANGE;
81

82
        if (!pid_is_valid(pid))
70,393✔
83
                return -ERANGE;
84

85
        if (ret)
70,391✔
86
                *ret = pid;
70,389✔
87
        return 0;
88
}
89

90
int parse_mode(const char *s, mode_t *ret) {
118,987✔
91
        unsigned m;
118,987✔
92
        int r;
118,987✔
93

94
        assert(s);
118,987✔
95

96
        r = safe_atou_full(s, 8 |
118,987✔
97
                           SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird,
98
                                                        * refuse. User might have wanted to add mode flags or
99
                                                        * so, but this parser doesn't allow that, so let's
100
                                                        * better be safe. */
101
                           &m);
102
        if (r < 0)
118,987✔
103
                return r;
118,987✔
104
        if (m > 07777)
118,972✔
105
                return -ERANGE;
106

107
        if (ret)
118,971✔
108
                *ret = m;
118,971✔
109
        return 0;
110
}
111

112
int parse_ifindex(const char *s) {
172,457✔
113
        int ifi, r;
172,457✔
114

115
        assert(s);
172,457✔
116

117
        r = safe_atoi(s, &ifi);
172,457✔
118
        if (r < 0)
172,457✔
119
                return r;
172,457✔
120
        if (ifi <= 0)
34,725✔
121
                return -EINVAL;
2✔
122

123
        return ifi;
124
}
125

126
int parse_mtu(int family, const char *s, uint32_t *ret) {
243✔
127
        uint64_t u, m;
243✔
128
        int r;
243✔
129

130
        assert(ret);
243✔
131

132
        r = parse_size(s, 1024, &u);
243✔
133
        if (r < 0)
243✔
134
                return r;
243✔
135

136
        if (u > UINT32_MAX)
220✔
137
                return -ERANGE;
138

139
        switch (family) {
216✔
140
        case AF_INET:
141
                m = IPV4_MIN_MTU; /* This is 68 */
142
                break;
143
        case AF_INET6:
29✔
144
                m = IPV6_MIN_MTU; /* This is 1280 */
29✔
145
                break;
29✔
146
        default:
147
                m = 0;
148
        }
149

150
        if (u < m)
39✔
151
                return -ERANGE;
152

153
        *ret = (uint32_t) u;
212✔
154
        return 0;
212✔
155
}
156

157
int parse_size(const char *t, uint64_t base, uint64_t *size) {
12,433✔
158

159
        /* Soo, sometimes we want to parse IEC binary suffixes, and
160
         * sometimes SI decimal suffixes. This function can parse
161
         * both. Which one is the right way depends on the
162
         * context. Wikipedia suggests that SI is customary for
163
         * hardware metrics and network speeds, while IEC is
164
         * customary for most data sizes used by software and volatile
165
         * (RAM) memory. Hence be careful which one you pick!
166
         *
167
         * In either case we use just K, M, G as suffix, and not Ki,
168
         * Mi, Gi or so (as IEC would suggest). That's because that's
169
         * frickin' ugly. But this means you really need to make sure
170
         * to document which base you are parsing when you use this
171
         * call. */
172

173
        struct table {
12,433✔
174
                const char *suffix;
175
                unsigned long long factor;
176
        };
177

178
        static const struct table iec[] = {
12,433✔
179
                { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
180
                { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
181
                { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
182
                { "G", 1024ULL*1024ULL*1024ULL },
183
                { "M", 1024ULL*1024ULL },
184
                { "K", 1024ULL },
185
                { "B", 1ULL },
186
                { "",  1ULL },
187
        };
188

189
        static const struct table si[] = {
12,433✔
190
                { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
191
                { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
192
                { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
193
                { "G", 1000ULL*1000ULL*1000ULL },
194
                { "M", 1000ULL*1000ULL },
195
                { "K", 1000ULL },
196
                { "B", 1ULL },
197
                { "",  1ULL },
198
        };
199

200
        const struct table *table;
12,433✔
201
        const char *p;
12,433✔
202
        unsigned long long r = 0;
12,433✔
203
        unsigned n_entries, start_pos = 0;
12,433✔
204

205
        assert(t);
12,433✔
206
        assert(IN_SET(base, 1000, 1024));
12,433✔
207
        assert(size);
12,433✔
208

209
        if (base == 1000) {
12,433✔
210
                table = si;
211
                n_entries = ELEMENTSOF(si);
212
        } else {
213
                table = iec;
12,365✔
214
                n_entries = ELEMENTSOF(iec);
12,365✔
215
        }
216

217
        p = t;
12,433✔
218
        do {
12,490✔
219
                unsigned long long l, tmp;
12,490✔
220
                double frac = 0;
12,490✔
221
                char *e;
12,490✔
222
                unsigned i;
12,490✔
223

224
                p += strspn(p, WHITESPACE);
12,490✔
225

226
                errno = 0;
12,490✔
227
                l = strtoull(p, &e, 10);
12,490✔
228
                if (errno > 0)
12,490✔
229
                        return -errno;
57✔
230
                if (e == p)
12,488✔
231
                        return -EINVAL;
232
                if (*p == '-')
12,449✔
233
                        return -ERANGE;
234

235
                if (*e == '.') {
12,437✔
236
                        e++;
45✔
237

238
                        /* strtoull() itself would accept space/+/- */
239
                        if (ascii_isdigit(*e)) {
45✔
240
                                unsigned long long l2;
40✔
241
                                char *e2;
40✔
242

243
                                l2 = strtoull(e, &e2, 10);
40✔
244
                                if (errno > 0)
40✔
UNCOV
245
                                        return -errno;
×
246

247
                                /* Ignore failure. E.g. 10.M is valid */
248
                                frac = l2;
40✔
249
                                for (; e < e2; e++)
90✔
250
                                        frac /= 10;
50✔
251
                        }
252
                }
253

254
                e += strspn(e, WHITESPACE);
12,437✔
255

256
                for (i = start_pos; i < n_entries; i++)
94,766✔
257
                        if (startswith(e, table[i].suffix))
94,764✔
258
                                break;
259

260
                if (i >= n_entries)
12,435✔
261
                        return -EINVAL;
262

263
                if (l + (frac > 0) > ULLONG_MAX / table[i].factor)
12,435✔
264
                        return -ERANGE;
265

266
                tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
12,433✔
267
                if (tmp > ULLONG_MAX - r)
12,433✔
268
                        return -ERANGE;
269

270
                r += tmp;
12,433✔
271
                if ((unsigned long long) (uint64_t) r != r)
12,433✔
272
                        return -ERANGE;
273

274
                p = e + strlen(table[i].suffix);
12,433✔
275

276
                start_pos = i + 1;
12,433✔
277

278
        } while (*p);
12,433✔
279

280
        *size = r;
12,376✔
281

282
        return 0;
12,376✔
283
}
284

285
int parse_sector_size(const char *t, uint64_t *ret) {
4✔
286
        int r;
4✔
287

288
        assert(t);
4✔
289
        assert(ret);
4✔
290

291
        uint64_t ss;
4✔
292

293
        r = safe_atou64(t, &ss);
4✔
294
        if (r < 0)
4✔
295
                return log_error_errno(r, "Failed to parse sector size parameter %s", t);
×
296
        if (ss < 512 || ss > 4096) /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */
4✔
297
                return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Sector size not between 512 and 4096: %s", t);
×
298
        if (!ISPOWEROF2(ss))
4✔
UNCOV
299
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size not power of 2: %s", t);
×
300

301
        *ret = ss;
4✔
302
        return 0;
4✔
303
}
304

305
int parse_range(const char *t, unsigned *lower, unsigned *upper) {
366✔
306
        _cleanup_free_ char *word = NULL;
366✔
307
        unsigned l, u;
366✔
308
        int r;
366✔
309

310
        assert(lower);
366✔
311
        assert(upper);
366✔
312

313
        /* Extract the lower bound. */
314
        r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
366✔
315
        if (r < 0)
366✔
316
                return r;
317
        if (r == 0)
366✔
318
                return -EINVAL;
319

320
        r = safe_atou(word, &l);
366✔
321
        if (r < 0)
366✔
322
                return r;
323

324
        /* Check for the upper bound and extract it if needed */
325
        if (!t)
337✔
326
                /* Single number with no dashes. */
327
                u = l;
131✔
328
        else if (!*t)
206✔
329
                /* Trailing dash is an error. */
330
                return -EINVAL;
331
        else {
332
                r = safe_atou(t, &u);
205✔
333
                if (r < 0)
205✔
334
                        return r;
335
        }
336

337
        *lower = l;
324✔
338
        *upper = u;
324✔
339
        return 0;
324✔
340
}
341

342
int parse_errno(const char *t) {
2,242✔
343
        int r, e;
2,242✔
344

345
        assert(t);
2,242✔
346

347
        r = errno_from_name(t);
2,242✔
348
        if (r > 0)
2,242✔
349
                return r;
2,242✔
350

351
        r = safe_atoi(t, &e);
33✔
352
        if (r < 0)
33✔
353
                return r;
354

355
        /* 0 is also allowed here */
356
        if (!errno_is_valid(e) && e != 0)
18✔
357
                return -ERANGE;
5✔
358

359
        return e;
360
}
361

362
int parse_fd(const char *t) {
158,149✔
363
        int r, fd;
158,149✔
364

365
        assert(t);
158,149✔
366

367
        r = safe_atoi(t, &fd);
158,149✔
368
        if (r < 0)
158,149✔
369
                return r;
158,149✔
370

371
        if (fd < 0)
158,145✔
372
                return -EBADF;
2✔
373

374
        return fd;
375
}
376

377
int parse_user_shell(const char *s, char **ret_sh, bool *ret_copy) {
26✔
378
        char *sh;
26✔
379
        int r;
26✔
380

381
        assert(ret_sh);
26✔
382
        assert(ret_copy);
26✔
383

384
        if (path_is_absolute(s) && path_is_normalized(s)) {
26✔
385
                sh = strdup(s);
9✔
386
                if (!sh)
9✔
387
                        return -ENOMEM;
388

389
                *ret_sh = sh;
9✔
390
                *ret_copy = false;
9✔
391
        } else {
392
                r = parse_boolean(s);
17✔
393
                if (r < 0)
17✔
394
                        return r;
395

396
                *ret_sh = NULL;
16✔
397
                *ret_copy = r;
16✔
398
        }
399

400
        return 0;
401
}
402

403
static const char *mangle_base(const char *s, unsigned *base) {
6,053,487✔
404
        const char *k;
6,053,487✔
405

406
        assert(s);
6,053,487✔
407
        assert(base);
6,053,487✔
408

409
        /* Base already explicitly specified, then don't do anything. */
410
        if (SAFE_ATO_MASK_FLAGS(*base) != 0)
6,053,487✔
411
                return s;
6,053,487✔
412

413
        /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */
414
        k = STARTSWITH_SET(s, "0b", "0B");
5,625,376✔
415
        if (k) {
5,625,376✔
416
                *base = 2 | (*base & SAFE_ATO_ALL_FLAGS);
4✔
417
                return k;
4✔
418
        }
419

420
        k = STARTSWITH_SET(s, "0o", "0O");
5,625,372✔
421
        if (k) {
5,625,372✔
422
                *base = 8 | (*base & SAFE_ATO_ALL_FLAGS);
4✔
423
                return k;
4✔
424
        }
425

426
        return s;
427
}
428

429
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
2,197,132✔
430
        char *x = NULL;
2,197,132✔
431
        unsigned long l;
2,197,132✔
432

433
        assert(s);
2,197,132✔
434
        assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
2,197,132✔
435

436
        /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without
437
         * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and
438
         * generate an error. But let's do so only after strtoul() validated that the string is clean
439
         * otherwise, so that we return EINVAL preferably over ERANGE. */
440

441
        if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
2,197,132✔
442
            strchr(WHITESPACE, s[0]))
236,339✔
443
                return -EINVAL;
2,197,132✔
444

445
        s += strspn(s, WHITESPACE);
2,197,115✔
446

447
        if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
2,197,115✔
448
            IN_SET(s[0], '+', '-'))
355,012✔
449
                return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a
450
                                 * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we
451
                                 * blanket refuse +/- prefixed integers, while if it is missing we'll just
452
                                 * return ERANGE, because the string actually parses correctly, but doesn't
453
                                 * fit in the return type. */
454

455
        if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
2,197,098✔
456
            s[0] == '0' && !streq(s, "0"))
235,176✔
457
                return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal
458
                                 * notation and assumed-to-be-decimal integers with a leading zero. */
459

460
        s = mangle_base(s, &base);
2,197,078✔
461

462
        errno = 0;
2,197,078✔
463
        l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual
2,197,078✔
464
                                                      * base is left */);
465
        if (errno > 0)
2,197,078✔
466
                return -errno;
2✔
467
        if (!x || x == s || *x != 0)
2,197,076✔
468
                return -EINVAL;
469
        if (l != 0 && s[0] == '-')
1,969,534✔
470
                return -ERANGE;
471
        if ((unsigned long) (unsigned) l != l)
1,822,051✔
472
                return -ERANGE;
473

474
        if (ret_u)
1,969,505✔
475
                *ret_u = (unsigned) l;
1,969,419✔
476

477
        return 0;
478
}
479

480
int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret) {
100✔
481
        unsigned v;
100✔
482
        int r;
100✔
483

484
        assert(ret);
100✔
485

486
        r = safe_atou(s, &v);
100✔
487
        if (r < 0)
100✔
488
                return r;
100✔
489

490
        if (v < min || v > max)
99✔
491
                return -ERANGE;
492

493
        *ret = v;
97✔
494
        return 0;
97✔
495
}
496

497
int safe_atoi(const char *s, int *ret_i) {
2,081,819✔
498
        unsigned base = 0;
2,081,819✔
499
        char *x = NULL;
2,081,819✔
500
        long l;
2,081,819✔
501

502
        assert(s);
2,081,819✔
503

504
        s += strspn(s, WHITESPACE);
2,081,819✔
505
        s = mangle_base(s, &base);
2,081,819✔
506

507
        errno = 0;
2,081,819✔
508
        l = strtol(s, &x, base);
2,081,819✔
509
        if (errno > 0)
2,081,819✔
510
                return -errno;
8✔
511
        if (!x || x == s || *x != 0)
2,081,811✔
512
                return -EINVAL;
513
        if ((long) (int) l != l)
1,927,277✔
514
                return -ERANGE;
515

516
        if (ret_i)
1,927,270✔
517
                *ret_i = (int) l;
1,926,899✔
518

519
        return 0;
520
}
521

522
int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu) {
1,774,536✔
523
        char *x = NULL;
1,774,536✔
524
        unsigned long long l;
1,774,536✔
525

526
        assert(s);
1,774,536✔
527
        assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
1,774,536✔
528

529
        if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
1,774,536✔
UNCOV
530
            strchr(WHITESPACE, s[0]))
×
531
                return -EINVAL;
1,774,536✔
532

533
        s += strspn(s, WHITESPACE);
1,774,536✔
534

535
        if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
1,774,536✔
UNCOV
536
            IN_SET(s[0], '+', '-'))
×
537
                return -EINVAL;
538

539
        if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
1,774,536✔
UNCOV
540
            s[0] == '0' && s[1] != 0)
×
541
                return -EINVAL;
542

543
        s = mangle_base(s, &base);
1,774,536✔
544

545
        errno = 0;
1,774,536✔
546
        l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base));
1,774,536✔
547
        if (errno > 0)
1,774,536✔
548
                return -errno;
3✔
549
        if (!x || x == s || *x != 0)
1,774,533✔
550
                return -EINVAL;
551
        if (l != 0 && s[0] == '-')
1,774,353✔
552
                return -ERANGE;
553

554
        if (ret_llu)
1,774,346✔
555
                *ret_llu = l;
1,774,346✔
556

557
        return 0;
558
}
559

560
int safe_atolli(const char *s, long long *ret_lli) {
40✔
561
        unsigned base = 0;
40✔
562
        char *x = NULL;
40✔
563
        long long l;
40✔
564

565
        assert(s);
40✔
566

567
        s += strspn(s, WHITESPACE);
40✔
568
        s = mangle_base(s, &base);
40✔
569

570
        errno = 0;
40✔
571
        l = strtoll(s, &x, base);
40✔
572
        if (errno > 0)
40✔
573
                return -errno;
4✔
574
        if (!x || x == s || *x != 0)
36✔
575
                return -EINVAL;
576

577
        if (ret_lli)
28✔
578
                *ret_lli = l;
28✔
579

580
        return 0;
581
}
582

583
int safe_atou8_full(const char *s, unsigned base, uint8_t *ret) {
144,448✔
584
        unsigned u;
144,448✔
585
        int r;
144,448✔
586

587
        assert(ret);
144,448✔
588

589
        r = safe_atou_full(s, base, &u);
144,448✔
590
        if (r < 0)
144,448✔
591
                return r;
144,448✔
592
        if (u > UINT8_MAX)
144,298✔
593
                return -ERANGE;
594

595
        *ret = (uint8_t) u;
144,297✔
596
        return 0;
144,297✔
597
}
598

599
int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) {
3,901✔
600
        unsigned u;
3,901✔
601
        int r;
3,901✔
602

603
        assert(ret);
3,901✔
604

605
        r = safe_atou_full(s, base, &u);
3,901✔
606
        if (r < 0)
3,901✔
607
                return r;
3,901✔
608
        if (u > UINT16_MAX)
3,818✔
609
                return -ERANGE;
610

611
        *ret = (uint16_t) u;
3,811✔
612
        return 0;
3,811✔
613
}
614

615
int safe_atoi16(const char *s, int16_t *ret) {
14✔
616
        unsigned base = 0;
14✔
617
        char *x = NULL;
14✔
618
        long l;
14✔
619

620
        assert(s);
14✔
621

622
        s += strspn(s, WHITESPACE);
14✔
623
        s = mangle_base(s, &base);
14✔
624

625
        errno = 0;
14✔
626
        l = strtol(s, &x, base);
14✔
627
        if (errno > 0)
14✔
UNCOV
628
                return -errno;
×
629
        if (!x || x == s || *x != 0)
14✔
630
                return -EINVAL;
631
        if ((long) (int16_t) l != l)
10✔
632
                return -ERANGE;
633

634
        if (ret)
8✔
635
                *ret = (int16_t) l;
8✔
636

637
        return 0;
638
}
639

640
int safe_atod(const char *s, double *ret_d) {
9✔
641
        _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
18✔
642
        char *x = NULL;
9✔
643
        double d = 0;
9✔
644

645
        assert(s);
9✔
646

647
        loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
9✔
648
        if (loc == (locale_t) 0)
9✔
UNCOV
649
                return -errno;
×
650

651
        errno = 0;
9✔
652
        d = strtod_l(s, &x, loc);
9✔
653
        if (errno > 0)
9✔
UNCOV
654
                return -errno;
×
655
        if (!x || x == s || *x != 0)
9✔
656
                return -EINVAL;
657

658
        if (ret_d)
6✔
659
                *ret_d = d;
6✔
660

661
        return 0;
662
}
663

664
int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
456✔
665
        unsigned val = 0;
456✔
666
        const char *s;
456✔
667

668
        assert(p);
456✔
669
        assert(res);
456✔
670

671
        s = *p;
456✔
672

673
        /* accept any number of digits, strtoull is limited to 19 */
674
        for (size_t i = 0; i < digits; i++, s++) {
2,690✔
675
                if (!ascii_isdigit(*s)) {
2,410✔
676
                        if (i == 0)
176✔
677
                                return -EINVAL;
678

679
                        /* too few digits, pad with 0 */
680
                        for (; i < digits; i++)
678✔
681
                                val *= 10;
502✔
682

683
                        break;
684
                }
685

686
                val *= 10;
2,234✔
687
                val += *s - '0';
2,234✔
688
        }
689

690
        /* maybe round up */
691
        if (*s >= '5' && *s <= '9')
456✔
692
                val++;
4✔
693

694
        s += strspn(s, DIGITS);
456✔
695

696
        *p = s;
456✔
697
        *res = val;
456✔
698

699
        return 0;
456✔
700
}
701

702
int parse_nice(const char *s, int *ret) {
325✔
703
        int n, r;
325✔
704

705
        assert(ret);
325✔
706

707
        r = safe_atoi(s, &n);
325✔
708
        if (r < 0)
325✔
709
                return r;
325✔
710

711
        if (!nice_is_valid(n))
319✔
712
                return -ERANGE;
713

714
        *ret = n;
313✔
715
        return 0;
313✔
716
}
717

718
int parse_ip_port(const char *s, uint16_t *ret) {
301✔
719
        uint16_t l;
301✔
720
        int r;
301✔
721

722
        assert(ret);
301✔
723

724
        r = safe_atou16_full(s, SAFE_ATO_REFUSE_LEADING_WHITESPACE, &l);
301✔
725
        if (r < 0)
301✔
726
                return r;
301✔
727

728
        if (l == 0)
228✔
729
                return -EINVAL;
730

731
        *ret = l;
223✔
732

733
        return 0;
223✔
734
}
735

736
int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero) {
36✔
737
        unsigned l, h;
36✔
738
        int r;
36✔
739

740
        assert(low);
36✔
741
        assert(high);
36✔
742

743
        r = parse_range(s, &l, &h);
36✔
744
        if (r < 0)
36✔
745
                return r;
36✔
746

747
        if (l > 65535 || h > 65535)
26✔
748
                return -EINVAL;
749

750
        if (!allow_zero && (l == 0 || h == 0))
24✔
751
                return -EINVAL;
752

753
        if (h < l)
24✔
754
                return -EINVAL;
755

756
        *low = l;
23✔
757
        *high = h;
23✔
758

759
        return 0;
23✔
760
}
761

762
int parse_oom_score_adjust(const char *s, int *ret) {
868✔
763
        int r, v;
868✔
764

765
        assert(s);
868✔
766
        assert(ret);
868✔
767

768
        r = safe_atoi(s, &v);
868✔
769
        if (r < 0)
868✔
770
                return r;
868✔
771

772
        if (!oom_score_adjust_is_valid(v))
866✔
773
                return -ERANGE;
774

775
        *ret = v;
866✔
776
        return 0;
866✔
777
}
778

779
int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) {
792✔
780
        assert(ret);
792✔
781

782
        if (i >= (~0UL << LOADAVG_PRECISION_BITS))
792✔
783
                return -ERANGE;
784

785
        i = i << LOADAVG_PRECISION_BITS;
791✔
786
        f = DIV_ROUND_UP((f << LOADAVG_PRECISION_BITS), 100);
791✔
787

788
        if (f >= LOADAVG_FIXED_POINT_1_0)
791✔
789
                return -ERANGE;
790

791
        *ret = i | f;
789✔
792
        return 0;
789✔
793
}
794

795
int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
767✔
796
        const char *d, *f_str, *i_str;
767✔
797
        unsigned long i, f;
767✔
798
        int r;
767✔
799

800
        assert(s);
767✔
801
        assert(ret);
767✔
802

803
        d = strchr(s, '.');
767✔
804
        if (!d)
767✔
805
                return -EINVAL;
767✔
806

807
        i_str = strndupa_safe(s, d - s);
764✔
808
        f_str = d + 1;
764✔
809

810
        r = safe_atolu_full(i_str, 10, &i);
764✔
811
        if (r < 0)
764✔
812
                return r;
813

814
        r = safe_atolu_full(f_str, 10, &f);
762✔
815
        if (r < 0)
762✔
816
                return r;
817

818
        return store_loadavg_fixed_point(i, f, ret);
760✔
819
}
820

821
/* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
822
 * https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
823
bool nft_identifier_valid(const char *id) {
106✔
824
        if (isempty(id))
106✔
825
                return false;
826

827
        if (strlen(id) >= NFT_NAME_MAXLEN)
104✔
828
                return false;
829

830
        if (!ascii_isalpha(id[0]))
103✔
831
                return false;
832

833
        return in_charset(id + 1, ALPHANUMERICAL "/\\_.");
101✔
834
}
835

836
int parse_capability_set(const char *s, uint64_t initial, uint64_t *current) {
2,340✔
837
        int r;
2,340✔
838

839
        assert(s);
2,340✔
840
        assert(current);
2,340✔
841

842
        if (isempty(s)) {
2,340✔
843
                *current = CAP_MASK_UNSET;
32✔
844
                return 1;
32✔
845
        }
846

847
        bool invert = false;
2,308✔
848
        if (s[0] == '~') {
2,308✔
849
                invert = true;
151✔
850
                s++;
151✔
851
        }
852

853
        uint64_t parsed;
2,308✔
854
        r = capability_set_from_string(s, &parsed);
2,308✔
855
        if (r < 0)
2,308✔
856
                return r;
857

858
        if (parsed == 0 || *current == initial)
2,308✔
859
                /* "~" or uninitialized data -> replace */
860
                *current = invert ? all_capabilities() & ~parsed : parsed;
2,439✔
861
        else {
862
                /* previous data -> merge */
863
                if (invert)
14✔
864
                        *current &= ~parsed;
6✔
865
                else
866
                        *current |= parsed;
8✔
867
        }
868

869
        return r;
870
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc