/src/systemd/src/basic/parse-util.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | | |
3 | | #include <errno.h> |
4 | | #include <inttypes.h> |
5 | | #include <linux/oom.h> |
6 | | #include <locale.h> |
7 | | #include <net/if.h> |
8 | | #include <stdio.h> |
9 | | #include <stdlib.h> |
10 | | #include <string.h> |
11 | | #include <sys/socket.h> |
12 | | |
13 | | #include "alloc-util.h" |
14 | | #include "errno-list.h" |
15 | | #include "extract-word.h" |
16 | | #include "locale-util.h" |
17 | | #include "macro.h" |
18 | | #include "missing.h" |
19 | | #include "parse-util.h" |
20 | | #include "process-util.h" |
21 | | #include "stat-util.h" |
22 | | #include "string-util.h" |
23 | | |
24 | 49.2k | int parse_boolean(const char *v) { |
25 | 49.2k | if (!v) |
26 | 0 | return -EINVAL; |
27 | 49.2k | |
28 | 49.2k | if (streq(v, "1") || strcaseeq(v, "yes") || strcaseeq(v, "y") || strcaseeq(v, "true") || strcaseeq(v, "t") || strcaseeq(v, "on")) |
29 | 49.2k | return 1; |
30 | 36.0k | else if (streq(v, "0") || strcaseeq(v, "no") || strcaseeq(v, "n") || strcaseeq(v, "false") || strcaseeq(v, "f") || strcaseeq(v, "off")) |
31 | 36.0k | return 0; |
32 | 19.2k | |
33 | 19.2k | return -EINVAL; |
34 | 19.2k | } |
35 | | |
36 | 3.29k | int parse_pid(const char *s, pid_t* ret_pid) { |
37 | 3.29k | unsigned long ul = 0; |
38 | 3.29k | pid_t pid; |
39 | 3.29k | int r; |
40 | 3.29k | |
41 | 3.29k | assert(s); |
42 | 3.29k | assert(ret_pid); |
43 | 3.29k | |
44 | 3.29k | r = safe_atolu(s, &ul); |
45 | 3.29k | if (r < 0) |
46 | 929 | return r; |
47 | 2.36k | |
48 | 2.36k | pid = (pid_t) ul; |
49 | 2.36k | |
50 | 2.36k | if ((unsigned long) pid != ul) |
51 | 257 | return -ERANGE; |
52 | 2.10k | |
53 | 2.10k | if (!pid_is_valid(pid)) |
54 | 332 | return -ERANGE; |
55 | 1.77k | |
56 | 1.77k | *ret_pid = pid; |
57 | 1.77k | return 0; |
58 | 1.77k | } |
59 | | |
60 | 8.83k | int parse_mode(const char *s, mode_t *ret) { |
61 | 8.83k | char *x; |
62 | 8.83k | long l; |
63 | 8.83k | |
64 | 8.83k | assert(s); |
65 | 8.83k | assert(ret); |
66 | 8.83k | |
67 | 8.83k | s += strspn(s, WHITESPACE); |
68 | 8.83k | if (s[0] == '-') |
69 | 711 | return -ERANGE; |
70 | 8.12k | |
71 | 8.12k | errno = 0; |
72 | 8.12k | l = strtol(s, &x, 8); |
73 | 8.12k | if (errno > 0) |
74 | 416 | return -errno; |
75 | 7.71k | if (!x || x == s || *x != 0) |
76 | 5.34k | return -EINVAL; |
77 | 2.36k | if (l < 0 || l > 07777) |
78 | 1.02k | return -ERANGE; |
79 | 1.34k | |
80 | 1.34k | *ret = (mode_t) l; |
81 | 1.34k | return 0; |
82 | 1.34k | } |
83 | | |
84 | 57 | int parse_ifindex(const char *s, int *ret) { |
85 | 57 | int ifi, r; |
86 | 57 | |
87 | 57 | assert(s); |
88 | 57 | assert(ret); |
89 | 57 | |
90 | 57 | r = safe_atoi(s, &ifi); |
91 | 57 | if (r < 0) |
92 | 3 | return r; |
93 | 54 | if (ifi <= 0) |
94 | 35 | return -EINVAL; |
95 | 19 | |
96 | 19 | *ret = ifi; |
97 | 19 | return 0; |
98 | 19 | } |
99 | | |
100 | 0 | int parse_ifindex_or_ifname(const char *s, int *ret) { |
101 | 0 | int r; |
102 | 0 |
|
103 | 0 | assert(s); |
104 | 0 | assert(ret); |
105 | 0 |
|
106 | 0 | r = parse_ifindex(s, ret); |
107 | 0 | if (r >= 0) |
108 | 0 | return r; |
109 | 0 | |
110 | 0 | r = (int) if_nametoindex(s); |
111 | 0 | if (r <= 0) |
112 | 0 | return -errno; |
113 | 0 | |
114 | 0 | *ret = r; |
115 | 0 | return 0; |
116 | 0 | } |
117 | | |
118 | 9.84k | int parse_mtu(int family, const char *s, uint32_t *ret) { |
119 | 9.84k | uint64_t u; |
120 | 9.84k | size_t m; |
121 | 9.84k | int r; |
122 | 9.84k | |
123 | 9.84k | r = parse_size(s, 1024, &u); |
124 | 9.84k | if (r < 0) |
125 | 6.31k | return r; |
126 | 3.53k | |
127 | 3.53k | if (u > UINT32_MAX) |
128 | 3.53k | return -ERANGE; |
129 | 2.38k | |
130 | 2.38k | if (family == AF_INET6) |
131 | 2.38k | m = IPV6_MIN_MTU; /* This is 1280 */ |
132 | 2.38k | else |
133 | 2.38k | m = IPV4_MIN_MTU; /* For all other protocols, including 'unspecified' we assume the IPv4 minimal MTU */ |
134 | 2.38k | |
135 | 2.38k | if (u < m) |
136 | 948 | return -ERANGE; |
137 | 1.43k | |
138 | 1.43k | *ret = (uint32_t) u; |
139 | 1.43k | return 0; |
140 | 1.43k | } |
141 | | |
142 | 24.5k | int parse_size(const char *t, uint64_t base, uint64_t *size) { |
143 | 24.5k | |
144 | 24.5k | /* Soo, sometimes we want to parse IEC binary suffixes, and |
145 | 24.5k | * sometimes SI decimal suffixes. This function can parse |
146 | 24.5k | * both. Which one is the right way depends on the |
147 | 24.5k | * context. Wikipedia suggests that SI is customary for |
148 | 24.5k | * hardware metrics and network speeds, while IEC is |
149 | 24.5k | * customary for most data sizes used by software and volatile |
150 | 24.5k | * (RAM) memory. Hence be careful which one you pick! |
151 | 24.5k | * |
152 | 24.5k | * In either case we use just K, M, G as suffix, and not Ki, |
153 | 24.5k | * Mi, Gi or so (as IEC would suggest). That's because that's |
154 | 24.5k | * frickin' ugly. But this means you really need to make sure |
155 | 24.5k | * to document which base you are parsing when you use this |
156 | 24.5k | * call. */ |
157 | 24.5k | |
158 | 24.5k | struct table { |
159 | 24.5k | const char *suffix; |
160 | 24.5k | unsigned long long factor; |
161 | 24.5k | }; |
162 | 24.5k | |
163 | 24.5k | static const struct table iec[] = { |
164 | 24.5k | { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, |
165 | 24.5k | { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL }, |
166 | 24.5k | { "T", 1024ULL*1024ULL*1024ULL*1024ULL }, |
167 | 24.5k | { "G", 1024ULL*1024ULL*1024ULL }, |
168 | 24.5k | { "M", 1024ULL*1024ULL }, |
169 | 24.5k | { "K", 1024ULL }, |
170 | 24.5k | { "B", 1ULL }, |
171 | 24.5k | { "", 1ULL }, |
172 | 24.5k | }; |
173 | 24.5k | |
174 | 24.5k | static const struct table si[] = { |
175 | 24.5k | { "E", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, |
176 | 24.5k | { "P", 1000ULL*1000ULL*1000ULL*1000ULL*1000ULL }, |
177 | 24.5k | { "T", 1000ULL*1000ULL*1000ULL*1000ULL }, |
178 | 24.5k | { "G", 1000ULL*1000ULL*1000ULL }, |
179 | 24.5k | { "M", 1000ULL*1000ULL }, |
180 | 24.5k | { "K", 1000ULL }, |
181 | 24.5k | { "B", 1ULL }, |
182 | 24.5k | { "", 1ULL }, |
183 | 24.5k | }; |
184 | 24.5k | |
185 | 24.5k | const struct table *table; |
186 | 24.5k | const char *p; |
187 | 24.5k | unsigned long long r = 0; |
188 | 24.5k | unsigned n_entries, start_pos = 0; |
189 | 24.5k | |
190 | 24.5k | assert(t); |
191 | 24.5k | assert(IN_SET(base, 1000, 1024)); |
192 | 24.5k | assert(size); |
193 | 24.5k | |
194 | 24.5k | if (base == 1000) { |
195 | 3.82k | table = si; |
196 | 3.82k | n_entries = ELEMENTSOF(si); |
197 | 20.6k | } else { |
198 | 20.6k | table = iec; |
199 | 20.6k | n_entries = ELEMENTSOF(iec); |
200 | 20.6k | } |
201 | 24.5k | |
202 | 24.5k | p = t; |
203 | 33.6k | do { |
204 | 33.6k | unsigned long long l, tmp; |
205 | 33.6k | double frac = 0; |
206 | 33.6k | char *e; |
207 | 33.6k | unsigned i; |
208 | 33.6k | |
209 | 33.6k | p += strspn(p, WHITESPACE); |
210 | 33.6k | |
211 | 33.6k | errno = 0; |
212 | 33.6k | l = strtoull(p, &e, 10); |
213 | 33.6k | if (errno > 0) |
214 | 983 | return -errno; |
215 | 32.6k | if (e == p) |
216 | 6.69k | return -EINVAL; |
217 | 26.0k | if (*p == '-') |
218 | 1.34k | return -ERANGE; |
219 | 24.6k | |
220 | 24.6k | if (*e == '.') { |
221 | 6.60k | e++; |
222 | 6.60k | |
223 | 6.60k | /* strtoull() itself would accept space/+/- */ |
224 | 6.60k | if (*e >= '0' && *e <= '9') { |
225 | 3.60k | unsigned long long l2; |
226 | 3.60k | char *e2; |
227 | 3.60k | |
228 | 3.60k | l2 = strtoull(e, &e2, 10); |
229 | 3.60k | if (errno > 0) |
230 | 972 | return -errno; |
231 | 2.62k | |
232 | 2.62k | /* Ignore failure. E.g. 10.M is valid */ |
233 | 2.62k | frac = l2; |
234 | 8.74k | for (; e < e2; e++) |
235 | 6.11k | frac /= 10; |
236 | 2.62k | } |
237 | 6.60k | } |
238 | 24.6k | |
239 | 24.6k | e += strspn(e, WHITESPACE); |
240 | 23.6k | |
241 | 138k | for (i = start_pos; i < n_entries; i++) |
242 | 137k | if (startswith(e, table[i].suffix)) |
243 | 22.0k | break; |
244 | 23.6k | |
245 | 23.6k | if (i >= n_entries) |
246 | 1.59k | return -EINVAL; |
247 | 22.0k | |
248 | 22.0k | if (l + (frac > 0) > ULLONG_MAX / table[i].factor) |
249 | 1.00k | return -ERANGE; |
250 | 21.0k | |
251 | 21.0k | tmp = l * table[i].factor + (unsigned long long) (frac * table[i].factor); |
252 | 21.0k | if (tmp > ULLONG_MAX - r) |
253 | 1.03k | return -ERANGE; |
254 | 20.0k | |
255 | 20.0k | r += tmp; |
256 | 20.0k | if ((unsigned long long) (uint64_t) r != r) |
257 | 0 | return -ERANGE; |
258 | 20.0k | |
259 | 20.0k | p = e + strlen(table[i].suffix); |
260 | 20.0k | |
261 | 20.0k | start_pos = i + 1; |
262 | 20.0k | |
263 | 20.0k | } while (*p); |
264 | 24.5k | |
265 | 24.5k | *size = r; |
266 | 10.8k | |
267 | 10.8k | return 0; |
268 | 24.5k | } |
269 | | |
270 | 7.32k | int parse_range(const char *t, unsigned *lower, unsigned *upper) { |
271 | 7.32k | _cleanup_free_ char *word = NULL; |
272 | 7.32k | unsigned l, u; |
273 | 7.32k | int r; |
274 | 7.32k | |
275 | 7.32k | assert(lower); |
276 | 7.32k | assert(upper); |
277 | 7.32k | |
278 | 7.32k | /* Extract the lower bound. */ |
279 | 7.32k | r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS); |
280 | 7.32k | if (r < 0) |
281 | 389 | return r; |
282 | 6.94k | if (r == 0) |
283 | 0 | return -EINVAL; |
284 | 6.94k | |
285 | 6.94k | r = safe_atou(word, &l); |
286 | 6.94k | if (r < 0) |
287 | 796 | return r; |
288 | 6.14k | |
289 | 6.14k | /* Check for the upper bound and extract it if needed */ |
290 | 6.14k | if (!t) |
291 | 2.80k | /* Single number with no dashes. */ |
292 | 2.80k | u = l; |
293 | 3.33k | else if (!*t) |
294 | 425 | /* Trailing dash is an error. */ |
295 | 425 | return -EINVAL; |
296 | 3.33k | else { |
297 | 2.91k | r = safe_atou(t, &u); |
298 | 2.91k | if (r < 0) |
299 | 525 | return r; |
300 | 5.19k | } |
301 | 5.19k | |
302 | 5.19k | *lower = l; |
303 | 5.19k | *upper = u; |
304 | 5.19k | return 0; |
305 | 5.19k | } |
306 | | |
307 | 0 | int parse_errno(const char *t) { |
308 | 0 | int r, e; |
309 | 0 |
|
310 | 0 | assert(t); |
311 | 0 |
|
312 | 0 | r = errno_from_name(t); |
313 | 0 | if (r > 0) |
314 | 0 | return r; |
315 | 0 | |
316 | 0 | r = safe_atoi(t, &e); |
317 | 0 | if (r < 0) |
318 | 0 | return r; |
319 | 0 | |
320 | 0 | /* 0 is also allowed here */ |
321 | 0 | if (!errno_is_valid(e) && e != 0) |
322 | 0 | return -ERANGE; |
323 | 0 | |
324 | 0 | return e; |
325 | 0 | } |
326 | | |
327 | 0 | int parse_syscall_and_errno(const char *in, char **name, int *error) { |
328 | 0 | _cleanup_free_ char *n = NULL; |
329 | 0 | char *p; |
330 | 0 | int e = -1; |
331 | 0 |
|
332 | 0 | assert(in); |
333 | 0 | assert(name); |
334 | 0 | assert(error); |
335 | 0 |
|
336 | 0 | /* |
337 | 0 | * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255". |
338 | 0 | * If errno is omitted, then error is set to -1. |
339 | 0 | * Empty syscall name is not allowed. |
340 | 0 | * Here, we do not check that the syscall name is valid or not. |
341 | 0 | */ |
342 | 0 |
|
343 | 0 | p = strchr(in, ':'); |
344 | 0 | if (p) { |
345 | 0 | e = parse_errno(p + 1); |
346 | 0 | if (e < 0) |
347 | 0 | return e; |
348 | 0 | |
349 | 0 | n = strndup(in, p - in); |
350 | 0 | } else |
351 | 0 | n = strdup(in); |
352 | 0 |
|
353 | 0 | if (!n) |
354 | 0 | return -ENOMEM; |
355 | 0 | |
356 | 0 | if (isempty(n)) |
357 | 0 | return -EINVAL; |
358 | 0 | |
359 | 0 | *error = e; |
360 | 0 | *name = TAKE_PTR(n); |
361 | 0 |
|
362 | 0 | return 0; |
363 | 0 | } |
364 | | |
365 | 80.3k | char *format_bytes(char *buf, size_t l, uint64_t t) { |
366 | 80.3k | unsigned i; |
367 | 80.3k | |
368 | 80.3k | /* This only does IEC units so far */ |
369 | 80.3k | |
370 | 80.3k | static const struct { |
371 | 80.3k | const char *suffix; |
372 | 80.3k | uint64_t factor; |
373 | 80.3k | } table[] = { |
374 | 80.3k | { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, |
375 | 80.3k | { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, |
376 | 80.3k | { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, |
377 | 80.3k | { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) }, |
378 | 80.3k | { "M", UINT64_C(1024)*UINT64_C(1024) }, |
379 | 80.3k | { "K", UINT64_C(1024) }, |
380 | 80.3k | }; |
381 | 80.3k | |
382 | 80.3k | if (t == (uint64_t) -1) |
383 | 0 | return NULL; |
384 | 80.3k | |
385 | 560k | for (i = 0; i < ELEMENTSOF(table); i++) { |
386 | 482k | |
387 | 482k | if (t >= table[i].factor) { |
388 | 1.61k | snprintf(buf, l, |
389 | 1.61k | "%" PRIu64 ".%" PRIu64 "%s", |
390 | 1.61k | t / table[i].factor, |
391 | 1.61k | ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10), |
392 | 1.61k | table[i].suffix); |
393 | 1.61k | |
394 | 1.61k | goto finish; |
395 | 1.61k | } |
396 | 482k | } |
397 | 80.3k | |
398 | 80.3k | snprintf(buf, l, "%" PRIu64 "B", t); |
399 | 78.7k | |
400 | 80.3k | finish: |
401 | 80.3k | buf[l-1] = 0; |
402 | 80.3k | return buf; |
403 | 78.7k | |
404 | 78.7k | } |
405 | | |
406 | 150k | int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) { |
407 | 150k | char *x = NULL; |
408 | 150k | unsigned long l; |
409 | 150k | |
410 | 150k | assert(s); |
411 | 150k | assert(ret_u); |
412 | 150k | assert(base <= 16); |
413 | 150k | |
414 | 150k | /* strtoul() is happy to parse negative values, and silently |
415 | 150k | * converts them to unsigned values without generating an |
416 | 150k | * error. We want a clean error, hence let's look for the "-" |
417 | 150k | * prefix on our own, and generate an error. But let's do so |
418 | 150k | * only after strtoul() validated that the string is clean |
419 | 150k | * otherwise, so that we return EINVAL preferably over |
420 | 150k | * ERANGE. */ |
421 | 150k | |
422 | 150k | s += strspn(s, WHITESPACE); |
423 | 150k | |
424 | 150k | errno = 0; |
425 | 150k | l = strtoul(s, &x, base); |
426 | 150k | if (errno > 0) |
427 | 1.34k | return -errno; |
428 | 149k | if (!x || x == s || *x != 0) |
429 | 113k | return -EINVAL; |
430 | 35.5k | if (s[0] == '-') |
431 | 1.27k | return -ERANGE; |
432 | 34.2k | if ((unsigned long) (unsigned) l != l) |
433 | 1.95k | return -ERANGE; |
434 | 32.3k | |
435 | 32.3k | *ret_u = (unsigned) l; |
436 | 32.3k | return 0; |
437 | 32.3k | } |
438 | | |
439 | 88.1k | int safe_atoi(const char *s, int *ret_i) { |
440 | 88.1k | char *x = NULL; |
441 | 88.1k | long l; |
442 | 88.1k | |
443 | 88.1k | assert(s); |
444 | 88.1k | assert(ret_i); |
445 | 88.1k | |
446 | 88.1k | errno = 0; |
447 | 88.1k | l = strtol(s, &x, 0); |
448 | 88.1k | if (errno > 0) |
449 | 1.33k | return -errno; |
450 | 86.7k | if (!x || x == s || *x != 0) |
451 | 35.1k | return -EINVAL; |
452 | 51.6k | if ((long) (int) l != l) |
453 | 2.64k | return -ERANGE; |
454 | 49.0k | |
455 | 49.0k | *ret_i = (int) l; |
456 | 49.0k | return 0; |
457 | 49.0k | } |
458 | | |
459 | 67.5k | int safe_atollu(const char *s, long long unsigned *ret_llu) { |
460 | 67.5k | char *x = NULL; |
461 | 67.5k | unsigned long long l; |
462 | 67.5k | |
463 | 67.5k | assert(s); |
464 | 67.5k | assert(ret_llu); |
465 | 67.5k | |
466 | 67.5k | s += strspn(s, WHITESPACE); |
467 | 67.5k | |
468 | 67.5k | errno = 0; |
469 | 67.5k | l = strtoull(s, &x, 0); |
470 | 67.5k | if (errno > 0) |
471 | 2.47k | return -errno; |
472 | 65.0k | if (!x || x == s || *x != 0) |
473 | 14.9k | return -EINVAL; |
474 | 50.1k | if (*s == '-') |
475 | 4.47k | return -ERANGE; |
476 | 45.6k | |
477 | 45.6k | *ret_llu = l; |
478 | 45.6k | return 0; |
479 | 45.6k | } |
480 | | |
481 | 780 | int safe_atolli(const char *s, long long int *ret_lli) { |
482 | 780 | char *x = NULL; |
483 | 780 | long long l; |
484 | 780 | |
485 | 780 | assert(s); |
486 | 780 | assert(ret_lli); |
487 | 780 | |
488 | 780 | errno = 0; |
489 | 780 | l = strtoll(s, &x, 0); |
490 | 780 | if (errno > 0) |
491 | 195 | return -errno; |
492 | 585 | if (!x || x == s || *x != 0) |
493 | 388 | return -EINVAL; |
494 | 197 | |
495 | 197 | *ret_lli = l; |
496 | 197 | return 0; |
497 | 197 | } |
498 | | |
499 | 11.6k | int safe_atou8(const char *s, uint8_t *ret) { |
500 | 11.6k | char *x = NULL; |
501 | 11.6k | unsigned long l; |
502 | 11.6k | |
503 | 11.6k | assert(s); |
504 | 11.6k | assert(ret); |
505 | 11.6k | |
506 | 11.6k | s += strspn(s, WHITESPACE); |
507 | 11.6k | |
508 | 11.6k | errno = 0; |
509 | 11.6k | l = strtoul(s, &x, 0); |
510 | 11.6k | if (errno > 0) |
511 | 588 | return -errno; |
512 | 11.0k | if (!x || x == s || *x != 0) |
513 | 4.02k | return -EINVAL; |
514 | 6.99k | if (s[0] == '-') |
515 | 649 | return -ERANGE; |
516 | 6.34k | if ((unsigned long) (uint8_t) l != l) |
517 | 1.02k | return -ERANGE; |
518 | 5.31k | |
519 | 5.31k | *ret = (uint8_t) l; |
520 | 5.31k | return 0; |
521 | 5.31k | } |
522 | | |
523 | 26.9k | int safe_atou16_full(const char *s, unsigned base, uint16_t *ret) { |
524 | 26.9k | char *x = NULL; |
525 | 26.9k | unsigned long l; |
526 | 26.9k | |
527 | 26.9k | assert(s); |
528 | 26.9k | assert(ret); |
529 | 26.9k | assert(base <= 16); |
530 | 26.9k | |
531 | 26.9k | s += strspn(s, WHITESPACE); |
532 | 26.9k | |
533 | 26.9k | errno = 0; |
534 | 26.9k | l = strtoul(s, &x, base); |
535 | 26.9k | if (errno > 0) |
536 | 793 | return -errno; |
537 | 26.1k | if (!x || x == s || *x != 0) |
538 | 12.7k | return -EINVAL; |
539 | 13.4k | if (s[0] == '-') |
540 | 1.02k | return -ERANGE; |
541 | 12.3k | if ((unsigned long) (uint16_t) l != l) |
542 | 1.36k | return -ERANGE; |
543 | 11.0k | |
544 | 11.0k | *ret = (uint16_t) l; |
545 | 11.0k | return 0; |
546 | 11.0k | } |
547 | | |
548 | 0 | int safe_atoi16(const char *s, int16_t *ret) { |
549 | 0 | char *x = NULL; |
550 | 0 | long l; |
551 | 0 |
|
552 | 0 | assert(s); |
553 | 0 | assert(ret); |
554 | 0 |
|
555 | 0 | errno = 0; |
556 | 0 | l = strtol(s, &x, 0); |
557 | 0 | if (errno > 0) |
558 | 0 | return -errno; |
559 | 0 | if (!x || x == s || *x != 0) |
560 | 0 | return -EINVAL; |
561 | 0 | if ((long) (int16_t) l != l) |
562 | 0 | return -ERANGE; |
563 | 0 | |
564 | 0 | *ret = (int16_t) l; |
565 | 0 | return 0; |
566 | 0 | } |
567 | | |
568 | 0 | int safe_atod(const char *s, double *ret_d) { |
569 | 0 | _cleanup_(freelocalep) locale_t loc = (locale_t) 0; |
570 | 0 | char *x = NULL; |
571 | 0 | double d = 0; |
572 | 0 |
|
573 | 0 | assert(s); |
574 | 0 | assert(ret_d); |
575 | 0 |
|
576 | 0 | loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t) 0); |
577 | 0 | if (loc == (locale_t) 0) |
578 | 0 | return -errno; |
579 | 0 | |
580 | 0 | errno = 0; |
581 | 0 | d = strtod_l(s, &x, loc); |
582 | 0 | if (errno > 0) |
583 | 0 | return -errno; |
584 | 0 | if (!x || x == s || *x != 0) |
585 | 0 | return -EINVAL; |
586 | 0 | |
587 | 0 | *ret_d = (double) d; |
588 | 0 | return 0; |
589 | 0 | } |
590 | | |
591 | 4.81k | int parse_fractional_part_u(const char **p, size_t digits, unsigned *res) { |
592 | 4.81k | size_t i; |
593 | 4.81k | unsigned val = 0; |
594 | 4.81k | const char *s; |
595 | 4.81k | |
596 | 4.81k | s = *p; |
597 | 4.81k | |
598 | 4.81k | /* accept any number of digits, strtoull is limited to 19 */ |
599 | 19.9k | for (i=0; i < digits; i++,s++) { |
600 | 18.3k | if (*s < '0' || *s > '9') { |
601 | 3.23k | if (i == 0) |
602 | 697 | return -EINVAL; |
603 | 2.54k | |
604 | 2.54k | /* too few digits, pad with 0 */ |
605 | 12.1k | for (; i < digits; i++) |
606 | 9.60k | val *= 10; |
607 | 2.54k | |
608 | 2.54k | break; |
609 | 2.54k | } |
610 | 15.1k | |
611 | 15.1k | val *= 10; |
612 | 15.1k | val += *s - '0'; |
613 | 15.1k | } |
614 | 4.81k | |
615 | 4.81k | /* maybe round up */ |
616 | 4.81k | if (*s >= '5' && *s <= '9') |
617 | 1.03k | val++; |
618 | 4.11k | |
619 | 4.11k | s += strspn(s, DIGITS); |
620 | 4.11k | |
621 | 4.11k | *p = s; |
622 | 4.11k | *res = val; |
623 | 4.11k | |
624 | 4.11k | return 0; |
625 | 4.81k | } |
626 | | |
627 | 0 | int parse_percent_unbounded(const char *p) { |
628 | 0 | const char *pc, *n; |
629 | 0 | int r, v; |
630 | 0 |
|
631 | 0 | pc = endswith(p, "%"); |
632 | 0 | if (!pc) |
633 | 0 | return -EINVAL; |
634 | 0 | |
635 | 0 | n = strndupa(p, pc - p); |
636 | 0 | r = safe_atoi(n, &v); |
637 | 0 | if (r < 0) |
638 | 0 | return r; |
639 | 0 | if (v < 0) |
640 | 0 | return -ERANGE; |
641 | 0 | |
642 | 0 | return v; |
643 | 0 | } |
644 | | |
645 | 0 | int parse_percent(const char *p) { |
646 | 0 | int v; |
647 | 0 |
|
648 | 0 | v = parse_percent_unbounded(p); |
649 | 0 | if (v > 100) |
650 | 0 | return -ERANGE; |
651 | 0 | |
652 | 0 | return v; |
653 | 0 | } |
654 | | |
655 | 9.26k | int parse_permille_unbounded(const char *p) { |
656 | 9.26k | const char *pc, *pm, *dot, *n; |
657 | 9.26k | int r, q, v; |
658 | 9.26k | |
659 | 9.26k | pm = endswith(p, "‰"); |
660 | 9.26k | if (pm) { |
661 | 1.82k | n = strndupa(p, pm - p); |
662 | 1.82k | r = safe_atoi(n, &v); |
663 | 1.82k | if (r < 0) |
664 | 846 | return r; |
665 | 977 | if (v < 0) |
666 | 449 | return -ERANGE; |
667 | 7.44k | } else { |
668 | 7.44k | pc = endswith(p, "%"); |
669 | 7.44k | if (!pc) |
670 | 2.60k | return -EINVAL; |
671 | 4.83k | |
672 | 4.83k | dot = memchr(p, '.', pc - p); |
673 | 4.83k | if (dot) { |
674 | 1.78k | if (dot + 2 != pc) |
675 | 428 | return -EINVAL; |
676 | 1.35k | if (dot[1] < '0' || dot[1] > '9') |
677 | 783 | return -EINVAL; |
678 | 570 | q = dot[1] - '0'; |
679 | 570 | n = strndupa(p, dot - p); |
680 | 3.05k | } else { |
681 | 3.05k | q = 0; |
682 | 3.05k | n = strndupa(p, pc - p); |
683 | 3.05k | } |
684 | 4.83k | r = safe_atoi(n, &v); |
685 | 3.62k | if (r < 0) |
686 | 763 | return r; |
687 | 2.86k | if (v < 0) |
688 | 612 | return -ERANGE; |
689 | 2.25k | if (v > (INT_MAX - q) / 10) |
690 | 408 | return -ERANGE; |
691 | 1.84k | |
692 | 1.84k | v = v * 10 + q; |
693 | 1.84k | } |
694 | 9.26k | |
695 | 9.26k | return v; |
696 | 9.26k | } |
697 | | |
698 | 8.70k | int parse_permille(const char *p) { |
699 | 8.70k | int v; |
700 | 8.70k | |
701 | 8.70k | v = parse_permille_unbounded(p); |
702 | 8.70k | if (v > 1000) |
703 | 617 | return -ERANGE; |
704 | 8.08k | |
705 | 8.08k | return v; |
706 | 8.08k | } |
707 | | |
708 | 971 | int parse_nice(const char *p, int *ret) { |
709 | 971 | int n, r; |
710 | 971 | |
711 | 971 | r = safe_atoi(p, &n); |
712 | 971 | if (r < 0) |
713 | 321 | return r; |
714 | 650 | |
715 | 650 | if (!nice_is_valid(n)) |
716 | 435 | return -ERANGE; |
717 | 215 | |
718 | 215 | *ret = n; |
719 | 215 | return 0; |
720 | 215 | } |
721 | | |
722 | 8.12k | int parse_ip_port(const char *s, uint16_t *ret) { |
723 | 8.12k | uint16_t l; |
724 | 8.12k | int r; |
725 | 8.12k | |
726 | 8.12k | r = safe_atou16(s, &l); |
727 | 8.12k | if (r < 0) |
728 | 3.93k | return r; |
729 | 4.19k | |
730 | 4.19k | if (l == 0) |
731 | 755 | return -EINVAL; |
732 | 3.43k | |
733 | 3.43k | *ret = (uint16_t) l; |
734 | 3.43k | |
735 | 3.43k | return 0; |
736 | 3.43k | } |
737 | | |
738 | 5.39k | int parse_ip_port_range(const char *s, uint16_t *low, uint16_t *high) { |
739 | 5.39k | unsigned l, h; |
740 | 5.39k | int r; |
741 | 5.39k | |
742 | 5.39k | r = parse_range(s, &l, &h); |
743 | 5.39k | if (r < 0) |
744 | 2.11k | return r; |
745 | 3.28k | |
746 | 3.28k | if (l <= 0 || l > 65535 || h <= 0 || h > 65535) |
747 | 2.10k | return -EINVAL; |
748 | 1.17k | |
749 | 1.17k | if (h < l) |
750 | 410 | return -EINVAL; |
751 | 769 | |
752 | 769 | *low = l; |
753 | 769 | *high = h; |
754 | 769 | |
755 | 769 | return 0; |
756 | 769 | } |
757 | | |
758 | 358 | int parse_dev(const char *s, dev_t *ret) { |
759 | 358 | const char *major; |
760 | 358 | unsigned x, y; |
761 | 358 | size_t n; |
762 | 358 | int r; |
763 | 358 | |
764 | 358 | n = strspn(s, DIGITS); |
765 | 358 | if (n == 0) |
766 | 2 | return -EINVAL; |
767 | 356 | if (s[n] != ':') |
768 | 27 | return -EINVAL; |
769 | 329 | |
770 | 329 | major = strndupa(s, n); |
771 | 329 | r = safe_atou(major, &x); |
772 | 329 | if (r < 0) |
773 | 98 | return r; |
774 | 231 | |
775 | 231 | r = safe_atou(s + n + 1, &y); |
776 | 231 | if (r < 0) |
777 | 114 | return r; |
778 | 117 | |
779 | 117 | if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y)) |
780 | 117 | return -ERANGE; |
781 | 57 | |
782 | 57 | *ret = makedev(x, y); |
783 | 57 | return 0; |
784 | 57 | } |
785 | | |
786 | 859 | int parse_oom_score_adjust(const char *s, int *ret) { |
787 | 859 | int r, v; |
788 | 859 | |
789 | 859 | assert(s); |
790 | 859 | assert(ret); |
791 | 859 | |
792 | 859 | r = safe_atoi(s, &v); |
793 | 859 | if (r < 0) |
794 | 208 | return r; |
795 | 651 | |
796 | 651 | if (v < OOM_SCORE_ADJ_MIN || v > OOM_SCORE_ADJ_MAX) |
797 | 651 | return -ERANGE; |
798 | 210 | |
799 | 210 | *ret = v; |
800 | 210 | return 0; |
801 | 210 | } |