]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
a7334b09 | 2 | |
ed5bcfbe | 3 | #include <errno.h> |
a8fbdf54 TA |
4 | #include <limits.h> |
5 | #include <stdint.h> | |
07630cea | 6 | #include <stdio.h> |
ed5bcfbe | 7 | #include <stdlib.h> |
a8fbdf54 | 8 | #include <sys/types.h> |
07630cea | 9 | |
b5efdb8a | 10 | #include "alloc-util.h" |
e8461023 | 11 | #include "conf-files.h" |
6bedfcbb | 12 | #include "conf-parser.h" |
e6dde451 | 13 | #include "def.h" |
0ec2a7a1 | 14 | #include "ether-addr-util.h" |
a8fbdf54 | 15 | #include "extract-word.h" |
6bedfcbb | 16 | #include "fd-util.h" |
e6dde451 | 17 | #include "fileio.h" |
f4f15635 | 18 | #include "fs-util.h" |
cf074772 | 19 | #include "in-addr-util.h" |
16354eff | 20 | #include "log.h" |
07630cea | 21 | #include "macro.h" |
f5947a5e | 22 | #include "missing_network.h" |
d8b4d14d | 23 | #include "nulstr-util.h" |
6bedfcbb | 24 | #include "parse-util.h" |
9eb977db | 25 | #include "path-util.h" |
ed5033fd | 26 | #include "percent-util.h" |
7b3e062c | 27 | #include "process-util.h" |
ef118d00 | 28 | #include "rlimit-util.h" |
12963533 | 29 | #include "sd-id128.h" |
0ec2a7a1 | 30 | #include "set.h" |
f757855e | 31 | #include "signal-util.h" |
d31645ad | 32 | #include "socket-util.h" |
07630cea LP |
33 | #include "string-util.h" |
34 | #include "strv.h" | |
7b3e062c | 35 | #include "syslog-util.h" |
a8fbdf54 | 36 | #include "time-util.h" |
07630cea | 37 | #include "utf8.h" |
e8e581bf | 38 | |
f975e971 | 39 | int config_item_table_lookup( |
e9f3d2d5 | 40 | const void *table, |
ed5bcfbe | 41 | const char *section, |
ed5bcfbe | 42 | const char *lvalue, |
0b954099 LP |
43 | ConfigParserCallback *ret_func, |
44 | int *ret_ltype, | |
45 | void **ret_data, | |
ed5bcfbe LP |
46 | void *userdata) { |
47 | ||
e9f3d2d5 | 48 | const ConfigTableItem *t; |
f975e971 LP |
49 | |
50 | assert(table); | |
ed5bcfbe | 51 | assert(lvalue); |
0b954099 LP |
52 | assert(ret_func); |
53 | assert(ret_ltype); | |
54 | assert(ret_data); | |
ed5bcfbe | 55 | |
f975e971 | 56 | for (t = table; t->lvalue; t++) { |
ed5bcfbe | 57 | |
f975e971 | 58 | if (!streq(lvalue, t->lvalue)) |
ed5bcfbe LP |
59 | continue; |
60 | ||
f975e971 | 61 | if (!streq_ptr(section, t->section)) |
ed5bcfbe LP |
62 | continue; |
63 | ||
0b954099 LP |
64 | *ret_func = t->parse; |
65 | *ret_ltype = t->ltype; | |
66 | *ret_data = t->data; | |
f975e971 LP |
67 | return 1; |
68 | } | |
ed5bcfbe | 69 | |
0b954099 LP |
70 | *ret_func = NULL; |
71 | *ret_ltype = 0; | |
72 | *ret_data = NULL; | |
f975e971 LP |
73 | return 0; |
74 | } | |
10e87ee7 | 75 | |
f975e971 | 76 | int config_item_perf_lookup( |
e9f3d2d5 | 77 | const void *table, |
f975e971 LP |
78 | const char *section, |
79 | const char *lvalue, | |
0b954099 LP |
80 | ConfigParserCallback *ret_func, |
81 | int *ret_ltype, | |
82 | void **ret_data, | |
f975e971 LP |
83 | void *userdata) { |
84 | ||
85 | ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table; | |
86 | const ConfigPerfItem *p; | |
87 | ||
88 | assert(table); | |
89 | assert(lvalue); | |
0b954099 LP |
90 | assert(ret_func); |
91 | assert(ret_ltype); | |
92 | assert(ret_data); | |
f975e971 | 93 | |
4472fa6d LP |
94 | if (section) { |
95 | const char *key; | |
f975e971 | 96 | |
4472fa6d | 97 | key = strjoina(section, ".", lvalue); |
f975e971 | 98 | p = lookup(key, strlen(key)); |
4472fa6d LP |
99 | } else |
100 | p = lookup(lvalue, strlen(lvalue)); | |
0b954099 LP |
101 | if (!p) { |
102 | *ret_func = NULL; | |
103 | *ret_ltype = 0; | |
104 | *ret_data = NULL; | |
f975e971 | 105 | return 0; |
0b954099 | 106 | } |
f975e971 | 107 | |
0b954099 LP |
108 | *ret_func = p->parse; |
109 | *ret_ltype = p->ltype; | |
110 | *ret_data = (uint8_t*) userdata + p->offset; | |
f975e971 LP |
111 | return 1; |
112 | } | |
113 | ||
114 | /* Run the user supplied parser for an assignment */ | |
bcde742e LP |
115 | static int next_assignment( |
116 | const char *unit, | |
117 | const char *filename, | |
118 | unsigned line, | |
119 | ConfigItemLookup lookup, | |
120 | const void *table, | |
121 | const char *section, | |
122 | unsigned section_line, | |
123 | const char *lvalue, | |
124 | const char *rvalue, | |
125 | ConfigParseFlags flags, | |
126 | void *userdata) { | |
f975e971 LP |
127 | |
128 | ConfigParserCallback func = NULL; | |
129 | int ltype = 0; | |
130 | void *data = NULL; | |
131 | int r; | |
132 | ||
133 | assert(filename); | |
134 | assert(line > 0); | |
135 | assert(lookup); | |
136 | assert(lvalue); | |
137 | assert(rvalue); | |
138 | ||
139 | r = lookup(table, section, lvalue, &func, <ype, &data, userdata); | |
140 | if (r < 0) | |
141 | return r; | |
d937fbbd | 142 | if (r > 0) { |
0b954099 LP |
143 | if (!func) |
144 | return 0; | |
d937fbbd | 145 | |
0b954099 LP |
146 | return func(unit, filename, line, section, section_line, |
147 | lvalue, ltype, rvalue, data, userdata); | |
d937fbbd | 148 | } |
f975e971 | 149 | |
46205bb6 | 150 | /* Warn about unknown non-extension fields. */ |
bcde742e | 151 | if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-")) |
28f30f40 ZJS |
152 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
153 | "Unknown key name '%s' in section '%s', ignoring.", lvalue, section); | |
46205bb6 | 154 | |
f1857be0 | 155 | return 0; |
ed5bcfbe LP |
156 | } |
157 | ||
8a37ce65 | 158 | /* Parse a single logical line */ |
bcde742e LP |
159 | static int parse_line( |
160 | const char* unit, | |
161 | const char *filename, | |
162 | unsigned line, | |
163 | const char *sections, | |
164 | ConfigItemLookup lookup, | |
165 | const void *table, | |
166 | ConfigParseFlags flags, | |
167 | char **section, | |
168 | unsigned *section_line, | |
169 | bool *section_ignored, | |
73a4ac8a | 170 | char *l, /* is modified */ |
bcde742e | 171 | void *userdata) { |
f975e971 | 172 | |
7ade8982 | 173 | char *e; |
ed5bcfbe | 174 | |
f975e971 LP |
175 | assert(filename); |
176 | assert(line > 0); | |
177 | assert(lookup); | |
178 | assert(l); | |
179 | ||
b2aa81ef | 180 | l = strstrip(l); |
73a4ac8a | 181 | if (isempty(l)) |
ed5bcfbe | 182 | return 0; |
1ea86b18 | 183 | |
73a4ac8a | 184 | if (l[0] == '\n') |
1ea86b18 | 185 | return 0; |
ed5bcfbe | 186 | |
78d17fa0 YW |
187 | if (!utf8_is_valid(l)) |
188 | return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l); | |
189 | ||
deec0b6d LP |
190 | if (l[0] == '[') { |
191 | _cleanup_free_ char *n = NULL; | |
ed5bcfbe | 192 | size_t k; |
ed5bcfbe | 193 | |
b2aa81ef | 194 | k = strlen(l); |
ed5bcfbe LP |
195 | assert(k > 0); |
196 | ||
3de39a1a YW |
197 | if (l[k-1] != ']') |
198 | return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EBADMSG), "Invalid section header '%s'", l); | |
ed5bcfbe | 199 | |
f975e971 LP |
200 | n = strndup(l+1, k-2); |
201 | if (!n) | |
7a602af0 | 202 | return log_oom(); |
ed5bcfbe | 203 | |
cec7f09d LP |
204 | if (!string_is_safe(n)) |
205 | return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EBADMSG), "Bad characters in section header '%s'", l); | |
206 | ||
f975e971 | 207 | if (sections && !nulstr_contains(sections, n)) { |
73a4ac8a | 208 | bool ignore; |
ddeb3f5d | 209 | const char *t; |
42f4e3c4 | 210 | |
73a4ac8a | 211 | ignore = (flags & CONFIG_PARSE_RELAXED) || startswith(n, "X-"); |
ddeb3f5d ZJS |
212 | |
213 | if (!ignore) | |
214 | NULSTR_FOREACH(t, sections) | |
73a4ac8a | 215 | if (streq_ptr(n, startswith(t, "-"))) { /* Ignore sections prefixed with "-" in valid section list */ |
ddeb3f5d ZJS |
216 | ignore = true; |
217 | break; | |
218 | } | |
219 | ||
220 | if (!ignore) | |
12ca818f | 221 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); |
f975e971 | 222 | |
a1e58e8e | 223 | *section = mfree(*section); |
71a61510 | 224 | *section_line = 0; |
342aea19 | 225 | *section_ignored = true; |
f975e971 | 226 | } else { |
97b9c506 | 227 | free_and_replace(*section, n); |
71a61510 | 228 | *section_line = line; |
342aea19 | 229 | *section_ignored = false; |
f975e971 | 230 | } |
ed5bcfbe LP |
231 | |
232 | return 0; | |
233 | } | |
234 | ||
62f168a0 | 235 | if (sections && !*section) { |
bcde742e | 236 | if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored) |
12ca818f | 237 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring."); |
62f168a0 | 238 | |
10e87ee7 | 239 | return 0; |
62f168a0 | 240 | } |
10e87ee7 | 241 | |
f975e971 | 242 | e = strchr(l, '='); |
2d4fffb0 ZJS |
243 | if (!e) |
244 | return log_syntax(unit, LOG_WARNING, filename, line, 0, | |
245 | "Missing '=', ignoring line."); | |
8be8ed8c ZJS |
246 | if (e == l) |
247 | return log_syntax(unit, LOG_WARNING, filename, line, 0, | |
248 | "Missing key name before '=', ignoring line."); | |
ed5bcfbe LP |
249 | |
250 | *e = 0; | |
251 | e++; | |
252 | ||
e8e581bf ZJS |
253 | return next_assignment(unit, |
254 | filename, | |
255 | line, | |
256 | lookup, | |
257 | table, | |
258 | *section, | |
71a61510 | 259 | *section_line, |
e8e581bf ZJS |
260 | strstrip(l), |
261 | strstrip(e), | |
bcde742e | 262 | flags, |
e8e581bf | 263 | userdata); |
ed5bcfbe LP |
264 | } |
265 | ||
266 | /* Go through the file and parse each line */ | |
c2f781bc YW |
267 | int config_parse( |
268 | const char *unit, | |
269 | const char *filename, | |
270 | FILE *f, | |
271 | const char *sections, | |
272 | ConfigItemLookup lookup, | |
273 | const void *table, | |
274 | ConfigParseFlags flags, | |
275 | void *userdata, | |
8524db50 | 276 | struct stat *ret_stat) { |
f975e971 | 277 | |
7fd1b19b HH |
278 | _cleanup_free_ char *section = NULL, *continuation = NULL; |
279 | _cleanup_fclose_ FILE *ours = NULL; | |
71a61510 | 280 | unsigned line = 0, section_line = 0; |
f9761a89 | 281 | bool section_ignored = false, bom_seen = false; |
8524db50 | 282 | struct stat st; |
14f594b9 | 283 | int r, fd; |
ed5bcfbe LP |
284 | |
285 | assert(filename); | |
f975e971 | 286 | assert(lookup); |
ed5bcfbe | 287 | |
87f0e418 | 288 | if (!f) { |
245802dd | 289 | f = ours = fopen(filename, "re"); |
f975e971 | 290 | if (!f) { |
36f822c4 ZJS |
291 | /* Only log on request, except for ENOENT, |
292 | * since we return 0 to the caller. */ | |
bcde742e | 293 | if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT) |
b8b846d7 LP |
294 | log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, |
295 | "Failed to open configuration file '%s': %m", filename); | |
8524db50 YW |
296 | |
297 | if (errno == ENOENT) { | |
298 | if (ret_stat) | |
299 | *ret_stat = (struct stat) {}; | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
304 | return -errno; | |
87f0e418 | 305 | } |
ed5bcfbe LP |
306 | } |
307 | ||
14f594b9 | 308 | fd = fileno(f); |
4f9ff96a | 309 | if (fd >= 0) { /* stream might not have an fd, let's be careful hence */ |
4f9ff96a LP |
310 | |
311 | if (fstat(fd, &st) < 0) | |
312 | return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_ERR : LOG_DEBUG, errno, | |
313 | "Failed to fstat(%s): %m", filename); | |
314 | ||
315 | (void) stat_warn_permissions(filename, &st); | |
9fd8d678 | 316 | } else |
8524db50 | 317 | st = (struct stat) {}; |
fdb9161c | 318 | |
9dd7ea9a | 319 | for (;;) { |
e6dde451 | 320 | _cleanup_free_ char *buf = NULL; |
3dab2943 | 321 | bool escaped = false; |
92b5e605 | 322 | char *l, *p, *e; |
ed5bcfbe | 323 | |
e6dde451 LP |
324 | r = read_line(f, LONG_LINE_MAX, &buf); |
325 | if (r == 0) | |
326 | break; | |
327 | if (r == -ENOBUFS) { | |
bcde742e | 328 | if (flags & CONFIG_PARSE_WARN) |
e6dde451 LP |
329 | log_error_errno(r, "%s:%u: Line too long", filename, line); |
330 | ||
331 | return r; | |
332 | } | |
333 | if (r < 0) { | |
5aca2e67 | 334 | if (FLAGS_SET(flags, CONFIG_PARSE_WARN)) |
e6dde451 | 335 | log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line); |
ed5bcfbe | 336 | |
e6dde451 | 337 | return r; |
ed5bcfbe LP |
338 | } |
339 | ||
68c1ac15 YW |
340 | line++; |
341 | ||
0ef69585 YW |
342 | l = skip_leading_chars(buf, WHITESPACE); |
343 | if (*l != '\0' && strchr(COMMENTS, *l)) | |
9adbfeb3 YW |
344 | continue; |
345 | ||
9dd7ea9a | 346 | l = buf; |
f9761a89 | 347 | if (!bom_seen) { |
e6dde451 | 348 | char *q; |
9dd7ea9a | 349 | |
e6dde451 LP |
350 | q = startswith(buf, UTF8_BYTE_ORDER_MARK); |
351 | if (q) { | |
352 | l = q; | |
f9761a89 | 353 | bom_seen = true; |
e6dde451 LP |
354 | } |
355 | } | |
3dab2943 LP |
356 | |
357 | if (continuation) { | |
e6dde451 | 358 | if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) { |
bcde742e | 359 | if (flags & CONFIG_PARSE_WARN) |
e6dde451 LP |
360 | log_error("%s:%u: Continuation line too long", filename, line); |
361 | return -ENOBUFS; | |
362 | } | |
363 | ||
c2bc710b | 364 | if (!strextend(&continuation, l)) { |
bcde742e | 365 | if (flags & CONFIG_PARSE_WARN) |
36f822c4 | 366 | log_oom(); |
245802dd | 367 | return -ENOMEM; |
36f822c4 | 368 | } |
3dab2943 | 369 | |
92b5e605 | 370 | p = continuation; |
3dab2943 LP |
371 | } else |
372 | p = l; | |
373 | ||
374 | for (e = p; *e; e++) { | |
375 | if (escaped) | |
376 | escaped = false; | |
377 | else if (*e == '\\') | |
378 | escaped = true; | |
379 | } | |
380 | ||
381 | if (escaped) { | |
382 | *(e-1) = ' '; | |
383 | ||
92b5e605 | 384 | if (!continuation) { |
f975e971 | 385 | continuation = strdup(l); |
36f822c4 | 386 | if (!continuation) { |
bcde742e | 387 | if (flags & CONFIG_PARSE_WARN) |
36f822c4 | 388 | log_oom(); |
245802dd | 389 | return -ENOMEM; |
36f822c4 | 390 | } |
3dab2943 LP |
391 | } |
392 | ||
393 | continue; | |
394 | } | |
395 | ||
e8e581bf ZJS |
396 | r = parse_line(unit, |
397 | filename, | |
68c1ac15 | 398 | line, |
e8e581bf ZJS |
399 | sections, |
400 | lookup, | |
401 | table, | |
bcde742e | 402 | flags, |
e8e581bf | 403 | §ion, |
71a61510 | 404 | §ion_line, |
342aea19 | 405 | §ion_ignored, |
e8e581bf ZJS |
406 | p, |
407 | userdata); | |
36f822c4 | 408 | if (r < 0) { |
bcde742e | 409 | if (flags & CONFIG_PARSE_WARN) |
e6dde451 | 410 | log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line); |
245802dd | 411 | return r; |
36f822c4 | 412 | } |
92b5e605 LP |
413 | |
414 | continuation = mfree(continuation); | |
ed5bcfbe LP |
415 | } |
416 | ||
4f29e0db FB |
417 | if (continuation) { |
418 | r = parse_line(unit, | |
419 | filename, | |
420 | ++line, | |
421 | sections, | |
422 | lookup, | |
423 | table, | |
424 | flags, | |
425 | §ion, | |
426 | §ion_line, | |
427 | §ion_ignored, | |
428 | continuation, | |
429 | userdata); | |
430 | if (r < 0) { | |
431 | if (flags & CONFIG_PARSE_WARN) | |
432 | log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line); | |
433 | return r; | |
4f29e0db FB |
434 | } |
435 | } | |
436 | ||
8524db50 YW |
437 | if (ret_stat) |
438 | *ret_stat = st; | |
4f9ff96a | 439 | |
8b8024f1 | 440 | return 1; |
ed5bcfbe LP |
441 | } |
442 | ||
8524db50 YW |
443 | static int hashmap_put_stats_by_path(Hashmap **stats_by_path, const char *path, const struct stat *st) { |
444 | _cleanup_free_ struct stat *st_copy = NULL; | |
445 | _cleanup_free_ char *path_copy = NULL; | |
446 | int r; | |
447 | ||
448 | assert(stats_by_path); | |
449 | assert(path); | |
450 | assert(st); | |
451 | ||
452 | r = hashmap_ensure_allocated(stats_by_path, &path_hash_ops_free_free); | |
453 | if (r < 0) | |
454 | return r; | |
455 | ||
456 | st_copy = newdup(struct stat, st, 1); | |
457 | if (!st_copy) | |
458 | return -ENOMEM; | |
459 | ||
460 | path_copy = strdup(path); | |
461 | if (!path) | |
462 | return -ENOMEM; | |
463 | ||
464 | r = hashmap_put(*stats_by_path, path_copy, st_copy); | |
465 | if (r < 0) | |
466 | return r; | |
467 | ||
468 | assert(r > 0); | |
469 | TAKE_PTR(path_copy); | |
470 | TAKE_PTR(st_copy); | |
471 | return 0; | |
472 | } | |
473 | ||
23bb31aa | 474 | static int config_parse_many_files( |
8b8024f1 | 475 | const char* const* conf_files, |
23bb31aa | 476 | char **files, |
43688c49 ZJS |
477 | const char *sections, |
478 | ConfigItemLookup lookup, | |
479 | const void *table, | |
bcde742e | 480 | ConfigParseFlags flags, |
4f9ff96a | 481 | void *userdata, |
8524db50 | 482 | Hashmap **ret_stats_by_path) { |
43688c49 | 483 | |
8524db50 YW |
484 | _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL; |
485 | struct stat st; | |
e8461023 JT |
486 | char **fn; |
487 | int r; | |
488 | ||
8524db50 YW |
489 | if (ret_stats_by_path) { |
490 | stats_by_path = hashmap_new(&path_hash_ops_free_free); | |
491 | if (!stats_by_path) | |
492 | return -ENOMEM; | |
493 | } | |
494 | ||
8b8024f1 ZJS |
495 | /* First read the first found main config file. */ |
496 | STRV_FOREACH(fn, (char**) conf_files) { | |
8524db50 | 497 | r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &st); |
e8461023 JT |
498 | if (r < 0) |
499 | return r; | |
8524db50 YW |
500 | if (r == 0) |
501 | continue; | |
502 | ||
503 | if (ret_stats_by_path) { | |
504 | r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st); | |
505 | if (r < 0) | |
506 | return r; | |
507 | } | |
508 | ||
509 | break; | |
e8461023 JT |
510 | } |
511 | ||
8b8024f1 | 512 | /* Then read all the drop-ins. */ |
e8461023 | 513 | STRV_FOREACH(fn, files) { |
8524db50 | 514 | r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &st); |
e8461023 JT |
515 | if (r < 0) |
516 | return r; | |
8524db50 YW |
517 | if (r == 0) |
518 | continue; | |
519 | ||
520 | if (ret_stats_by_path) { | |
521 | r = hashmap_put_stats_by_path(&stats_by_path, *fn, &st); | |
522 | if (r < 0) | |
523 | return r; | |
524 | } | |
e8461023 JT |
525 | } |
526 | ||
8524db50 YW |
527 | if (ret_stats_by_path) |
528 | *ret_stats_by_path = TAKE_PTR(stats_by_path); | |
4f9ff96a | 529 | |
e8461023 JT |
530 | return 0; |
531 | } | |
532 | ||
23bb31aa ZJS |
533 | /* Parse each config file in the directories specified as nulstr. */ |
534 | int config_parse_many_nulstr( | |
535 | const char *conf_file, | |
536 | const char *conf_file_dirs, | |
537 | const char *sections, | |
538 | ConfigItemLookup lookup, | |
539 | const void *table, | |
bcde742e | 540 | ConfigParseFlags flags, |
4f9ff96a | 541 | void *userdata, |
8524db50 | 542 | Hashmap **ret_stats_by_path) { |
23bb31aa ZJS |
543 | |
544 | _cleanup_strv_free_ char **files = NULL; | |
545 | int r; | |
546 | ||
b5084605 | 547 | r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs); |
23bb31aa ZJS |
548 | if (r < 0) |
549 | return r; | |
550 | ||
8b8024f1 ZJS |
551 | return config_parse_many_files(STRV_MAKE_CONST(conf_file), |
552 | files, sections, lookup, table, flags, userdata, | |
8524db50 | 553 | ret_stats_by_path); |
23bb31aa ZJS |
554 | } |
555 | ||
556 | /* Parse each config file in the directories specified as strv. */ | |
557 | int config_parse_many( | |
8b8024f1 | 558 | const char* const* conf_files, |
23bb31aa ZJS |
559 | const char* const* conf_file_dirs, |
560 | const char *dropin_dirname, | |
561 | const char *sections, | |
562 | ConfigItemLookup lookup, | |
563 | const void *table, | |
bcde742e | 564 | ConfigParseFlags flags, |
9f83091e | 565 | void *userdata, |
8524db50 | 566 | Hashmap **ret_stats_by_path) { |
23bb31aa ZJS |
567 | |
568 | _cleanup_strv_free_ char **dropin_dirs = NULL; | |
569 | _cleanup_strv_free_ char **files = NULL; | |
570 | const char *suffix; | |
571 | int r; | |
572 | ||
573 | suffix = strjoina("/", dropin_dirname); | |
574 | r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix); | |
575 | if (r < 0) | |
576 | return r; | |
577 | ||
b5084605 | 578 | r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs); |
23bb31aa ZJS |
579 | if (r < 0) |
580 | return r; | |
581 | ||
8524db50 | 582 | return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path); |
23bb31aa ZJS |
583 | } |
584 | ||
307fe3cd YW |
585 | static void config_section_hash_func(const ConfigSection *c, struct siphash *state) { |
586 | siphash24_compress_string(c->filename, state); | |
587 | siphash24_compress(&c->line, sizeof(c->line), state); | |
588 | } | |
589 | ||
590 | static int config_section_compare_func(const ConfigSection *x, const ConfigSection *y) { | |
591 | int r; | |
592 | ||
593 | r = strcmp(x->filename, y->filename); | |
594 | if (r != 0) | |
595 | return r; | |
596 | ||
597 | return CMP(x->line, y->line); | |
598 | } | |
599 | ||
600 | DEFINE_HASH_OPS(config_section_hash_ops, ConfigSection, config_section_hash_func, config_section_compare_func); | |
601 | ||
602 | int config_section_new(const char *filename, unsigned line, ConfigSection **s) { | |
603 | ConfigSection *cs; | |
604 | ||
605 | cs = malloc0(offsetof(ConfigSection, filename) + strlen(filename) + 1); | |
606 | if (!cs) | |
607 | return -ENOMEM; | |
608 | ||
609 | strcpy(cs->filename, filename); | |
610 | cs->line = line; | |
611 | ||
612 | *s = TAKE_PTR(cs); | |
613 | ||
614 | return 0; | |
615 | } | |
616 | ||
617 | unsigned hashmap_find_free_section_line(Hashmap *hashmap) { | |
618 | ConfigSection *cs; | |
619 | unsigned n = 0; | |
620 | void *entry; | |
621 | ||
622 | HASHMAP_FOREACH_KEY(entry, cs, hashmap) | |
623 | if (n < cs->line) | |
624 | n = cs->line; | |
625 | ||
626 | return n + 1; | |
627 | } | |
628 | ||
eb3491d9 | 629 | #define DEFINE_PARSER(type, vartype, conv_func) \ |
2d1729ca | 630 | DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype, "Failed to parse " #type " value") |
94d75d64 LP |
631 | |
632 | DEFINE_PARSER(int, int, safe_atoi); | |
633 | DEFINE_PARSER(long, long, safe_atoli); | |
134e24e1 | 634 | DEFINE_PARSER(uint8, uint8_t, safe_atou8); |
c7440e74 | 635 | DEFINE_PARSER(uint16, uint16_t, safe_atou16); |
94d75d64 | 636 | DEFINE_PARSER(uint32, uint32_t, safe_atou32); |
b57ebc60 | 637 | DEFINE_PARSER(int32, int32_t, safe_atoi32); |
94d75d64 LP |
638 | DEFINE_PARSER(uint64, uint64_t, safe_atou64); |
639 | DEFINE_PARSER(unsigned, unsigned, safe_atou); | |
640 | DEFINE_PARSER(double, double, safe_atod); | |
641 | DEFINE_PARSER(nsec, nsec_t, parse_nsec); | |
642 | DEFINE_PARSER(sec, usec_t, parse_sec); | |
7b61ce3c | 643 | DEFINE_PARSER(sec_def_infinity, usec_t, parse_sec_def_infinity); |
94d75d64 | 644 | DEFINE_PARSER(mode, mode_t, parse_mode); |
ed5bcfbe | 645 | |
c2f781bc YW |
646 | int config_parse_iec_size( |
647 | const char* unit, | |
648 | const char *filename, | |
649 | unsigned line, | |
650 | const char *section, | |
651 | unsigned section_line, | |
652 | const char *lvalue, | |
653 | int ltype, | |
654 | const char *rvalue, | |
655 | void *data, | |
656 | void *userdata) { | |
ed5bcfbe LP |
657 | |
658 | size_t *sz = data; | |
59f448cf | 659 | uint64_t v; |
e8e581bf | 660 | int r; |
ed5bcfbe LP |
661 | |
662 | assert(filename); | |
663 | assert(lvalue); | |
664 | assert(rvalue); | |
665 | assert(data); | |
666 | ||
59f448cf | 667 | r = parse_size(rvalue, 1024, &v); |
1e5f4e8b YW |
668 | if (r >= 0 && (uint64_t) (size_t) v != v) |
669 | r = -ERANGE; | |
670 | if (r < 0) { | |
d96edb2c | 671 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue); |
9ba1a159 LP |
672 | return 0; |
673 | } | |
674 | ||
59f448cf | 675 | *sz = (size_t) v; |
9ba1a159 LP |
676 | return 0; |
677 | } | |
678 | ||
50299121 | 679 | int config_parse_si_uint64( |
78f3c4bc LP |
680 | const char* unit, |
681 | const char *filename, | |
682 | unsigned line, | |
683 | const char *section, | |
684 | unsigned section_line, | |
685 | const char *lvalue, | |
686 | int ltype, | |
687 | const char *rvalue, | |
688 | void *data, | |
689 | void *userdata) { | |
5556b5fe | 690 | |
50299121 | 691 | uint64_t *sz = data; |
5556b5fe LP |
692 | int r; |
693 | ||
694 | assert(filename); | |
695 | assert(lvalue); | |
696 | assert(rvalue); | |
697 | assert(data); | |
698 | ||
50299121 | 699 | r = parse_size(rvalue, 1000, sz); |
d96edb2c YW |
700 | if (r < 0) |
701 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue); | |
5556b5fe | 702 | |
5556b5fe LP |
703 | return 0; |
704 | } | |
9ba1a159 | 705 | |
78f3c4bc LP |
706 | int config_parse_iec_uint64( |
707 | const char* unit, | |
708 | const char *filename, | |
709 | unsigned line, | |
710 | const char *section, | |
711 | unsigned section_line, | |
712 | const char *lvalue, | |
713 | int ltype, | |
714 | const char *rvalue, | |
715 | void *data, | |
716 | void *userdata) { | |
9ba1a159 | 717 | |
59f448cf | 718 | uint64_t *bytes = data; |
e8e581bf | 719 | int r; |
9ba1a159 LP |
720 | |
721 | assert(filename); | |
722 | assert(lvalue); | |
723 | assert(rvalue); | |
724 | assert(data); | |
725 | ||
5556b5fe | 726 | r = parse_size(rvalue, 1024, bytes); |
e8e581bf | 727 | if (r < 0) |
d96edb2c | 728 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue); |
ed5bcfbe | 729 | |
ed5bcfbe LP |
730 | return 0; |
731 | } | |
732 | ||
6e8791a0 TB |
733 | int config_parse_iec_uint64_infinity( |
734 | const char* unit, | |
735 | const char *filename, | |
736 | unsigned line, | |
737 | const char *section, | |
738 | unsigned section_line, | |
739 | const char *lvalue, | |
740 | int ltype, | |
741 | const char *rvalue, | |
742 | void *data, | |
743 | void *userdata) { | |
744 | ||
745 | uint64_t *bytes = data; | |
746 | ||
747 | assert(rvalue); | |
748 | assert(data); | |
749 | ||
750 | if (streq(rvalue, "infinity")) { | |
751 | *bytes = UINT64_MAX; | |
752 | return 0; | |
753 | } | |
754 | ||
755 | return config_parse_iec_uint64(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata); | |
756 | } | |
757 | ||
c2f781bc YW |
758 | int config_parse_bool( |
759 | const char* unit, | |
760 | const char *filename, | |
761 | unsigned line, | |
762 | const char *section, | |
763 | unsigned section_line, | |
764 | const char *lvalue, | |
765 | int ltype, | |
766 | const char *rvalue, | |
767 | void *data, | |
768 | void *userdata) { | |
ed5bcfbe LP |
769 | |
770 | int k; | |
771 | bool *b = data; | |
2c75fb73 | 772 | bool fatal = ltype; |
ed5bcfbe LP |
773 | |
774 | assert(filename); | |
775 | assert(lvalue); | |
776 | assert(rvalue); | |
777 | assert(data); | |
778 | ||
e8e581bf ZJS |
779 | k = parse_boolean(rvalue); |
780 | if (k < 0) { | |
d96edb2c | 781 | log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, k, |
2c75fb73 ZJS |
782 | "Failed to parse boolean value%s: %s", |
783 | fatal ? "" : ", ignoring", rvalue); | |
784 | return fatal ? -ENOEXEC : 0; | |
ed5bcfbe LP |
785 | } |
786 | ||
5d904a6a | 787 | *b = k; |
ed5bcfbe LP |
788 | return 0; |
789 | } | |
790 | ||
12963533 TH |
791 | int config_parse_id128( |
792 | const char *unit, | |
793 | const char *filename, | |
794 | unsigned line, | |
795 | const char *section, | |
796 | unsigned section_line, | |
797 | const char *lvalue, | |
798 | int ltype, | |
799 | const char *rvalue, | |
800 | void *data, | |
801 | void *userdata) { | |
802 | ||
803 | sd_id128_t t, *result = data; | |
804 | int r; | |
805 | ||
806 | assert(filename); | |
807 | assert(lvalue); | |
808 | assert(rvalue); | |
809 | ||
810 | r = sd_id128_from_string(rvalue, &t); | |
d96edb2c YW |
811 | if (r < 0) { |
812 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue); | |
813 | return 0; | |
814 | } | |
815 | ||
816 | if (sd_id128_is_null(t)) { | |
817 | log_syntax(unit, LOG_WARNING, filename, line, 0, "128bit ID/UUID is all 0, ignoring: %s", rvalue); | |
818 | return 0; | |
819 | } | |
12963533 TH |
820 | |
821 | *result = t; | |
822 | return 0; | |
823 | } | |
824 | ||
f757855e LP |
825 | int config_parse_tristate( |
826 | const char* unit, | |
827 | const char *filename, | |
828 | unsigned line, | |
829 | const char *section, | |
830 | unsigned section_line, | |
831 | const char *lvalue, | |
832 | int ltype, | |
833 | const char *rvalue, | |
834 | void *data, | |
835 | void *userdata) { | |
836 | ||
837 | int k, *t = data; | |
838 | ||
839 | assert(filename); | |
840 | assert(lvalue); | |
841 | assert(rvalue); | |
842 | assert(data); | |
843 | ||
33f2de7b YW |
844 | /* A tristate is pretty much a boolean, except that it can also take an empty string, |
845 | * indicating "uninitialized", much like NULL is for a pointer type. */ | |
846 | ||
847 | if (isempty(rvalue)) { | |
848 | *t = -1; | |
849 | return 0; | |
850 | } | |
f757855e LP |
851 | |
852 | k = parse_boolean(rvalue); | |
853 | if (k < 0) { | |
33f2de7b YW |
854 | log_syntax(unit, LOG_WARNING, filename, line, k, |
855 | "Failed to parse boolean value for %s=, ignoring: %s", lvalue, rvalue); | |
f757855e LP |
856 | return 0; |
857 | } | |
858 | ||
fec5929f | 859 | *t = k; |
f757855e LP |
860 | return 0; |
861 | } | |
862 | ||
8f2665a4 LP |
863 | int config_parse_string( |
864 | const char *unit, | |
865 | const char *filename, | |
866 | unsigned line, | |
867 | const char *section, | |
868 | unsigned section_line, | |
869 | const char *lvalue, | |
870 | int ltype, | |
871 | const char *rvalue, | |
872 | void *data, | |
873 | void *userdata) { | |
874 | ||
5a4ff988 | 875 | char **s = data; |
ed5bcfbe LP |
876 | |
877 | assert(filename); | |
878 | assert(lvalue); | |
879 | assert(rvalue); | |
880 | assert(data); | |
881 | ||
b3f9c17a | 882 | return free_and_strdup_warn(s, empty_to_null(rvalue)); |
ed5bcfbe | 883 | } |
57d42a5f | 884 | |
2d17d699 LP |
885 | int config_parse_safe_string( |
886 | const char *unit, | |
887 | const char *filename, | |
888 | unsigned line, | |
889 | const char *section, | |
890 | unsigned section_line, | |
891 | const char *lvalue, | |
892 | int ltype, | |
893 | const char *rvalue, | |
894 | void *data, | |
895 | void *userdata) { | |
896 | ||
897 | char **s = data; | |
898 | ||
899 | assert(filename); | |
900 | assert(lvalue); | |
901 | assert(rvalue); | |
902 | assert(data); | |
903 | ||
904 | if (!string_is_safe(rvalue)) { | |
905 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Specified string contains unsafe characters, ignoring: %s", rvalue); | |
906 | return 0; | |
907 | } | |
908 | ||
909 | return free_and_strdup_warn(s, empty_to_null(rvalue)); | |
910 | } | |
911 | ||
8f2665a4 LP |
912 | int config_parse_path( |
913 | const char *unit, | |
914 | const char *filename, | |
915 | unsigned line, | |
916 | const char *section, | |
917 | unsigned section_line, | |
918 | const char *lvalue, | |
919 | int ltype, | |
920 | const char *rvalue, | |
921 | void *data, | |
922 | void *userdata) { | |
034c6ed7 | 923 | |
c0d72c43 | 924 | _cleanup_free_ char *n = NULL; |
2c75fb73 | 925 | bool fatal = ltype; |
c0d72c43 | 926 | char **s = data; |
cd4f53c5 | 927 | int r; |
034c6ed7 LP |
928 | |
929 | assert(filename); | |
930 | assert(lvalue); | |
931 | assert(rvalue); | |
932 | assert(data); | |
933 | ||
8e7b5bd0 | 934 | if (isempty(rvalue)) |
0fe50629 | 935 | goto finalize; |
0fe50629 | 936 | |
7f110ff9 LP |
937 | n = strdup(rvalue); |
938 | if (!n) | |
74051b9b | 939 | return log_oom(); |
034c6ed7 | 940 | |
cd4f53c5 YW |
941 | r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue); |
942 | if (r < 0) | |
943 | return fatal ? -ENOEXEC : 0; | |
01f78473 | 944 | |
0fe50629 | 945 | finalize: |
8e7b5bd0 | 946 | return free_and_replace(*s, n); |
034c6ed7 | 947 | } |
57d42a5f | 948 | |
8249bb72 LP |
949 | int config_parse_strv( |
950 | const char *unit, | |
951 | const char *filename, | |
952 | unsigned line, | |
953 | const char *section, | |
954 | unsigned section_line, | |
955 | const char *lvalue, | |
956 | int ltype, | |
957 | const char *rvalue, | |
958 | void *data, | |
959 | void *userdata) { | |
57d42a5f | 960 | |
a2a5291b | 961 | char ***sv = data; |
00d0fd06 | 962 | int r; |
57d42a5f LP |
963 | |
964 | assert(filename); | |
965 | assert(lvalue); | |
966 | assert(rvalue); | |
967 | assert(data); | |
968 | ||
74051b9b | 969 | if (isempty(rvalue)) { |
8249bb72 | 970 | *sv = strv_free(*sv); |
853b8397 | 971 | return 0; |
74051b9b LP |
972 | } |
973 | ||
d96edb2c | 974 | for (const char *p = rvalue;;) { |
34f253f0 | 975 | char *word = NULL; |
00d0fd06 | 976 | |
d96edb2c | 977 | r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); |
34f253f0 | 978 | if (r == 0) |
d96edb2c | 979 | return 0; |
34f253f0 | 980 | if (r == -ENOMEM) |
853b8397 | 981 | return log_oom(); |
34f253f0 | 982 | if (r < 0) { |
d96edb2c YW |
983 | log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); |
984 | return 0; | |
34f253f0 | 985 | } |
853b8397 | 986 | |
34f253f0 | 987 | r = strv_consume(sv, word); |
853b8397 LP |
988 | if (r < 0) |
989 | return log_oom(); | |
7f110ff9 | 990 | } |
57d42a5f | 991 | } |
15ae422b | 992 | |
1e35c5ab RP |
993 | int config_parse_warn_compat( |
994 | const char *unit, | |
995 | const char *filename, | |
996 | unsigned line, | |
997 | const char *section, | |
998 | unsigned section_line, | |
999 | const char *lvalue, | |
1000 | int ltype, | |
1001 | const char *rvalue, | |
1002 | void *data, | |
1003 | void *userdata) { | |
7ef7e15b | 1004 | |
1e35c5ab RP |
1005 | Disabled reason = ltype; |
1006 | ||
1007 | switch(reason) { | |
7ef7e15b | 1008 | |
1e35c5ab RP |
1009 | case DISABLED_CONFIGURATION: |
1010 | log_syntax(unit, LOG_DEBUG, filename, line, 0, | |
1011 | "Support for option %s= has been disabled at compile time and it is ignored", lvalue); | |
1012 | break; | |
7ef7e15b | 1013 | |
1e35c5ab RP |
1014 | case DISABLED_LEGACY: |
1015 | log_syntax(unit, LOG_INFO, filename, line, 0, | |
1016 | "Support for option %s= has been removed and it is ignored", lvalue); | |
1017 | break; | |
7ef7e15b | 1018 | |
1e35c5ab RP |
1019 | case DISABLED_EXPERIMENTAL: |
1020 | log_syntax(unit, LOG_INFO, filename, line, 0, | |
1021 | "Support for option %s= has not yet been enabled and it is ignored", lvalue); | |
1022 | break; | |
7ef7e15b | 1023 | } |
1e35c5ab RP |
1024 | |
1025 | return 0; | |
1026 | } | |
1027 | ||
ca37242e LP |
1028 | int config_parse_log_facility( |
1029 | const char *unit, | |
1030 | const char *filename, | |
1031 | unsigned line, | |
1032 | const char *section, | |
1033 | unsigned section_line, | |
1034 | const char *lvalue, | |
1035 | int ltype, | |
1036 | const char *rvalue, | |
1037 | void *data, | |
1038 | void *userdata) { | |
213ba152 | 1039 | |
213ba152 LP |
1040 | int *o = data, x; |
1041 | ||
1042 | assert(filename); | |
1043 | assert(lvalue); | |
1044 | assert(rvalue); | |
1045 | assert(data); | |
1046 | ||
1047 | x = log_facility_unshifted_from_string(rvalue); | |
1048 | if (x < 0) { | |
b98680b2 | 1049 | log_syntax(unit, LOG_WARNING, filename, line, x, "Failed to parse log facility, ignoring: %s", rvalue); |
213ba152 LP |
1050 | return 0; |
1051 | } | |
1052 | ||
1053 | *o = (x << 3) | LOG_PRI(*o); | |
1054 | ||
1055 | return 0; | |
1056 | } | |
1057 | ||
ca37242e LP |
1058 | int config_parse_log_level( |
1059 | const char *unit, | |
1060 | const char *filename, | |
1061 | unsigned line, | |
1062 | const char *section, | |
1063 | unsigned section_line, | |
1064 | const char *lvalue, | |
1065 | int ltype, | |
1066 | const char *rvalue, | |
1067 | void *data, | |
1068 | void *userdata) { | |
213ba152 | 1069 | |
213ba152 LP |
1070 | int *o = data, x; |
1071 | ||
1072 | assert(filename); | |
1073 | assert(lvalue); | |
1074 | assert(rvalue); | |
1075 | assert(data); | |
1076 | ||
1077 | x = log_level_from_string(rvalue); | |
1078 | if (x < 0) { | |
b98680b2 | 1079 | log_syntax(unit, LOG_WARNING, filename, line, x, "Failed to parse log level, ignoring: %s", rvalue); |
213ba152 LP |
1080 | return 0; |
1081 | } | |
1082 | ||
d3070fbd LP |
1083 | if (*o < 0) /* if it wasn't initialized so far, assume zero facility */ |
1084 | *o = x; | |
1085 | else | |
1086 | *o = (*o & LOG_FACMASK) | x; | |
1087 | ||
213ba152 LP |
1088 | return 0; |
1089 | } | |
f757855e LP |
1090 | |
1091 | int config_parse_signal( | |
1092 | const char *unit, | |
1093 | const char *filename, | |
1094 | unsigned line, | |
1095 | const char *section, | |
1096 | unsigned section_line, | |
1097 | const char *lvalue, | |
1098 | int ltype, | |
1099 | const char *rvalue, | |
1100 | void *data, | |
1101 | void *userdata) { | |
1102 | ||
1103 | int *sig = data, r; | |
1104 | ||
1105 | assert(filename); | |
1106 | assert(lvalue); | |
1107 | assert(rvalue); | |
1108 | assert(sig); | |
1109 | ||
29a3db75 | 1110 | r = signal_from_string(rvalue); |
f757855e | 1111 | if (r <= 0) { |
b98680b2 | 1112 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse signal name, ignoring: %s", rvalue); |
f757855e LP |
1113 | return 0; |
1114 | } | |
1115 | ||
1116 | *sig = r; | |
1117 | return 0; | |
1118 | } | |
1119 | ||
1120 | int config_parse_personality( | |
1121 | const char *unit, | |
1122 | const char *filename, | |
1123 | unsigned line, | |
1124 | const char *section, | |
1125 | unsigned section_line, | |
1126 | const char *lvalue, | |
1127 | int ltype, | |
1128 | const char *rvalue, | |
1129 | void *data, | |
1130 | void *userdata) { | |
1131 | ||
1132 | unsigned long *personality = data, p; | |
1133 | ||
1134 | assert(filename); | |
1135 | assert(lvalue); | |
1136 | assert(rvalue); | |
1137 | assert(personality); | |
1138 | ||
40fdd636 LP |
1139 | if (isempty(rvalue)) |
1140 | p = PERSONALITY_INVALID; | |
1141 | else { | |
1142 | p = personality_from_string(rvalue); | |
1143 | if (p == PERSONALITY_INVALID) { | |
d96edb2c | 1144 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue); |
40fdd636 LP |
1145 | return 0; |
1146 | } | |
f757855e LP |
1147 | } |
1148 | ||
1149 | *personality = p; | |
1150 | return 0; | |
1151 | } | |
d31645ad LP |
1152 | |
1153 | int config_parse_ifname( | |
1154 | const char *unit, | |
1155 | const char *filename, | |
1156 | unsigned line, | |
1157 | const char *section, | |
1158 | unsigned section_line, | |
1159 | const char *lvalue, | |
1160 | int ltype, | |
1161 | const char *rvalue, | |
1162 | void *data, | |
1163 | void *userdata) { | |
1164 | ||
1165 | char **s = data; | |
1166 | int r; | |
1167 | ||
1168 | assert(filename); | |
1169 | assert(lvalue); | |
1170 | assert(rvalue); | |
1171 | assert(data); | |
1172 | ||
1173 | if (isempty(rvalue)) { | |
1174 | *s = mfree(*s); | |
1175 | return 0; | |
1176 | } | |
1177 | ||
1178 | if (!ifname_valid(rvalue)) { | |
d96edb2c | 1179 | log_syntax(unit, LOG_WARNING, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue); |
d31645ad LP |
1180 | return 0; |
1181 | } | |
1182 | ||
1183 | r = free_and_strdup(s, rvalue); | |
1184 | if (r < 0) | |
1185 | return log_oom(); | |
1186 | ||
1187 | return 0; | |
1188 | } | |
177d0b20 | 1189 | |
a5053a15 YW |
1190 | int config_parse_ifnames( |
1191 | const char *unit, | |
1192 | const char *filename, | |
1193 | unsigned line, | |
1194 | const char *section, | |
1195 | unsigned section_line, | |
1196 | const char *lvalue, | |
1197 | int ltype, | |
1198 | const char *rvalue, | |
1199 | void *data, | |
1200 | void *userdata) { | |
1201 | ||
1202 | _cleanup_strv_free_ char **names = NULL; | |
1203 | char ***s = data; | |
a5053a15 YW |
1204 | int r; |
1205 | ||
1206 | assert(filename); | |
1207 | assert(lvalue); | |
1208 | assert(rvalue); | |
1209 | assert(data); | |
1210 | ||
1211 | if (isempty(rvalue)) { | |
1212 | *s = strv_free(*s); | |
1213 | return 0; | |
1214 | } | |
1215 | ||
d96edb2c | 1216 | for (const char *p = rvalue;;) { |
a5053a15 YW |
1217 | _cleanup_free_ char *word = NULL; |
1218 | ||
1219 | r = extract_first_word(&p, &word, NULL, 0); | |
d96edb2c YW |
1220 | if (r == -ENOMEM) |
1221 | return log_oom(); | |
a5053a15 | 1222 | if (r < 0) { |
d96edb2c | 1223 | log_syntax(unit, LOG_WARNING, filename, line, r, |
a5053a15 YW |
1224 | "Failed to extract interface name, ignoring assignment: %s", |
1225 | rvalue); | |
1226 | return 0; | |
1227 | } | |
1228 | if (r == 0) | |
1229 | break; | |
1230 | ||
1231 | if (!ifname_valid_full(word, ltype)) { | |
d96edb2c | 1232 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
a5053a15 YW |
1233 | "Interface name is not valid or too long, ignoring assignment: %s", |
1234 | word); | |
1235 | continue; | |
1236 | } | |
1237 | ||
1238 | r = strv_consume(&names, TAKE_PTR(word)); | |
1239 | if (r < 0) | |
1240 | return log_oom(); | |
1241 | } | |
1242 | ||
1243 | r = strv_extend_strv(s, names, true); | |
1244 | if (r < 0) | |
1245 | return log_oom(); | |
1246 | ||
1247 | return 0; | |
1248 | } | |
1249 | ||
177d0b20 SS |
1250 | int config_parse_ip_port( |
1251 | const char *unit, | |
1252 | const char *filename, | |
1253 | unsigned line, | |
1254 | const char *section, | |
1255 | unsigned section_line, | |
1256 | const char *lvalue, | |
1257 | int ltype, | |
1258 | const char *rvalue, | |
1259 | void *data, | |
1260 | void *userdata) { | |
1261 | ||
1262 | uint16_t *s = data; | |
1263 | uint16_t port; | |
1264 | int r; | |
1265 | ||
1266 | assert(filename); | |
1267 | assert(lvalue); | |
1268 | assert(rvalue); | |
1269 | assert(data); | |
1270 | ||
1271 | if (isempty(rvalue)) { | |
1272 | *s = 0; | |
1273 | return 0; | |
1274 | } | |
1275 | ||
1276 | r = parse_ip_port(rvalue, &port); | |
1277 | if (r < 0) { | |
d96edb2c | 1278 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse port '%s'.", rvalue); |
177d0b20 SS |
1279 | return 0; |
1280 | } | |
1281 | ||
1282 | *s = port; | |
1283 | ||
1284 | return 0; | |
1285 | } | |
9ecdba8c | 1286 | |
79138a38 LP |
1287 | int config_parse_mtu( |
1288 | const char *unit, | |
1289 | const char *filename, | |
1290 | unsigned line, | |
1291 | const char *section, | |
1292 | unsigned section_line, | |
1293 | const char *lvalue, | |
1294 | int ltype, | |
1295 | const char *rvalue, | |
1296 | void *data, | |
1297 | void *userdata) { | |
1298 | ||
1299 | uint32_t *mtu = data; | |
1300 | int r; | |
1301 | ||
1302 | assert(rvalue); | |
1303 | assert(mtu); | |
1304 | ||
1305 | r = parse_mtu(ltype, rvalue, mtu); | |
1306 | if (r == -ERANGE) { | |
d96edb2c | 1307 | log_syntax(unit, LOG_WARNING, filename, line, r, |
79138a38 LP |
1308 | "Maximum transfer unit (MTU) value out of range. Permitted range is %" PRIu32 "…%" PRIu32 ", ignoring: %s", |
1309 | (uint32_t) (ltype == AF_INET6 ? IPV6_MIN_MTU : IPV4_MIN_MTU), (uint32_t) UINT32_MAX, | |
1310 | rvalue); | |
1311 | return 0; | |
1312 | } | |
1313 | if (r < 0) { | |
d96edb2c | 1314 | log_syntax(unit, LOG_WARNING, filename, line, r, |
79138a38 LP |
1315 | "Failed to parse MTU value '%s', ignoring: %m", rvalue); |
1316 | return 0; | |
1317 | } | |
1318 | ||
1319 | return 0; | |
1320 | } | |
4f424df7 LP |
1321 | |
1322 | int config_parse_rlimit( | |
1323 | const char *unit, | |
1324 | const char *filename, | |
1325 | unsigned line, | |
1326 | const char *section, | |
1327 | unsigned section_line, | |
1328 | const char *lvalue, | |
1329 | int ltype, | |
1330 | const char *rvalue, | |
1331 | void *data, | |
1332 | void *userdata) { | |
1333 | ||
1334 | struct rlimit **rl = data, d = {}; | |
1335 | int r; | |
1336 | ||
1337 | assert(rvalue); | |
1338 | assert(rl); | |
1339 | ||
1340 | r = rlimit_parse(ltype, rvalue, &d); | |
1341 | if (r == -EILSEQ) { | |
1342 | log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue); | |
1343 | return 0; | |
1344 | } | |
1345 | if (r < 0) { | |
d96edb2c | 1346 | log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue); |
4f424df7 LP |
1347 | return 0; |
1348 | } | |
1349 | ||
1350 | if (rl[ltype]) | |
1351 | *rl[ltype] = d; | |
1352 | else { | |
1353 | rl[ltype] = newdup(struct rlimit, &d, 1); | |
1354 | if (!rl[ltype]) | |
1355 | return log_oom(); | |
1356 | } | |
1357 | ||
1358 | return 0; | |
1359 | } | |
c07b23ca | 1360 | |
c2f781bc YW |
1361 | int config_parse_permille( |
1362 | const char* unit, | |
1363 | const char *filename, | |
1364 | unsigned line, | |
1365 | const char *section, | |
1366 | unsigned section_line, | |
1367 | const char *lvalue, | |
1368 | int ltype, | |
1369 | const char *rvalue, | |
1370 | void *data, | |
1371 | void *userdata) { | |
c07b23ca MKB |
1372 | |
1373 | unsigned *permille = data; | |
1374 | int r; | |
1375 | ||
1376 | assert(filename); | |
1377 | assert(lvalue); | |
1378 | assert(rvalue); | |
1379 | assert(permille); | |
1380 | ||
1381 | r = parse_permille(rvalue); | |
1382 | if (r < 0) { | |
d96edb2c | 1383 | log_syntax(unit, LOG_WARNING, filename, line, r, |
c07b23ca MKB |
1384 | "Failed to parse permille value, ignoring: %s", rvalue); |
1385 | return 0; | |
1386 | } | |
1387 | ||
1388 | *permille = (unsigned) r; | |
1389 | ||
1390 | return 0; | |
1391 | } | |
4df4df5b | 1392 | |
c2f781bc YW |
1393 | int config_parse_vlanprotocol( |
1394 | const char* unit, | |
1395 | const char *filename, | |
1396 | unsigned line, | |
1397 | const char *section, | |
1398 | unsigned section_line, | |
1399 | const char *lvalue, | |
1400 | int ltype, | |
1401 | const char *rvalue, | |
1402 | void *data, | |
1403 | void *userdata) { | |
1404 | ||
4df4df5b | 1405 | int *vlan_protocol = data; |
c2f781bc | 1406 | |
4df4df5b RF |
1407 | assert(filename); |
1408 | assert(lvalue); | |
1409 | ||
1410 | if (isempty(rvalue)) { | |
1411 | *vlan_protocol = -1; | |
1412 | return 0; | |
1413 | } | |
1414 | ||
1415 | if (STR_IN_SET(rvalue, "802.1ad", "802.1AD")) | |
1416 | *vlan_protocol = ETH_P_8021AD; | |
1417 | else if (STR_IN_SET(rvalue, "802.1q", "802.1Q")) | |
1418 | *vlan_protocol = ETH_P_8021Q; | |
1419 | else { | |
d96edb2c | 1420 | log_syntax(unit, LOG_WARNING, filename, line, 0, |
4df4df5b RF |
1421 | "Failed to parse VLAN protocol value, ignoring: %s", rvalue); |
1422 | return 0; | |
1423 | } | |
1424 | ||
1425 | return 0; | |
1426 | } | |
9de5e321 | 1427 | |
99628f36 YW |
1428 | int config_parse_hw_addr( |
1429 | const char *unit, | |
1430 | const char *filename, | |
1431 | unsigned line, | |
1432 | const char *section, | |
1433 | unsigned section_line, | |
1434 | const char *lvalue, | |
1435 | int ltype, | |
1436 | const char *rvalue, | |
1437 | void *data, | |
1438 | void *userdata) { | |
1439 | ||
1440 | struct hw_addr_data a, *hwaddr = data; | |
1441 | int r; | |
1442 | ||
1443 | assert(filename); | |
1444 | assert(lvalue); | |
1445 | assert(rvalue); | |
1446 | assert(data); | |
1447 | ||
1448 | if (isempty(rvalue)) { | |
1449 | *hwaddr = HW_ADDR_NULL; | |
1450 | return 0; | |
1451 | } | |
1452 | ||
1453 | r = parse_hw_addr_full(rvalue, ltype, &a); | |
1454 | if (r < 0) { | |
1455 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1456 | "Not a valid hardware address, ignoring assignment: %s", rvalue); | |
1457 | return 0; | |
1458 | } | |
1459 | ||
1460 | *hwaddr = a; | |
1461 | return 0; | |
1462 | } | |
1463 | ||
1464 | int config_parse_hw_addrs( | |
1465 | const char *unit, | |
1466 | const char *filename, | |
1467 | unsigned line, | |
1468 | const char *section, | |
1469 | unsigned section_line, | |
1470 | const char *lvalue, | |
1471 | int ltype, | |
1472 | const char *rvalue, | |
1473 | void *data, | |
1474 | void *userdata) { | |
1475 | ||
1476 | Set **hwaddrs = data; | |
1477 | int r; | |
1478 | ||
1479 | assert(filename); | |
1480 | assert(lvalue); | |
1481 | assert(rvalue); | |
1482 | assert(data); | |
1483 | ||
1484 | if (isempty(rvalue)) { | |
1485 | /* Empty assignment resets the list */ | |
1486 | *hwaddrs = set_free(*hwaddrs); | |
1487 | return 0; | |
1488 | } | |
1489 | ||
1490 | for (const char *p = rvalue;;) { | |
1491 | _cleanup_free_ char *word = NULL; | |
1492 | _cleanup_free_ struct hw_addr_data *n = NULL; | |
1493 | ||
1494 | r = extract_first_word(&p, &word, NULL, 0); | |
1495 | if (r == 0) | |
1496 | return 0; | |
1497 | if (r == -ENOMEM) | |
1498 | return log_oom(); | |
1499 | if (r < 0) { | |
1500 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1501 | "Invalid syntax, ignoring: %s", rvalue); | |
1502 | return 0; | |
1503 | } | |
1504 | ||
1505 | n = new(struct hw_addr_data, 1); | |
1506 | if (!n) | |
1507 | return log_oom(); | |
1508 | ||
1509 | r = parse_hw_addr_full(word, ltype, n); | |
1510 | if (r < 0) { | |
1511 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1512 | "Not a valid hardware address, ignoring: %s", word); | |
1513 | continue; | |
1514 | } | |
1515 | ||
1516 | r = set_ensure_consume(hwaddrs, &hw_addr_hash_ops_free, TAKE_PTR(n)); | |
1517 | if (r < 0) | |
1518 | return log_oom(); | |
1519 | } | |
1520 | } | |
1521 | ||
aa4f7653 | 1522 | int config_parse_ether_addr( |
0ec2a7a1 YW |
1523 | const char *unit, |
1524 | const char *filename, | |
1525 | unsigned line, | |
1526 | const char *section, | |
1527 | unsigned section_line, | |
1528 | const char *lvalue, | |
1529 | int ltype, | |
1530 | const char *rvalue, | |
1531 | void *data, | |
1532 | void *userdata) { | |
1533 | ||
1534 | _cleanup_free_ struct ether_addr *n = NULL; | |
1535 | struct ether_addr **hwaddr = data; | |
1536 | int r; | |
1537 | ||
1538 | assert(filename); | |
1539 | assert(lvalue); | |
1540 | assert(rvalue); | |
1541 | assert(data); | |
1542 | ||
1543 | if (isempty(rvalue)) { | |
1544 | *hwaddr = mfree(*hwaddr); | |
1545 | return 0; | |
1546 | } | |
1547 | ||
1548 | n = new0(struct ether_addr, 1); | |
1549 | if (!n) | |
1550 | return log_oom(); | |
1551 | ||
227e9ce2 | 1552 | r = parse_ether_addr(rvalue, n); |
0ec2a7a1 YW |
1553 | if (r < 0) { |
1554 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1555 | "Not a valid MAC address, ignoring assignment: %s", rvalue); | |
1556 | return 0; | |
1557 | } | |
1558 | ||
1559 | free_and_replace(*hwaddr, n); | |
1560 | ||
1561 | return 0; | |
1562 | } | |
1563 | ||
aa4f7653 | 1564 | int config_parse_ether_addrs( |
0ec2a7a1 YW |
1565 | const char *unit, |
1566 | const char *filename, | |
1567 | unsigned line, | |
1568 | const char *section, | |
1569 | unsigned section_line, | |
1570 | const char *lvalue, | |
1571 | int ltype, | |
1572 | const char *rvalue, | |
1573 | void *data, | |
1574 | void *userdata) { | |
1575 | ||
1576 | Set **hwaddrs = data; | |
1577 | int r; | |
1578 | ||
1579 | assert(filename); | |
1580 | assert(lvalue); | |
1581 | assert(rvalue); | |
1582 | assert(data); | |
1583 | ||
1584 | if (isempty(rvalue)) { | |
1585 | /* Empty assignment resets the list */ | |
c6df73ca | 1586 | *hwaddrs = set_free(*hwaddrs); |
0ec2a7a1 YW |
1587 | return 0; |
1588 | } | |
1589 | ||
1590 | for (const char *p = rvalue;;) { | |
1591 | _cleanup_free_ char *word = NULL; | |
1592 | _cleanup_free_ struct ether_addr *n = NULL; | |
1593 | ||
1594 | r = extract_first_word(&p, &word, NULL, 0); | |
1595 | if (r == 0) | |
1596 | return 0; | |
1597 | if (r == -ENOMEM) | |
1598 | return log_oom(); | |
1599 | if (r < 0) { | |
1600 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1601 | "Invalid syntax, ignoring: %s", rvalue); | |
1602 | return 0; | |
1603 | } | |
1604 | ||
1605 | n = new(struct ether_addr, 1); | |
1606 | if (!n) | |
1607 | return log_oom(); | |
1608 | ||
227e9ce2 | 1609 | r = parse_ether_addr(word, n); |
0ec2a7a1 YW |
1610 | if (r < 0) { |
1611 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1612 | "Not a valid MAC address, ignoring: %s", word); | |
1613 | continue; | |
1614 | } | |
1615 | ||
c6df73ca | 1616 | r = set_ensure_consume(hwaddrs, ðer_addr_hash_ops_free, TAKE_PTR(n)); |
0ec2a7a1 YW |
1617 | if (r < 0) |
1618 | return log_oom(); | |
0ec2a7a1 YW |
1619 | } |
1620 | } | |
1621 | ||
cf074772 YW |
1622 | int config_parse_in_addr_non_null( |
1623 | const char *unit, | |
1624 | const char *filename, | |
1625 | unsigned line, | |
1626 | const char *section, | |
1627 | unsigned section_line, | |
1628 | const char *lvalue, | |
1629 | int ltype, | |
1630 | const char *rvalue, | |
1631 | void *data, | |
1632 | void *userdata) { | |
1633 | ||
1634 | /* data must be a pointer to struct in_addr or in6_addr, and the type is determined by ltype. */ | |
1635 | struct in_addr *ipv4 = data; | |
1636 | struct in6_addr *ipv6 = data; | |
1637 | union in_addr_union a; | |
1638 | int r; | |
1639 | ||
1640 | assert(filename); | |
1641 | assert(lvalue); | |
1642 | assert(rvalue); | |
1643 | assert(data); | |
1644 | assert(IN_SET(ltype, AF_INET, AF_INET6)); | |
1645 | ||
1646 | if (isempty(rvalue)) { | |
1647 | if (ltype == AF_INET) | |
1648 | *ipv4 = (struct in_addr) {}; | |
1649 | else | |
1650 | *ipv6 = (struct in6_addr) {}; | |
1651 | return 0; | |
1652 | } | |
1653 | ||
1654 | r = in_addr_from_string(ltype, rvalue, &a); | |
1655 | if (r < 0) { | |
1656 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
1657 | "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); | |
1658 | return 0; | |
1659 | } | |
1660 | ||
1661 | if (!in_addr_is_set(ltype, &a)) { | |
1662 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
1663 | "%s= cannot be the ANY address, ignoring: %s", lvalue, rvalue); | |
1664 | return 0; | |
1665 | } | |
1666 | ||
1667 | if (ltype == AF_INET) | |
1668 | *ipv4 = a.in; | |
1669 | else | |
1670 | *ipv6 = a.in6; | |
1671 | return 0; | |
1672 | } | |
1673 | ||
9de5e321 | 1674 | DEFINE_CONFIG_PARSE(config_parse_percent, parse_percent, "Failed to parse percent value"); |
0a9f9344 | 1675 | DEFINE_CONFIG_PARSE(config_parse_permyriad, parse_permyriad, "Failed to parse permyriad value"); |