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