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