]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/conf-parser.c
main: let's use physical_memory_scale() where appropriate
[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"
0ec2a7a1 14#include "ether-addr-util.h"
a8fbdf54 15#include "extract-word.h"
6bedfcbb 16#include "fd-util.h"
e6dde451 17#include "fileio.h"
f4f15635 18#include "fs-util.h"
16354eff 19#include "log.h"
07630cea 20#include "macro.h"
f5947a5e 21#include "missing_network.h"
d8b4d14d 22#include "nulstr-util.h"
6bedfcbb 23#include "parse-util.h"
9eb977db 24#include "path-util.h"
7b3e062c 25#include "process-util.h"
ef118d00 26#include "rlimit-util.h"
12963533 27#include "sd-id128.h"
0ec2a7a1 28#include "set.h"
f757855e 29#include "signal-util.h"
d31645ad 30#include "socket-util.h"
07630cea
LP
31#include "string-util.h"
32#include "strv.h"
7b3e062c 33#include "syslog-util.h"
a8fbdf54 34#include "time-util.h"
07630cea 35#include "utf8.h"
e8e581bf 36
f975e971 37int config_item_table_lookup(
e9f3d2d5 38 const void *table,
ed5bcfbe 39 const char *section,
ed5bcfbe 40 const char *lvalue,
f975e971
LP
41 ConfigParserCallback *func,
42 int *ltype,
43 void **data,
ed5bcfbe
LP
44 void *userdata) {
45
e9f3d2d5 46 const ConfigTableItem *t;
f975e971
LP
47
48 assert(table);
ed5bcfbe 49 assert(lvalue);
f975e971
LP
50 assert(func);
51 assert(ltype);
52 assert(data);
ed5bcfbe 53
f975e971 54 for (t = table; t->lvalue; t++) {
ed5bcfbe 55
f975e971 56 if (!streq(lvalue, t->lvalue))
ed5bcfbe
LP
57 continue;
58
f975e971 59 if (!streq_ptr(section, t->section))
ed5bcfbe
LP
60 continue;
61
f975e971
LP
62 *func = t->parse;
63 *ltype = t->ltype;
64 *data = t->data;
65 return 1;
66 }
ed5bcfbe 67
f975e971
LP
68 return 0;
69}
10e87ee7 70
f975e971 71int config_item_perf_lookup(
e9f3d2d5 72 const void *table,
f975e971
LP
73 const char *section,
74 const char *lvalue,
75 ConfigParserCallback *func,
76 int *ltype,
77 void **data,
78 void *userdata) {
79
80 ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
81 const ConfigPerfItem *p;
82
83 assert(table);
84 assert(lvalue);
85 assert(func);
86 assert(ltype);
87 assert(data);
88
4472fa6d
LP
89 if (section) {
90 const char *key;
f975e971 91
4472fa6d 92 key = strjoina(section, ".", lvalue);
f975e971 93 p = lookup(key, strlen(key));
4472fa6d
LP
94 } else
95 p = lookup(lvalue, strlen(lvalue));
f975e971
LP
96 if (!p)
97 return 0;
98
99 *func = p->parse;
100 *ltype = p->ltype;
101 *data = (uint8_t*) userdata + p->offset;
102 return 1;
103}
104
105/* Run the user supplied parser for an assignment */
bcde742e
LP
106static int next_assignment(
107 const char *unit,
108 const char *filename,
109 unsigned line,
110 ConfigItemLookup lookup,
111 const void *table,
112 const char *section,
113 unsigned section_line,
114 const char *lvalue,
115 const char *rvalue,
116 ConfigParseFlags flags,
117 void *userdata) {
f975e971
LP
118
119 ConfigParserCallback func = NULL;
120 int ltype = 0;
121 void *data = NULL;
122 int r;
123
124 assert(filename);
125 assert(line > 0);
126 assert(lookup);
127 assert(lvalue);
128 assert(rvalue);
129
130 r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
131 if (r < 0)
132 return r;
d937fbbd
LP
133 if (r > 0) {
134 if (func)
71a61510
TG
135 return func(unit, filename, line, section, section_line,
136 lvalue, ltype, rvalue, data, userdata);
d937fbbd
LP
137
138 return 0;
139 }
f975e971 140
46205bb6 141 /* Warn about unknown non-extension fields. */
bcde742e 142 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
28f30f40
ZJS
143 log_syntax(unit, LOG_WARNING, filename, line, 0,
144 "Unknown key name '%s' in section '%s', ignoring.", lvalue, section);
46205bb6 145
f1857be0 146 return 0;
ed5bcfbe
LP
147}
148
8a37ce65 149/* Parse a single logical line */
bcde742e
LP
150static int parse_line(
151 const char* unit,
152 const char *filename,
153 unsigned line,
154 const char *sections,
155 ConfigItemLookup lookup,
156 const void *table,
157 ConfigParseFlags flags,
158 char **section,
159 unsigned *section_line,
160 bool *section_ignored,
161 char *l,
162 void *userdata) {
f975e971 163
7ade8982 164 char *e;
ed5bcfbe 165
f975e971
LP
166 assert(filename);
167 assert(line > 0);
168 assert(lookup);
169 assert(l);
170
b2aa81ef 171 l = strstrip(l);
b2aa81ef 172 if (!*l)
ed5bcfbe 173 return 0;
1ea86b18 174
9adbfeb3 175 if (*l == '\n')
1ea86b18 176 return 0;
ed5bcfbe 177
78d17fa0
YW
178 if (!utf8_is_valid(l))
179 return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l);
180
b2aa81ef 181 if (*l == '[') {
ed5bcfbe
LP
182 size_t k;
183 char *n;
184
b2aa81ef 185 k = strlen(l);
ed5bcfbe
LP
186 assert(k > 0);
187
3de39a1a
YW
188 if (l[k-1] != ']')
189 return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EBADMSG), "Invalid section header '%s'", l);
ed5bcfbe 190
f975e971
LP
191 n = strndup(l+1, k-2);
192 if (!n)
7a602af0 193 return log_oom();
ed5bcfbe 194
f975e971 195 if (sections && !nulstr_contains(sections, n)) {
ddeb3f5d
ZJS
196 bool ignore = flags & CONFIG_PARSE_RELAXED;
197 const char *t;
42f4e3c4 198
ddeb3f5d
ZJS
199 ignore = ignore || startswith(n, "X-");
200
201 if (!ignore)
202 NULSTR_FOREACH(t, sections)
203 if (streq_ptr(n, startswith(t, "-"))) {
204 ignore = true;
205 break;
206 }
207
208 if (!ignore)
12ca818f 209 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
f975e971
LP
210
211 free(n);
a1e58e8e 212 *section = mfree(*section);
71a61510 213 *section_line = 0;
342aea19 214 *section_ignored = true;
f975e971 215 } else {
97b9c506 216 free_and_replace(*section, n);
71a61510 217 *section_line = line;
342aea19 218 *section_ignored = false;
f975e971 219 }
ed5bcfbe
LP
220
221 return 0;
222 }
223
62f168a0 224 if (sections && !*section) {
bcde742e 225 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
12ca818f 226 log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
62f168a0 227
10e87ee7 228 return 0;
62f168a0 229 }
10e87ee7 230
f975e971 231 e = strchr(l, '=');
2d4fffb0
ZJS
232 if (!e)
233 return log_syntax(unit, LOG_WARNING, filename, line, 0,
234 "Missing '=', ignoring line.");
8be8ed8c
ZJS
235 if (e == l)
236 return log_syntax(unit, LOG_WARNING, filename, line, 0,
237 "Missing key name before '=', ignoring line.");
ed5bcfbe
LP
238
239 *e = 0;
240 e++;
241
e8e581bf
ZJS
242 return next_assignment(unit,
243 filename,
244 line,
245 lookup,
246 table,
247 *section,
71a61510 248 *section_line,
e8e581bf
ZJS
249 strstrip(l),
250 strstrip(e),
bcde742e 251 flags,
e8e581bf 252 userdata);
ed5bcfbe
LP
253}
254
255/* Go through the file and parse each line */
c2f781bc
YW
256int config_parse(
257 const char *unit,
258 const char *filename,
259 FILE *f,
260 const char *sections,
261 ConfigItemLookup lookup,
262 const void *table,
263 ConfigParseFlags flags,
264 void *userdata,
265 usec_t *ret_mtime) {
f975e971 266
7fd1b19b
HH
267 _cleanup_free_ char *section = NULL, *continuation = NULL;
268 _cleanup_fclose_ FILE *ours = NULL;
71a61510 269 unsigned line = 0, section_line = 0;
f9761a89 270 bool section_ignored = false, bom_seen = false;
14f594b9 271 int r, fd;
4f9ff96a 272 usec_t mtime;
ed5bcfbe
LP
273
274 assert(filename);
f975e971 275 assert(lookup);
ed5bcfbe 276
87f0e418 277 if (!f) {
245802dd 278 f = ours = fopen(filename, "re");
f975e971 279 if (!f) {
36f822c4
ZJS
280 /* Only log on request, except for ENOENT,
281 * since we return 0 to the caller. */
bcde742e 282 if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
b8b846d7
LP
283 log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
284 "Failed to open configuration file '%s': %m", filename);
9f43a07f 285 return errno == ENOENT ? 0 : -errno;
87f0e418 286 }
ed5bcfbe
LP
287 }
288
14f594b9 289 fd = fileno(f);
4f9ff96a
LP
290 if (fd >= 0) { /* stream might not have an fd, let's be careful hence */
291 struct stat st;
292
293 if (fstat(fd, &st) < 0)
294 return log_full_errno(FLAGS_SET(flags, CONFIG_PARSE_WARN) ? LOG_ERR : LOG_DEBUG, errno,
295 "Failed to fstat(%s): %m", filename);
296
297 (void) stat_warn_permissions(filename, &st);
298 mtime = timespec_load(&st.st_mtim);
299 }
fdb9161c 300
9dd7ea9a 301 for (;;) {
e6dde451 302 _cleanup_free_ char *buf = NULL;
3dab2943 303 bool escaped = false;
92b5e605 304 char *l, *p, *e;
ed5bcfbe 305
e6dde451
LP
306 r = read_line(f, LONG_LINE_MAX, &buf);
307 if (r == 0)
308 break;
309 if (r == -ENOBUFS) {
bcde742e 310 if (flags & CONFIG_PARSE_WARN)
e6dde451
LP
311 log_error_errno(r, "%s:%u: Line too long", filename, line);
312
313 return r;
314 }
315 if (r < 0) {
5aca2e67 316 if (FLAGS_SET(flags, CONFIG_PARSE_WARN))
e6dde451 317 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
ed5bcfbe 318
e6dde451 319 return r;
ed5bcfbe
LP
320 }
321
68c1ac15
YW
322 line++;
323
0ef69585
YW
324 l = skip_leading_chars(buf, WHITESPACE);
325 if (*l != '\0' && strchr(COMMENTS, *l))
9adbfeb3
YW
326 continue;
327
9dd7ea9a 328 l = buf;
f9761a89 329 if (!bom_seen) {
e6dde451 330 char *q;
9dd7ea9a 331
e6dde451
LP
332 q = startswith(buf, UTF8_BYTE_ORDER_MARK);
333 if (q) {
334 l = q;
f9761a89 335 bom_seen = true;
e6dde451
LP
336 }
337 }
3dab2943
LP
338
339 if (continuation) {
e6dde451 340 if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
bcde742e 341 if (flags & CONFIG_PARSE_WARN)
e6dde451
LP
342 log_error("%s:%u: Continuation line too long", filename, line);
343 return -ENOBUFS;
344 }
345
c2bc710b 346 if (!strextend(&continuation, l)) {
bcde742e 347 if (flags & CONFIG_PARSE_WARN)
36f822c4 348 log_oom();
245802dd 349 return -ENOMEM;
36f822c4 350 }
3dab2943 351
92b5e605 352 p = continuation;
3dab2943
LP
353 } else
354 p = l;
355
356 for (e = p; *e; e++) {
357 if (escaped)
358 escaped = false;
359 else if (*e == '\\')
360 escaped = true;
361 }
362
363 if (escaped) {
364 *(e-1) = ' ';
365
92b5e605 366 if (!continuation) {
f975e971 367 continuation = strdup(l);
36f822c4 368 if (!continuation) {
bcde742e 369 if (flags & CONFIG_PARSE_WARN)
36f822c4 370 log_oom();
245802dd 371 return -ENOMEM;
36f822c4 372 }
3dab2943
LP
373 }
374
375 continue;
376 }
377
e8e581bf
ZJS
378 r = parse_line(unit,
379 filename,
68c1ac15 380 line,
e8e581bf
ZJS
381 sections,
382 lookup,
383 table,
bcde742e 384 flags,
e8e581bf 385 &section,
71a61510 386 &section_line,
342aea19 387 &section_ignored,
e8e581bf
ZJS
388 p,
389 userdata);
36f822c4 390 if (r < 0) {
bcde742e 391 if (flags & CONFIG_PARSE_WARN)
e6dde451 392 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
245802dd 393 return r;
36f822c4 394 }
92b5e605
LP
395
396 continuation = mfree(continuation);
ed5bcfbe
LP
397 }
398
4f29e0db
FB
399 if (continuation) {
400 r = parse_line(unit,
401 filename,
402 ++line,
403 sections,
404 lookup,
405 table,
406 flags,
407 &section,
408 &section_line,
409 &section_ignored,
410 continuation,
411 userdata);
412 if (r < 0) {
413 if (flags & CONFIG_PARSE_WARN)
414 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
415 return r;
4f29e0db
FB
416 }
417 }
418
4f9ff96a
LP
419 if (ret_mtime)
420 *ret_mtime = mtime;
421
245802dd 422 return 0;
ed5bcfbe
LP
423}
424
23bb31aa 425static int config_parse_many_files(
43688c49 426 const char *conf_file,
23bb31aa 427 char **files,
43688c49
ZJS
428 const char *sections,
429 ConfigItemLookup lookup,
430 const void *table,
bcde742e 431 ConfigParseFlags flags,
4f9ff96a
LP
432 void *userdata,
433 usec_t *ret_mtime) {
43688c49 434
4f9ff96a 435 usec_t mtime = 0;
e8461023
JT
436 char **fn;
437 int r;
438
e8461023 439 if (conf_file) {
4f9ff96a 440 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata, &mtime);
e8461023
JT
441 if (r < 0)
442 return r;
443 }
444
445 STRV_FOREACH(fn, files) {
4f9ff96a
LP
446 usec_t t;
447
448 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata, &t);
e8461023
JT
449 if (r < 0)
450 return r;
4f9ff96a
LP
451 if (t > mtime) /* Find the newest */
452 mtime = t;
e8461023
JT
453 }
454
4f9ff96a
LP
455 if (ret_mtime)
456 *ret_mtime = mtime;
457
e8461023
JT
458 return 0;
459}
460
23bb31aa
ZJS
461/* Parse each config file in the directories specified as nulstr. */
462int config_parse_many_nulstr(
463 const char *conf_file,
464 const char *conf_file_dirs,
465 const char *sections,
466 ConfigItemLookup lookup,
467 const void *table,
bcde742e 468 ConfigParseFlags flags,
4f9ff96a
LP
469 void *userdata,
470 usec_t *ret_mtime) {
23bb31aa
ZJS
471
472 _cleanup_strv_free_ char **files = NULL;
473 int r;
474
b5084605 475 r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
23bb31aa
ZJS
476 if (r < 0)
477 return r;
478
4f9ff96a 479 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata, ret_mtime);
23bb31aa
ZJS
480}
481
482/* Parse each config file in the directories specified as strv. */
483int config_parse_many(
484 const char *conf_file,
485 const char* const* conf_file_dirs,
486 const char *dropin_dirname,
487 const char *sections,
488 ConfigItemLookup lookup,
489 const void *table,
bcde742e 490 ConfigParseFlags flags,
9f83091e 491 void *userdata,
4f9ff96a 492 usec_t *ret_mtime) {
23bb31aa
ZJS
493
494 _cleanup_strv_free_ char **dropin_dirs = NULL;
495 _cleanup_strv_free_ char **files = NULL;
496 const char *suffix;
497 int r;
498
499 suffix = strjoina("/", dropin_dirname);
500 r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
501 if (r < 0)
502 return r;
503
b5084605 504 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
23bb31aa
ZJS
505 if (r < 0)
506 return r;
507
4f9ff96a 508 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata, ret_mtime);
23bb31aa
ZJS
509}
510
eb3491d9 511#define DEFINE_PARSER(type, vartype, conv_func) \
2d1729ca 512 DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype, "Failed to parse " #type " value")
94d75d64
LP
513
514DEFINE_PARSER(int, int, safe_atoi);
515DEFINE_PARSER(long, long, safe_atoli);
134e24e1 516DEFINE_PARSER(uint8, uint8_t, safe_atou8);
c7440e74 517DEFINE_PARSER(uint16, uint16_t, safe_atou16);
94d75d64 518DEFINE_PARSER(uint32, uint32_t, safe_atou32);
b57ebc60 519DEFINE_PARSER(int32, int32_t, safe_atoi32);
94d75d64
LP
520DEFINE_PARSER(uint64, uint64_t, safe_atou64);
521DEFINE_PARSER(unsigned, unsigned, safe_atou);
522DEFINE_PARSER(double, double, safe_atod);
523DEFINE_PARSER(nsec, nsec_t, parse_nsec);
524DEFINE_PARSER(sec, usec_t, parse_sec);
7b61ce3c 525DEFINE_PARSER(sec_def_infinity, usec_t, parse_sec_def_infinity);
94d75d64 526DEFINE_PARSER(mode, mode_t, parse_mode);
ed5bcfbe 527
c2f781bc
YW
528int config_parse_iec_size(
529 const char* unit,
530 const char *filename,
531 unsigned line,
532 const char *section,
533 unsigned section_line,
534 const char *lvalue,
535 int ltype,
536 const char *rvalue,
537 void *data,
538 void *userdata) {
ed5bcfbe
LP
539
540 size_t *sz = data;
59f448cf 541 uint64_t v;
e8e581bf 542 int r;
ed5bcfbe
LP
543
544 assert(filename);
545 assert(lvalue);
546 assert(rvalue);
547 assert(data);
548
59f448cf 549 r = parse_size(rvalue, 1024, &v);
1e5f4e8b
YW
550 if (r >= 0 && (uint64_t) (size_t) v != v)
551 r = -ERANGE;
552 if (r < 0) {
d96edb2c 553 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
9ba1a159
LP
554 return 0;
555 }
556
59f448cf 557 *sz = (size_t) v;
9ba1a159
LP
558 return 0;
559}
560
50299121 561int config_parse_si_uint64(
78f3c4bc
LP
562 const char* unit,
563 const char *filename,
564 unsigned line,
565 const char *section,
566 unsigned section_line,
567 const char *lvalue,
568 int ltype,
569 const char *rvalue,
570 void *data,
571 void *userdata) {
5556b5fe 572
50299121 573 uint64_t *sz = data;
5556b5fe
LP
574 int r;
575
576 assert(filename);
577 assert(lvalue);
578 assert(rvalue);
579 assert(data);
580
50299121 581 r = parse_size(rvalue, 1000, sz);
d96edb2c
YW
582 if (r < 0)
583 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value '%s', ignoring: %m", rvalue);
5556b5fe 584
5556b5fe
LP
585 return 0;
586}
9ba1a159 587
78f3c4bc
LP
588int config_parse_iec_uint64(
589 const char* unit,
590 const char *filename,
591 unsigned line,
592 const char *section,
593 unsigned section_line,
594 const char *lvalue,
595 int ltype,
596 const char *rvalue,
597 void *data,
598 void *userdata) {
9ba1a159 599
59f448cf 600 uint64_t *bytes = data;
e8e581bf 601 int r;
9ba1a159
LP
602
603 assert(filename);
604 assert(lvalue);
605 assert(rvalue);
606 assert(data);
607
5556b5fe 608 r = parse_size(rvalue, 1024, bytes);
e8e581bf 609 if (r < 0)
d96edb2c 610 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
ed5bcfbe 611
ed5bcfbe
LP
612 return 0;
613}
614
c2f781bc
YW
615int config_parse_bool(
616 const char* unit,
617 const char *filename,
618 unsigned line,
619 const char *section,
620 unsigned section_line,
621 const char *lvalue,
622 int ltype,
623 const char *rvalue,
624 void *data,
625 void *userdata) {
ed5bcfbe
LP
626
627 int k;
628 bool *b = data;
2c75fb73 629 bool fatal = ltype;
ed5bcfbe
LP
630
631 assert(filename);
632 assert(lvalue);
633 assert(rvalue);
634 assert(data);
635
e8e581bf
ZJS
636 k = parse_boolean(rvalue);
637 if (k < 0) {
d96edb2c 638 log_syntax(unit, fatal ? LOG_ERR : LOG_WARNING, filename, line, k,
2c75fb73
ZJS
639 "Failed to parse boolean value%s: %s",
640 fatal ? "" : ", ignoring", rvalue);
641 return fatal ? -ENOEXEC : 0;
ed5bcfbe
LP
642 }
643
5d904a6a 644 *b = k;
ed5bcfbe
LP
645 return 0;
646}
647
12963533
TH
648int config_parse_id128(
649 const char *unit,
650 const char *filename,
651 unsigned line,
652 const char *section,
653 unsigned section_line,
654 const char *lvalue,
655 int ltype,
656 const char *rvalue,
657 void *data,
658 void *userdata) {
659
660 sd_id128_t t, *result = data;
661 int r;
662
663 assert(filename);
664 assert(lvalue);
665 assert(rvalue);
666
667 r = sd_id128_from_string(rvalue, &t);
d96edb2c
YW
668 if (r < 0) {
669 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse 128bit ID/UUID, ignoring: %s", rvalue);
670 return 0;
671 }
672
673 if (sd_id128_is_null(t)) {
674 log_syntax(unit, LOG_WARNING, filename, line, 0, "128bit ID/UUID is all 0, ignoring: %s", rvalue);
675 return 0;
676 }
12963533
TH
677
678 *result = t;
679 return 0;
680}
681
f757855e
LP
682int config_parse_tristate(
683 const char* unit,
684 const char *filename,
685 unsigned line,
686 const char *section,
687 unsigned section_line,
688 const char *lvalue,
689 int ltype,
690 const char *rvalue,
691 void *data,
692 void *userdata) {
693
694 int k, *t = data;
695
696 assert(filename);
697 assert(lvalue);
698 assert(rvalue);
699 assert(data);
700
701 /* A tristate is pretty much a boolean, except that it can
702 * also take the special value -1, indicating "uninitialized",
703 * much like NULL is for a pointer type. */
704
705 k = parse_boolean(rvalue);
706 if (k < 0) {
d96edb2c 707 log_syntax(unit, LOG_WARNING, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
f757855e
LP
708 return 0;
709 }
710
fec5929f 711 *t = k;
f757855e
LP
712 return 0;
713}
714
8f2665a4
LP
715int config_parse_string(
716 const char *unit,
717 const char *filename,
718 unsigned line,
719 const char *section,
720 unsigned section_line,
721 const char *lvalue,
722 int ltype,
723 const char *rvalue,
724 void *data,
725 void *userdata) {
726
5a4ff988 727 char **s = data;
ed5bcfbe
LP
728
729 assert(filename);
730 assert(lvalue);
731 assert(rvalue);
732 assert(data);
733
b3f9c17a 734 return free_and_strdup_warn(s, empty_to_null(rvalue));
ed5bcfbe 735}
57d42a5f 736
8f2665a4
LP
737int config_parse_path(
738 const char *unit,
739 const char *filename,
740 unsigned line,
741 const char *section,
742 unsigned section_line,
743 const char *lvalue,
744 int ltype,
745 const char *rvalue,
746 void *data,
747 void *userdata) {
034c6ed7 748
c0d72c43 749 _cleanup_free_ char *n = NULL;
2c75fb73 750 bool fatal = ltype;
c0d72c43 751 char **s = data;
cd4f53c5 752 int r;
034c6ed7
LP
753
754 assert(filename);
755 assert(lvalue);
756 assert(rvalue);
757 assert(data);
758
8e7b5bd0 759 if (isempty(rvalue))
0fe50629 760 goto finalize;
0fe50629 761
7f110ff9
LP
762 n = strdup(rvalue);
763 if (!n)
74051b9b 764 return log_oom();
034c6ed7 765
cd4f53c5
YW
766 r = path_simplify_and_warn(n, PATH_CHECK_ABSOLUTE | (fatal ? PATH_CHECK_FATAL : 0), unit, filename, line, lvalue);
767 if (r < 0)
768 return fatal ? -ENOEXEC : 0;
01f78473 769
0fe50629 770finalize:
8e7b5bd0 771 return free_and_replace(*s, n);
034c6ed7 772}
57d42a5f 773
8249bb72
LP
774int config_parse_strv(
775 const char *unit,
776 const char *filename,
777 unsigned line,
778 const char *section,
779 unsigned section_line,
780 const char *lvalue,
781 int ltype,
782 const char *rvalue,
783 void *data,
784 void *userdata) {
57d42a5f 785
a2a5291b 786 char ***sv = data;
00d0fd06 787 int r;
57d42a5f
LP
788
789 assert(filename);
790 assert(lvalue);
791 assert(rvalue);
792 assert(data);
793
74051b9b 794 if (isempty(rvalue)) {
8249bb72 795 *sv = strv_free(*sv);
853b8397 796 return 0;
74051b9b
LP
797 }
798
d96edb2c 799 for (const char *p = rvalue;;) {
34f253f0 800 char *word = NULL;
00d0fd06 801
d96edb2c 802 r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
34f253f0 803 if (r == 0)
d96edb2c 804 return 0;
34f253f0 805 if (r == -ENOMEM)
853b8397 806 return log_oom();
34f253f0 807 if (r < 0) {
d96edb2c
YW
808 log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
809 return 0;
34f253f0 810 }
853b8397 811
34f253f0 812 r = strv_consume(sv, word);
853b8397
LP
813 if (r < 0)
814 return log_oom();
7f110ff9 815 }
57d42a5f 816}
15ae422b 817
1e35c5ab
RP
818int config_parse_warn_compat(
819 const char *unit,
820 const char *filename,
821 unsigned line,
822 const char *section,
823 unsigned section_line,
824 const char *lvalue,
825 int ltype,
826 const char *rvalue,
827 void *data,
828 void *userdata) {
7ef7e15b 829
1e35c5ab
RP
830 Disabled reason = ltype;
831
832 switch(reason) {
7ef7e15b 833
1e35c5ab
RP
834 case DISABLED_CONFIGURATION:
835 log_syntax(unit, LOG_DEBUG, filename, line, 0,
836 "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
837 break;
7ef7e15b 838
1e35c5ab
RP
839 case DISABLED_LEGACY:
840 log_syntax(unit, LOG_INFO, filename, line, 0,
841 "Support for option %s= has been removed and it is ignored", lvalue);
842 break;
7ef7e15b 843
1e35c5ab
RP
844 case DISABLED_EXPERIMENTAL:
845 log_syntax(unit, LOG_INFO, filename, line, 0,
846 "Support for option %s= has not yet been enabled and it is ignored", lvalue);
847 break;
7ef7e15b 848 }
1e35c5ab
RP
849
850 return 0;
851}
852
ca37242e
LP
853int config_parse_log_facility(
854 const char *unit,
855 const char *filename,
856 unsigned line,
857 const char *section,
858 unsigned section_line,
859 const char *lvalue,
860 int ltype,
861 const char *rvalue,
862 void *data,
863 void *userdata) {
213ba152 864
213ba152
LP
865 int *o = data, x;
866
867 assert(filename);
868 assert(lvalue);
869 assert(rvalue);
870 assert(data);
871
872 x = log_facility_unshifted_from_string(rvalue);
873 if (x < 0) {
b98680b2 874 log_syntax(unit, LOG_WARNING, filename, line, x, "Failed to parse log facility, ignoring: %s", rvalue);
213ba152
LP
875 return 0;
876 }
877
878 *o = (x << 3) | LOG_PRI(*o);
879
880 return 0;
881}
882
ca37242e
LP
883int config_parse_log_level(
884 const char *unit,
885 const char *filename,
886 unsigned line,
887 const char *section,
888 unsigned section_line,
889 const char *lvalue,
890 int ltype,
891 const char *rvalue,
892 void *data,
893 void *userdata) {
213ba152 894
213ba152
LP
895 int *o = data, x;
896
897 assert(filename);
898 assert(lvalue);
899 assert(rvalue);
900 assert(data);
901
902 x = log_level_from_string(rvalue);
903 if (x < 0) {
b98680b2 904 log_syntax(unit, LOG_WARNING, filename, line, x, "Failed to parse log level, ignoring: %s", rvalue);
213ba152
LP
905 return 0;
906 }
907
d3070fbd
LP
908 if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
909 *o = x;
910 else
911 *o = (*o & LOG_FACMASK) | x;
912
213ba152
LP
913 return 0;
914}
f757855e
LP
915
916int config_parse_signal(
917 const char *unit,
918 const char *filename,
919 unsigned line,
920 const char *section,
921 unsigned section_line,
922 const char *lvalue,
923 int ltype,
924 const char *rvalue,
925 void *data,
926 void *userdata) {
927
928 int *sig = data, r;
929
930 assert(filename);
931 assert(lvalue);
932 assert(rvalue);
933 assert(sig);
934
29a3db75 935 r = signal_from_string(rvalue);
f757855e 936 if (r <= 0) {
b98680b2 937 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse signal name, ignoring: %s", rvalue);
f757855e
LP
938 return 0;
939 }
940
941 *sig = r;
942 return 0;
943}
944
945int config_parse_personality(
946 const char *unit,
947 const char *filename,
948 unsigned line,
949 const char *section,
950 unsigned section_line,
951 const char *lvalue,
952 int ltype,
953 const char *rvalue,
954 void *data,
955 void *userdata) {
956
957 unsigned long *personality = data, p;
958
959 assert(filename);
960 assert(lvalue);
961 assert(rvalue);
962 assert(personality);
963
40fdd636
LP
964 if (isempty(rvalue))
965 p = PERSONALITY_INVALID;
966 else {
967 p = personality_from_string(rvalue);
968 if (p == PERSONALITY_INVALID) {
d96edb2c 969 log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
40fdd636
LP
970 return 0;
971 }
f757855e
LP
972 }
973
974 *personality = p;
975 return 0;
976}
d31645ad
LP
977
978int config_parse_ifname(
979 const char *unit,
980 const char *filename,
981 unsigned line,
982 const char *section,
983 unsigned section_line,
984 const char *lvalue,
985 int ltype,
986 const char *rvalue,
987 void *data,
988 void *userdata) {
989
990 char **s = data;
991 int r;
992
993 assert(filename);
994 assert(lvalue);
995 assert(rvalue);
996 assert(data);
997
998 if (isempty(rvalue)) {
999 *s = mfree(*s);
1000 return 0;
1001 }
1002
1003 if (!ifname_valid(rvalue)) {
d96edb2c 1004 log_syntax(unit, LOG_WARNING, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
d31645ad
LP
1005 return 0;
1006 }
1007
1008 r = free_and_strdup(s, rvalue);
1009 if (r < 0)
1010 return log_oom();
1011
1012 return 0;
1013}
177d0b20 1014
a5053a15
YW
1015int config_parse_ifnames(
1016 const char *unit,
1017 const char *filename,
1018 unsigned line,
1019 const char *section,
1020 unsigned section_line,
1021 const char *lvalue,
1022 int ltype,
1023 const char *rvalue,
1024 void *data,
1025 void *userdata) {
1026
1027 _cleanup_strv_free_ char **names = NULL;
1028 char ***s = data;
a5053a15
YW
1029 int r;
1030
1031 assert(filename);
1032 assert(lvalue);
1033 assert(rvalue);
1034 assert(data);
1035
1036 if (isempty(rvalue)) {
1037 *s = strv_free(*s);
1038 return 0;
1039 }
1040
d96edb2c 1041 for (const char *p = rvalue;;) {
a5053a15
YW
1042 _cleanup_free_ char *word = NULL;
1043
1044 r = extract_first_word(&p, &word, NULL, 0);
d96edb2c
YW
1045 if (r == -ENOMEM)
1046 return log_oom();
a5053a15 1047 if (r < 0) {
d96edb2c 1048 log_syntax(unit, LOG_WARNING, filename, line, r,
a5053a15
YW
1049 "Failed to extract interface name, ignoring assignment: %s",
1050 rvalue);
1051 return 0;
1052 }
1053 if (r == 0)
1054 break;
1055
1056 if (!ifname_valid_full(word, ltype)) {
d96edb2c 1057 log_syntax(unit, LOG_WARNING, filename, line, 0,
a5053a15
YW
1058 "Interface name is not valid or too long, ignoring assignment: %s",
1059 word);
1060 continue;
1061 }
1062
1063 r = strv_consume(&names, TAKE_PTR(word));
1064 if (r < 0)
1065 return log_oom();
1066 }
1067
1068 r = strv_extend_strv(s, names, true);
1069 if (r < 0)
1070 return log_oom();
1071
1072 return 0;
1073}
1074
177d0b20
SS
1075int config_parse_ip_port(
1076 const char *unit,
1077 const char *filename,
1078 unsigned line,
1079 const char *section,
1080 unsigned section_line,
1081 const char *lvalue,
1082 int ltype,
1083 const char *rvalue,
1084 void *data,
1085 void *userdata) {
1086
1087 uint16_t *s = data;
1088 uint16_t port;
1089 int r;
1090
1091 assert(filename);
1092 assert(lvalue);
1093 assert(rvalue);
1094 assert(data);
1095
1096 if (isempty(rvalue)) {
1097 *s = 0;
1098 return 0;
1099 }
1100
1101 r = parse_ip_port(rvalue, &port);
1102 if (r < 0) {
d96edb2c 1103 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse port '%s'.", rvalue);
177d0b20
SS
1104 return 0;
1105 }
1106
1107 *s = port;
1108
1109 return 0;
1110}
9ecdba8c 1111
79138a38
LP
1112int config_parse_mtu(
1113 const char *unit,
1114 const char *filename,
1115 unsigned line,
1116 const char *section,
1117 unsigned section_line,
1118 const char *lvalue,
1119 int ltype,
1120 const char *rvalue,
1121 void *data,
1122 void *userdata) {
1123
1124 uint32_t *mtu = data;
1125 int r;
1126
1127 assert(rvalue);
1128 assert(mtu);
1129
1130 r = parse_mtu(ltype, rvalue, mtu);
1131 if (r == -ERANGE) {
d96edb2c 1132 log_syntax(unit, LOG_WARNING, filename, line, r,
79138a38
LP
1133 "Maximum transfer unit (MTU) value out of range. Permitted range is %" PRIu32 "…%" PRIu32 ", ignoring: %s",
1134 (uint32_t) (ltype == AF_INET6 ? IPV6_MIN_MTU : IPV4_MIN_MTU), (uint32_t) UINT32_MAX,
1135 rvalue);
1136 return 0;
1137 }
1138 if (r < 0) {
d96edb2c 1139 log_syntax(unit, LOG_WARNING, filename, line, r,
79138a38
LP
1140 "Failed to parse MTU value '%s', ignoring: %m", rvalue);
1141 return 0;
1142 }
1143
1144 return 0;
1145}
4f424df7
LP
1146
1147int config_parse_rlimit(
1148 const char *unit,
1149 const char *filename,
1150 unsigned line,
1151 const char *section,
1152 unsigned section_line,
1153 const char *lvalue,
1154 int ltype,
1155 const char *rvalue,
1156 void *data,
1157 void *userdata) {
1158
1159 struct rlimit **rl = data, d = {};
1160 int r;
1161
1162 assert(rvalue);
1163 assert(rl);
1164
1165 r = rlimit_parse(ltype, rvalue, &d);
1166 if (r == -EILSEQ) {
1167 log_syntax(unit, LOG_WARNING, filename, line, r, "Soft resource limit chosen higher than hard limit, ignoring: %s", rvalue);
1168 return 0;
1169 }
1170 if (r < 0) {
d96edb2c 1171 log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse resource value, ignoring: %s", rvalue);
4f424df7
LP
1172 return 0;
1173 }
1174
1175 if (rl[ltype])
1176 *rl[ltype] = d;
1177 else {
1178 rl[ltype] = newdup(struct rlimit, &d, 1);
1179 if (!rl[ltype])
1180 return log_oom();
1181 }
1182
1183 return 0;
1184}
c07b23ca 1185
c2f781bc
YW
1186int config_parse_permille(
1187 const char* unit,
1188 const char *filename,
1189 unsigned line,
1190 const char *section,
1191 unsigned section_line,
1192 const char *lvalue,
1193 int ltype,
1194 const char *rvalue,
1195 void *data,
1196 void *userdata) {
c07b23ca
MKB
1197
1198 unsigned *permille = data;
1199 int r;
1200
1201 assert(filename);
1202 assert(lvalue);
1203 assert(rvalue);
1204 assert(permille);
1205
1206 r = parse_permille(rvalue);
1207 if (r < 0) {
d96edb2c 1208 log_syntax(unit, LOG_WARNING, filename, line, r,
c07b23ca
MKB
1209 "Failed to parse permille value, ignoring: %s", rvalue);
1210 return 0;
1211 }
1212
1213 *permille = (unsigned) r;
1214
1215 return 0;
1216}
4df4df5b 1217
c2f781bc
YW
1218int config_parse_vlanprotocol(
1219 const char* unit,
1220 const char *filename,
1221 unsigned line,
1222 const char *section,
1223 unsigned section_line,
1224 const char *lvalue,
1225 int ltype,
1226 const char *rvalue,
1227 void *data,
1228 void *userdata) {
1229
4df4df5b 1230 int *vlan_protocol = data;
c2f781bc 1231
4df4df5b
RF
1232 assert(filename);
1233 assert(lvalue);
1234
1235 if (isempty(rvalue)) {
1236 *vlan_protocol = -1;
1237 return 0;
1238 }
1239
1240 if (STR_IN_SET(rvalue, "802.1ad", "802.1AD"))
1241 *vlan_protocol = ETH_P_8021AD;
1242 else if (STR_IN_SET(rvalue, "802.1q", "802.1Q"))
1243 *vlan_protocol = ETH_P_8021Q;
1244 else {
d96edb2c 1245 log_syntax(unit, LOG_WARNING, filename, line, 0,
4df4df5b
RF
1246 "Failed to parse VLAN protocol value, ignoring: %s", rvalue);
1247 return 0;
1248 }
1249
1250 return 0;
1251}
9de5e321 1252
0ec2a7a1
YW
1253int config_parse_hwaddr(
1254 const char *unit,
1255 const char *filename,
1256 unsigned line,
1257 const char *section,
1258 unsigned section_line,
1259 const char *lvalue,
1260 int ltype,
1261 const char *rvalue,
1262 void *data,
1263 void *userdata) {
1264
1265 _cleanup_free_ struct ether_addr *n = NULL;
1266 struct ether_addr **hwaddr = data;
1267 int r;
1268
1269 assert(filename);
1270 assert(lvalue);
1271 assert(rvalue);
1272 assert(data);
1273
1274 if (isempty(rvalue)) {
1275 *hwaddr = mfree(*hwaddr);
1276 return 0;
1277 }
1278
1279 n = new0(struct ether_addr, 1);
1280 if (!n)
1281 return log_oom();
1282
1283 r = ether_addr_from_string(rvalue, n);
1284 if (r < 0) {
1285 log_syntax(unit, LOG_WARNING, filename, line, r,
1286 "Not a valid MAC address, ignoring assignment: %s", rvalue);
1287 return 0;
1288 }
1289
1290 free_and_replace(*hwaddr, n);
1291
1292 return 0;
1293}
1294
1295int config_parse_hwaddrs(
1296 const char *unit,
1297 const char *filename,
1298 unsigned line,
1299 const char *section,
1300 unsigned section_line,
1301 const char *lvalue,
1302 int ltype,
1303 const char *rvalue,
1304 void *data,
1305 void *userdata) {
1306
1307 Set **hwaddrs = data;
1308 int r;
1309
1310 assert(filename);
1311 assert(lvalue);
1312 assert(rvalue);
1313 assert(data);
1314
1315 if (isempty(rvalue)) {
1316 /* Empty assignment resets the list */
1317 *hwaddrs = set_free_free(*hwaddrs);
1318 return 0;
1319 }
1320
1321 for (const char *p = rvalue;;) {
1322 _cleanup_free_ char *word = NULL;
1323 _cleanup_free_ struct ether_addr *n = NULL;
1324
1325 r = extract_first_word(&p, &word, NULL, 0);
1326 if (r == 0)
1327 return 0;
1328 if (r == -ENOMEM)
1329 return log_oom();
1330 if (r < 0) {
1331 log_syntax(unit, LOG_WARNING, filename, line, r,
1332 "Invalid syntax, ignoring: %s", rvalue);
1333 return 0;
1334 }
1335
1336 n = new(struct ether_addr, 1);
1337 if (!n)
1338 return log_oom();
1339
1340 r = ether_addr_from_string(word, n);
1341 if (r < 0) {
1342 log_syntax(unit, LOG_WARNING, filename, line, r,
1343 "Not a valid MAC address, ignoring: %s", word);
1344 continue;
1345 }
1346
1347 r = set_ensure_put(hwaddrs, &ether_addr_hash_ops, n);
1348 if (r < 0)
1349 return log_oom();
1350 if (r > 0)
1351 TAKE_PTR(n); /* avoid cleanup */
1352 }
1353}
1354
9de5e321 1355DEFINE_CONFIG_PARSE(config_parse_percent, parse_percent, "Failed to parse percent value");
0a9f9344 1356DEFINE_CONFIG_PARSE(config_parse_permyriad, parse_permyriad, "Failed to parse permyriad value");