]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/parse-util.c
logind: Don't match non-leader processes for utmp TTY determination (#38027)
[thirdparty/systemd.git] / src / basic / parse-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
6bedfcbb 2
47a71f98 3#include <linux/ipv6.h>
78f2c174 4#include <linux/netfilter/nf_tables.h>
11c3a366
TA
5#include <stdio.h>
6#include <stdlib.h>
f91c6093 7#include <sys/socket.h>
11c3a366 8
28cb17ef 9#include "alloc-util.h"
cf26c4a7 10#include "errno-list.h"
28cb17ef 11#include "extract-word.h"
e520e0fc 12#include "locale-util.h"
93a1f792 13#include "log.h"
f5947a5e 14#include "missing_network.h"
93cc7779 15#include "parse-util.h"
41bf0590 16#include "process-util.h"
6bedfcbb 17#include "string-util.h"
ddd6a22a 18#include "strv.h"
6bedfcbb
LP
19
20int parse_boolean(const char *v) {
b06f0cc6
LP
21 if (!v)
22 return -EINVAL;
6bedfcbb 23
ddd6a22a
LP
24 if (STRCASE_IN_SET(v,
25 "1",
26 "yes",
27 "y",
28 "true",
29 "t",
30 "on"))
6bedfcbb 31 return 1;
ddd6a22a
LP
32
33 if (STRCASE_IN_SET(v,
34 "0",
35 "no",
36 "n",
37 "false",
38 "f",
39 "off"))
6bedfcbb
LP
40 return 0;
41
42 return -EINVAL;
43}
44
b71a721f
LP
45int parse_tristate_full(const char *v, const char *third, int *ret) {
46 int r;
47
48 if (isempty(v) || streq_ptr(v, third)) { /* Empty string is always taken as the third/invalid/auto state */
49 if (ret)
50 *ret = -1;
51 } else {
52 r = parse_boolean(v);
53 if (r < 0)
54 return r;
55
56 if (ret)
57 *ret = r;
58 }
59
60 return 0;
61}
62
6bedfcbb
LP
63int parse_pid(const char *s, pid_t* ret_pid) {
64 unsigned long ul = 0;
65 pid_t pid;
66 int r;
67
68 assert(s);
6bedfcbb
LP
69
70 r = safe_atolu(s, &ul);
71 if (r < 0)
72 return r;
73
74 pid = (pid_t) ul;
75
76 if ((unsigned long) pid != ul)
77 return -ERANGE;
78
54191eb3 79 if (!pid_is_valid(pid))
6bedfcbb
LP
80 return -ERANGE;
81
91ce42f0
LP
82 if (ret_pid)
83 *ret_pid = pid;
6bedfcbb
LP
84 return 0;
85}
86
87int parse_mode(const char *s, mode_t *ret) {
c44702a8
LP
88 unsigned m;
89 int r;
6bedfcbb
LP
90
91 assert(s);
2d49a208 92
c44702a8
LP
93 r = safe_atou_full(s, 8 |
94 SAFE_ATO_REFUSE_PLUS_MINUS, /* Leading '+' or even '-' char? that's just weird,
95 * refuse. User might have wanted to add mode flags or
96 * so, but this parser doesn't allow that, so let's
97 * better be safe. */
98 &m);
99 if (r < 0)
100 return r;
101 if (m > 07777)
6bedfcbb
LP
102 return -ERANGE;
103
c44702a8
LP
104 if (ret)
105 *ret = m;
6bedfcbb
LP
106 return 0;
107}
108
597da51b 109int parse_ifindex(const char *s) {
6ad623a3
LP
110 int ifi, r;
111
f5072281 112 assert(s);
f5072281 113
6ad623a3
LP
114 r = safe_atoi(s, &ifi);
115 if (r < 0)
116 return r;
117 if (ifi <= 0)
118 return -EINVAL;
119
597da51b 120 return ifi;
88465a4e
YW
121}
122
f91c6093 123int parse_mtu(int family, const char *s, uint32_t *ret) {
a0460dfe 124 uint64_t u, m;
f91c6093
LP
125 int r;
126
127 r = parse_size(s, 1024, &u);
128 if (r < 0)
129 return r;
130
131 if (u > UINT32_MAX)
132 return -ERANGE;
133
a0460dfe
YW
134 switch (family) {
135 case AF_INET:
136 m = IPV4_MIN_MTU; /* This is 68 */
137 break;
138 case AF_INET6:
f91c6093 139 m = IPV6_MIN_MTU; /* This is 1280 */
a0460dfe
YW
140 break;
141 default:
142 m = 0;
143 }
f91c6093
LP
144
145 if (u < m)
146 return -ERANGE;
147
148 *ret = (uint32_t) u;
149 return 0;
150}
151
6bedfcbb
LP
152int parse_size(const char *t, uint64_t base, uint64_t *size) {
153
154 /* Soo, sometimes we want to parse IEC binary suffixes, and
155 * sometimes SI decimal suffixes. This function can parse
156 * both. Which one is the right way depends on the
157 * context. Wikipedia suggests that SI is customary for
158 * hardware metrics and network speeds, while IEC is
159 * customary for most data sizes used by software and volatile
160 * (RAM) memory. Hence be careful which one you pick!
161 *
162 * In either case we use just K, M, G as suffix, and not Ki,
163 * Mi, Gi or so (as IEC would suggest). That's because that's
164 * frickin' ugly. But this means you really need to make sure
165 * to document which base you are parsing when you use this
166 * call. */
167
168 struct table {
169 const char *suffix;
170 unsigned long long factor;
171 };
172
173 static const struct table iec[] = {
174 { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
175 { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
176 { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
177 { "G", 1024ULL*1024ULL*1024ULL },
178 { "M", 1024ULL*1024ULL },
179 { "K", 1024ULL },
180 { "B", 1ULL },
181 { "", 1ULL },
182 };
183
184 static const struct table si[] = {
185 { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
186 { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL },
187 { "T", 1000ULL*1000ULL*1000ULL*1000ULL },
188 { "G", 1000ULL*1000ULL*1000ULL },
189 { "M", 1000ULL*1000ULL },
190 { "K", 1000ULL },
191 { "B", 1ULL },
192 { "", 1ULL },
193 };
194
195 const struct table *table;
196 const char *p;
197 unsigned long long r = 0;
198 unsigned n_entries, start_pos = 0;
199
200 assert(t);
4c701096 201 assert(IN_SET(base, 1000, 1024));
6bedfcbb
LP
202 assert(size);
203
204 if (base == 1000) {
205 table = si;
206 n_entries = ELEMENTSOF(si);
207 } else {
208 table = iec;
209 n_entries = ELEMENTSOF(iec);
210 }
211
212 p = t;
213 do {
214 unsigned long long l, tmp;
215 double frac = 0;
216 char *e;
217 unsigned i;
218
219 p += strspn(p, WHITESPACE);
6bedfcbb
LP
220
221 errno = 0;
222 l = strtoull(p, &e, 10);
b3267152 223 if (errno > 0)
6bedfcbb
LP
224 return -errno;
225 if (e == p)
226 return -EINVAL;
2d49a208
LP
227 if (*p == '-')
228 return -ERANGE;
6bedfcbb
LP
229
230 if (*e == '.') {
231 e++;
232
233 /* strtoull() itself would accept space/+/- */
ff25d338 234 if (ascii_isdigit(*e)) {
6bedfcbb
LP
235 unsigned long long l2;
236 char *e2;
237
238 l2 = strtoull(e, &e2, 10);
b3267152 239 if (errno > 0)
6bedfcbb
LP
240 return -errno;
241
242 /* Ignore failure. E.g. 10.M is valid */
243 frac = l2;
244 for (; e < e2; e++)
245 frac /= 10;
246 }
247 }
248
249 e += strspn(e, WHITESPACE);
250
251 for (i = start_pos; i < n_entries; i++)
252 if (startswith(e, table[i].suffix))
253 break;
254
255 if (i >= n_entries)
256 return -EINVAL;
257
258 if (l + (frac > 0) > ULLONG_MAX / table[i].factor)
259 return -ERANGE;
260
261 tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor);
262 if (tmp > ULLONG_MAX - r)
263 return -ERANGE;
264
265 r += tmp;
266 if ((unsigned long long) (uint64_t) r != r)
267 return -ERANGE;
268
269 p = e + strlen(table[i].suffix);
270
271 start_pos = i + 1;
272
273 } while (*p);
274
275 *size = r;
276
277 return 0;
278}
279
e1878ef7
DDM
280int parse_sector_size(const char *t, uint64_t *ret) {
281 int r;
282
283 assert(t);
284 assert(ret);
285
286 uint64_t ss;
287
288 r = safe_atou64(t, &ss);
289 if (r < 0)
290 return log_error_errno(r, "Failed to parse sector size parameter %s", t);
291 if (ss < 512 || ss > 4096) /* Allow up to 4K due to dm-crypt support and 4K alignment by the homed LUKS backend */
292 return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Sector size not between 512 and 4096: %s", t);
293 if (!ISPOWEROF2(ss))
294 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Sector size not power of 2: %s", t);
295
296 *ret = ss;
297 return 0;
298}
299
28cb17ef
FB
300int parse_range(const char *t, unsigned *lower, unsigned *upper) {
301 _cleanup_free_ char *word = NULL;
302 unsigned l, u;
303 int r;
304
305 assert(lower);
306 assert(upper);
307
308 /* Extract the lower bound. */
309 r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
310 if (r < 0)
311 return r;
312 if (r == 0)
313 return -EINVAL;
314
315 r = safe_atou(word, &l);
316 if (r < 0)
317 return r;
318
319 /* Check for the upper bound and extract it if needed */
320 if (!t)
321 /* Single number with no dashes. */
322 u = l;
323 else if (!*t)
324 /* Trailing dash is an error. */
325 return -EINVAL;
326 else {
327 r = safe_atou(t, &u);
328 if (r < 0)
329 return r;
330 }
331
332 *lower = l;
333 *upper = u;
334 return 0;
335}
336
cf26c4a7
YW
337int parse_errno(const char *t) {
338 int r, e;
339
340 assert(t);
341
342 r = errno_from_name(t);
343 if (r > 0)
344 return r;
345
346 r = safe_atoi(t, &e);
347 if (r < 0)
348 return r;
349
33d12153
YW
350 /* 0 is also allowed here */
351 if (!errno_is_valid(e) && e != 0)
cf26c4a7
YW
352 return -ERANGE;
353
354 return e;
355}
356
b8f83d7f
DT
357int parse_fd(const char *t) {
358 int r, fd;
359
360 assert(t);
361
362 r = safe_atoi(t, &fd);
363 if (r < 0)
364 return r;
365
366 if (fd < 0)
d2132d3d 367 return -EBADF;
b8f83d7f
DT
368
369 return fd;
370}
371
fc80cabc
LP
372static const char *mangle_base(const char *s, unsigned *base) {
373 const char *k;
374
375 assert(s);
376 assert(base);
377
378 /* Base already explicitly specified, then don't do anything. */
379 if (SAFE_ATO_MASK_FLAGS(*base) != 0)
380 return s;
381
382 /* Support Python 3 style "0b" and 0x" prefixes, because they truly make sense, much more than C's "0" prefix for octal. */
383 k = STARTSWITH_SET(s, "0b", "0B");
384 if (k) {
385 *base = 2 | (*base & SAFE_ATO_ALL_FLAGS);
386 return k;
387 }
388
389 k = STARTSWITH_SET(s, "0o", "0O");
390 if (k) {
391 *base = 8 | (*base & SAFE_ATO_ALL_FLAGS);
392 return k;
393 }
394
395 return s;
396}
397
65baa289 398int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
6bedfcbb
LP
399 char *x = NULL;
400 unsigned long l;
401
402 assert(s);
707e93af 403 assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
6bedfcbb 404
707e93af
LP
405 /* strtoul() is happy to parse negative values, and silently converts them to unsigned values without
406 * generating an error. We want a clean error, hence let's look for the "-" prefix on our own, and
407 * generate an error. But let's do so only after strtoul() validated that the string is clean
408 * otherwise, so that we return EINVAL preferably over ERANGE. */
409
410 if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
411 strchr(WHITESPACE, s[0]))
412 return -EINVAL;
6bedfcbb 413
2d49a208 414 s += strspn(s, WHITESPACE);
6bedfcbb 415
707e93af
LP
416 if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
417 IN_SET(s[0], '+', '-'))
418 return -EINVAL; /* Note that we check the "-" prefix again a second time below, but return a
419 * different error. I.e. if the SAFE_ATO_REFUSE_PLUS_MINUS flag is set we
420 * blanket refuse +/- prefixed integers, while if it is missing we'll just
421 * return ERANGE, because the string actually parses correctly, but doesn't
422 * fit in the return type. */
423
424 if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
425 s[0] == '0' && !streq(s, "0"))
426 return -EINVAL; /* This is particularly useful to avoid ambiguities between C's octal
427 * notation and assumed-to-be-decimal integers with a leading zero. */
428
fc80cabc
LP
429 s = mangle_base(s, &base);
430
2d49a208 431 errno = 0;
707e93af
LP
432 l = strtoul(s, &x, SAFE_ATO_MASK_FLAGS(base) /* Let's mask off the flags bits so that only the actual
433 * base is left */);
b3267152 434 if (errno > 0)
2d49a208 435 return -errno;
b5ffbc55 436 if (!x || x == s || *x != 0)
2d49a208 437 return -EINVAL;
c78eefc1 438 if (l != 0 && s[0] == '-')
2d49a208 439 return -ERANGE;
6bedfcbb
LP
440 if ((unsigned long) (unsigned) l != l)
441 return -ERANGE;
442
22810041
LP
443 if (ret_u)
444 *ret_u = (unsigned) l;
445
6bedfcbb
LP
446 return 0;
447}
448
3b6cabd8
ZJS
449int safe_atou_bounded(const char *s, unsigned min, unsigned max, unsigned *ret) {
450 unsigned v;
451 int r;
452
453 r = safe_atou(s, &v);
454 if (r < 0)
455 return r;
456
457 if (v < min || v > max)
458 return -ERANGE;
459
460 *ret = v;
461 return 0;
462}
463
6bedfcbb 464int safe_atoi(const char *s, int *ret_i) {
fc80cabc 465 unsigned base = 0;
6bedfcbb
LP
466 char *x = NULL;
467 long l;
468
469 assert(s);
6bedfcbb 470
fc80cabc
LP
471 s += strspn(s, WHITESPACE);
472 s = mangle_base(s, &base);
473
6bedfcbb 474 errno = 0;
fc80cabc 475 l = strtol(s, &x, base);
b3267152 476 if (errno > 0)
2d49a208 477 return -errno;
b5ffbc55 478 if (!x || x == s || *x != 0)
2d49a208 479 return -EINVAL;
6bedfcbb
LP
480 if ((long) (int) l != l)
481 return -ERANGE;
482
22810041
LP
483 if (ret_i)
484 *ret_i = (int) l;
485
6bedfcbb
LP
486 return 0;
487}
488
da185cd0 489int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu) {
6bedfcbb
LP
490 char *x = NULL;
491 unsigned long long l;
492
493 assert(s);
707e93af
LP
494 assert(SAFE_ATO_MASK_FLAGS(base) <= 16);
495
496 if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_WHITESPACE) &&
497 strchr(WHITESPACE, s[0]))
498 return -EINVAL;
6bedfcbb 499
2d49a208
LP
500 s += strspn(s, WHITESPACE);
501
707e93af
LP
502 if (FLAGS_SET(base, SAFE_ATO_REFUSE_PLUS_MINUS) &&
503 IN_SET(s[0], '+', '-'))
504 return -EINVAL;
505
506 if (FLAGS_SET(base, SAFE_ATO_REFUSE_LEADING_ZERO) &&
507 s[0] == '0' && s[1] != 0)
508 return -EINVAL;
509
fc80cabc
LP
510 s = mangle_base(s, &base);
511
6bedfcbb 512 errno = 0;
707e93af 513 l = strtoull(s, &x, SAFE_ATO_MASK_FLAGS(base));
b3267152 514 if (errno > 0)
2d49a208 515 return -errno;
b5ffbc55 516 if (!x || x == s || *x != 0)
2d49a208 517 return -EINVAL;
c78eefc1 518 if (l != 0 && s[0] == '-')
2d49a208 519 return -ERANGE;
6bedfcbb 520
22810041
LP
521 if (ret_llu)
522 *ret_llu = l;
523
6bedfcbb
LP
524 return 0;
525}
526
9ec7d7ae 527int safe_atolli(const char *s, long long *ret_lli) {
fc80cabc 528 unsigned base = 0;
6bedfcbb
LP
529 char *x = NULL;
530 long long l;
531
532 assert(s);
6bedfcbb 533
fc80cabc
LP
534 s += strspn(s, WHITESPACE);
535 s = mangle_base(s, &base);
536
6bedfcbb 537 errno = 0;
fc80cabc 538 l = strtoll(s, &x, base);
b3267152 539 if (errno > 0)
2d49a208 540 return -errno;
b5ffbc55 541 if (!x || x == s || *x != 0)
2d49a208 542 return -EINVAL;
6bedfcbb 543
22810041
LP
544 if (ret_lli)
545 *ret_lli = l;
546
6bedfcbb
LP
547 return 0;
548}
549
11a1ac59
LP
550int safe_atou8_full(const char *s, unsigned base, uint8_t *ret) {
551 unsigned u;
552 int r;
2d49a208 553
11a1ac59
LP
554 r = safe_atou_full(s, base, &u);
555 if (r < 0)
556 return r;
557 if (u > UINT8_MAX)
6bedfcbb
LP
558 return -ERANGE;
559
11a1ac59 560 *ret = (uint8_t) u;
6bedfcbb
LP
561 return 0;
562}
563
5ef56aa2 564int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) {
c7410120
LP
565 unsigned u;
566 int r;
fc80cabc 567
c7410120
LP
568 r = safe_atou_full(s, base, &u);
569 if (r < 0)
570 return r;
571 if (u > UINT16_MAX)
6bedfcbb
LP
572 return -ERANGE;
573
c7410120 574 *ret = (uint16_t) u;
6bedfcbb
LP
575 return 0;
576}
577
578int safe_atoi16(const char *s, int16_t *ret) {
fc80cabc 579 unsigned base = 0;
6bedfcbb
LP
580 char *x = NULL;
581 long l;
582
583 assert(s);
6bedfcbb 584
fc80cabc
LP
585 s += strspn(s, WHITESPACE);
586 s = mangle_base(s, &base);
587
6bedfcbb 588 errno = 0;
fc80cabc 589 l = strtol(s, &x, base);
b3267152 590 if (errno > 0)
2d49a208 591 return -errno;
b5ffbc55 592 if (!x || x == s || *x != 0)
2d49a208 593 return -EINVAL;
6bedfcbb
LP
594 if ((long) (int16_t) l != l)
595 return -ERANGE;
596
22810041
LP
597 if (ret)
598 *ret = (int16_t) l;
599
6bedfcbb
LP
600 return 0;
601}
602
603int safe_atod(const char *s, double *ret_d) {
e520e0fc 604 _cleanup_(freelocalep) locale_t loc = (locale_t) 0;
6bedfcbb
LP
605 char *x = NULL;
606 double d = 0;
6bedfcbb
LP
607
608 assert(s);
6bedfcbb
LP
609
610 loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0);
611 if (loc == (locale_t) 0)
612 return -errno;
613
614 errno = 0;
615 d = strtod_l(s, &x, loc);
e520e0fc 616 if (errno > 0)
2d49a208 617 return -errno;
e520e0fc 618 if (!x || x == s || *x != 0)
2d49a208 619 return -EINVAL;
6bedfcbb 620
22810041
LP
621 if (ret_d)
622 *ret_d = (double) d;
623
6bedfcbb
LP
624 return 0;
625}
436dd70f
HV
626
627int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) {
436dd70f
HV
628 unsigned val = 0;
629 const char *s;
630
631 s = *p;
632
f21f31b2 633 /* accept any number of digits, strtoull is limited to 19 */
7f5c82aa 634 for (size_t i = 0; i < digits; i++, s++) {
ff25d338 635 if (!ascii_isdigit(*s)) {
436dd70f
HV
636 if (i == 0)
637 return -EINVAL;
638
639 /* too few digits, pad with 0 */
640 for (; i < digits; i++)
641 val *= 10;
642
643 break;
644 }
645
646 val *= 10;
647 val += *s - '0';
648 }
649
650 /* maybe round up */
651 if (*s >= '5' && *s <= '9')
652 val++;
653
654 s += strspn(s, DIGITS);
655
656 *p = s;
657 *res = val;
658
659 return 0;
660}
9184ca48 661
41bf0590
LP
662int parse_nice(const char *p, int *ret) {
663 int n, r;
664
665 r = safe_atoi(p, &n);
666 if (r < 0)
667 return r;
668
669 if (!nice_is_valid(n))
670 return -ERANGE;
671
672 *ret = n;
673 return 0;
674}
10452f7c
SS
675
676int parse_ip_port(const char *s, uint16_t *ret) {
677 uint16_t l;
678 int r;
679
4c9bb708 680 r = safe_atou16_full(s, SAFE_ATO_REFUSE_LEADING_WHITESPACE, &l);
10452f7c
SS
681 if (r < 0)
682 return r;
683
684 if (l == 0)
685 return -EINVAL;
686
687 *ret = (uint16_t) l;
688
689 return 0;
690}
fbcc7f41 691
dcfac3a3 692int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high, bool allow_zero) {
926062f0
SS
693 unsigned l, h;
694 int r;
695
696 r = parse_range(s, &l, &h);
697 if (r < 0)
698 return r;
699
dcfac3a3 700 if (l > 65535 || h > 65535)
701 return -EINVAL;
702
703 if (!allow_zero && (l == 0 || h == 0))
926062f0
SS
704 return -EINVAL;
705
706 if (h < l)
707 return -EINVAL;
708
709 *low = l;
710 *high = h;
711
712 return 0;
713}
714
e9eb2c02
LP
715int parse_oom_score_adjust(const char *s, int *ret) {
716 int r, v;
717
718 assert(s);
719 assert(ret);
720
721 r = safe_atoi(s, &v);
722 if (r < 0)
723 return r;
724
c62f67f7 725 if (!oom_score_adjust_is_valid(v))
e9eb2c02
LP
726 return -ERANGE;
727
728 *ret = v;
729 return 0;
730}
510ca79c
AZ
731
732int store_loadavg_fixed_point(unsigned long i, unsigned long f, loadavg_t *ret) {
733 assert(ret);
734
3542da24 735 if (i >= (~0UL << LOADAVG_PRECISION_BITS))
510ca79c
AZ
736 return -ERANGE;
737
3542da24
LB
738 i = i << LOADAVG_PRECISION_BITS;
739 f = DIV_ROUND_UP((f << LOADAVG_PRECISION_BITS), 100);
510ca79c 740
3542da24 741 if (f >= LOADAVG_FIXED_POINT_1_0)
510ca79c
AZ
742 return -ERANGE;
743
744 *ret = i | f;
745 return 0;
746}
747
748int parse_loadavg_fixed_point(const char *s, loadavg_t *ret) {
749 const char *d, *f_str, *i_str;
750 unsigned long i, f;
751 int r;
752
753 assert(s);
754 assert(ret);
755
756 d = strchr(s, '.');
757 if (!d)
758 return -EINVAL;
759
2f82562b 760 i_str = strndupa_safe(s, d - s);
510ca79c
AZ
761 f_str = d + 1;
762
763 r = safe_atolu_full(i_str, 10, &i);
764 if (r < 0)
765 return r;
766
767 r = safe_atolu_full(f_str, 10, &f);
768 if (r < 0)
769 return r;
770
771 return store_loadavg_fixed_point(i, f, ret);
772}
fc289dd0
TM
773
774/* Limitations are described in https://www.netfilter.org/projects/nftables/manpage.html and
775 * https://bugzilla.netfilter.org/show_bug.cgi?id=1175 */
776bool nft_identifier_valid(const char *id) {
78f2c174 777 if (isempty(id))
fc289dd0
TM
778 return false;
779
78f2c174 780 if (strlen(id) >= NFT_NAME_MAXLEN)
fc289dd0
TM
781 return false;
782
783 if (!ascii_isalpha(id[0]))
784 return false;
785
78f2c174 786 return in_charset(id + 1, ALPHANUMERICAL "/\\_.");
fc289dd0 787}