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