]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/conf-parser.c
tree-wide: drop license boilerplate
[thirdparty/systemd.git] / src / shared / conf-parser.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a7334b09
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
a7334b09
LP
6***/
7
ed5bcfbe 8#include <errno.h>
a8fbdf54
TA
9#include <limits.h>
10#include <stdint.h>
07630cea 11#include <stdio.h>
ed5bcfbe 12#include <stdlib.h>
07630cea 13#include <string.h>
a8fbdf54 14#include <sys/types.h>
07630cea 15
b5efdb8a 16#include "alloc-util.h"
e8461023 17#include "conf-files.h"
6bedfcbb 18#include "conf-parser.h"
e6dde451 19#include "def.h"
a8fbdf54 20#include "extract-word.h"
6bedfcbb 21#include "fd-util.h"
e6dde451 22#include "fileio.h"
f4f15635 23#include "fs-util.h"
16354eff 24#include "log.h"
07630cea 25#include "macro.h"
6bedfcbb 26#include "parse-util.h"
9eb977db 27#include "path-util.h"
7b3e062c 28#include "process-util.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
89 if (!section)
90 p = lookup(lvalue, strlen(lvalue));
91 else {
92 char *key;
93
605405c6 94 key = strjoin(section, ".", lvalue);
44d91056 95 if (!key)
f975e971
LP
96 return -ENOMEM;
97
98 p = lookup(key, strlen(key));
99 free(key);
ed5bcfbe
LP
100 }
101
f975e971
LP
102 if (!p)
103 return 0;
104
105 *func = p->parse;
106 *ltype = p->ltype;
107 *data = (uint8_t*) userdata + p->offset;
108 return 1;
109}
110
111/* Run the user supplied parser for an assignment */
bcde742e
LP
112static int next_assignment(
113 const char *unit,
114 const char *filename,
115 unsigned line,
116 ConfigItemLookup lookup,
117 const void *table,
118 const char *section,
119 unsigned section_line,
120 const char *lvalue,
121 const char *rvalue,
122 ConfigParseFlags flags,
123 void *userdata) {
f975e971
LP
124
125 ConfigParserCallback func = NULL;
126 int ltype = 0;
127 void *data = NULL;
128 int r;
129
130 assert(filename);
131 assert(line > 0);
132 assert(lookup);
133 assert(lvalue);
134 assert(rvalue);
135
136 r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
137 if (r < 0)
138 return r;
139
d937fbbd
LP
140 if (r > 0) {
141 if (func)
71a61510
TG
142 return func(unit, filename, line, section, section_line,
143 lvalue, ltype, rvalue, data, userdata);
d937fbbd
LP
144
145 return 0;
146 }
f975e971 147
46205bb6 148 /* Warn about unknown non-extension fields. */
bcde742e 149 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
12ca818f 150 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
46205bb6 151
f1857be0 152 return 0;
ed5bcfbe
LP
153}
154
8a37ce65 155/* Parse a single logical line */
bcde742e
LP
156static int parse_line(
157 const char* unit,
158 const char *filename,
159 unsigned line,
160 const char *sections,
161 ConfigItemLookup lookup,
162 const void *table,
163 ConfigParseFlags flags,
164 char **section,
165 unsigned *section_line,
166 bool *section_ignored,
167 char *l,
168 void *userdata) {
f975e971 169
bdc8e623 170 char *e, *include;
ed5bcfbe 171
f975e971
LP
172 assert(filename);
173 assert(line > 0);
174 assert(lookup);
175 assert(l);
176
b2aa81ef 177 l = strstrip(l);
b2aa81ef 178 if (!*l)
ed5bcfbe 179 return 0;
1ea86b18 180
d3b6d0c2 181 if (strchr(COMMENTS "\n", *l))
1ea86b18 182 return 0;
ed5bcfbe 183
bdc8e623
LP
184 include = first_word(l, ".include");
185 if (include) {
db5c0122
LP
186 _cleanup_free_ char *fn = NULL;
187
b8e7a47b
LP
188 /* .includes are a bad idea, we only support them here
189 * for historical reasons. They create cyclic include
190 * problems and make it difficult to detect
191 * configuration file changes with an easy
192 * stat(). Better approaches, such as .d/ drop-in
193 * snippets exist.
194 *
195 * Support for them should be eventually removed. */
196
bcde742e 197 if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
12ca818f 198 log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
db5c0122
LP
199 return 0;
200 }
ed5bcfbe 201
41b283d0
LP
202 log_syntax(unit, LOG_WARNING, filename, line, 0,
203 ".include directives are deprecated, and support for them will be removed in a future version of systemd. "
204 "Please use drop-in files instead.");
205
bdc8e623 206 fn = file_in_same_dir(filename, strstrip(include));
f975e971 207 if (!fn)
b2aa81ef 208 return -ENOMEM;
ed5bcfbe 209
bcde742e 210 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
ed5bcfbe
LP
211 }
212
b2aa81ef 213 if (*l == '[') {
ed5bcfbe
LP
214 size_t k;
215 char *n;
216
b2aa81ef 217 k = strlen(l);
ed5bcfbe
LP
218 assert(k > 0);
219
b2aa81ef 220 if (l[k-1] != ']') {
12ca818f 221 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
ed5bcfbe
LP
222 return -EBADMSG;
223 }
224
f975e971
LP
225 n = strndup(l+1, k-2);
226 if (!n)
ed5bcfbe
LP
227 return -ENOMEM;
228
f975e971 229 if (sections && !nulstr_contains(sections, n)) {
42f4e3c4 230
bcde742e 231 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
12ca818f 232 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
f975e971
LP
233
234 free(n);
a1e58e8e 235 *section = mfree(*section);
71a61510 236 *section_line = 0;
342aea19 237 *section_ignored = true;
f975e971
LP
238 } else {
239 free(*section);
240 *section = n;
71a61510 241 *section_line = line;
342aea19 242 *section_ignored = false;
f975e971 243 }
ed5bcfbe
LP
244
245 return 0;
246 }
247
62f168a0
LP
248 if (sections && !*section) {
249
bcde742e 250 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
12ca818f 251 log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
62f168a0 252
10e87ee7 253 return 0;
62f168a0 254 }
10e87ee7 255
f975e971
LP
256 e = strchr(l, '=');
257 if (!e) {
12ca818f
LP
258 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
259 return -EINVAL;
ed5bcfbe
LP
260 }
261
262 *e = 0;
263 e++;
264
e8e581bf
ZJS
265 return next_assignment(unit,
266 filename,
267 line,
268 lookup,
269 table,
270 *section,
71a61510 271 *section_line,
e8e581bf
ZJS
272 strstrip(l),
273 strstrip(e),
bcde742e 274 flags,
e8e581bf 275 userdata);
ed5bcfbe
LP
276}
277
278/* Go through the file and parse each line */
e8e581bf
ZJS
279int config_parse(const char *unit,
280 const char *filename,
281 FILE *f,
282 const char *sections,
283 ConfigItemLookup lookup,
e9f3d2d5 284 const void *table,
bcde742e 285 ConfigParseFlags flags,
e8e581bf 286 void *userdata) {
f975e971 287
7fd1b19b
HH
288 _cleanup_free_ char *section = NULL, *continuation = NULL;
289 _cleanup_fclose_ FILE *ours = NULL;
71a61510 290 unsigned line = 0, section_line = 0;
bcde742e 291 bool section_ignored = false;
ed5bcfbe
LP
292 int r;
293
294 assert(filename);
f975e971 295 assert(lookup);
ed5bcfbe 296
87f0e418 297 if (!f) {
245802dd 298 f = ours = fopen(filename, "re");
f975e971 299 if (!f) {
36f822c4
ZJS
300 /* Only log on request, except for ENOENT,
301 * since we return 0 to the caller. */
bcde742e 302 if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
36f822c4
ZJS
303 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
304 "Failed to open configuration file '%s': %m", filename);
9f43a07f 305 return errno == ENOENT ? 0 : -errno;
87f0e418 306 }
ed5bcfbe
LP
307 }
308
fdb9161c
LP
309 fd_warn_permissions(filename, fileno(f));
310
9dd7ea9a 311 for (;;) {
e6dde451 312 _cleanup_free_ char *buf = NULL;
3dab2943 313 bool escaped = false;
92b5e605 314 char *l, *p, *e;
ed5bcfbe 315
e6dde451
LP
316 r = read_line(f, LONG_LINE_MAX, &buf);
317 if (r == 0)
318 break;
319 if (r == -ENOBUFS) {
bcde742e 320 if (flags & CONFIG_PARSE_WARN)
e6dde451
LP
321 log_error_errno(r, "%s:%u: Line too long", filename, line);
322
323 return r;
324 }
325 if (r < 0) {
bcde742e 326 if (CONFIG_PARSE_WARN)
e6dde451 327 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
ed5bcfbe 328
e6dde451 329 return r;
ed5bcfbe
LP
330 }
331
9dd7ea9a 332 l = buf;
bcde742e 333 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
e6dde451 334 char *q;
9dd7ea9a 335
e6dde451
LP
336 q = startswith(buf, UTF8_BYTE_ORDER_MARK);
337 if (q) {
338 l = q;
bcde742e 339 flags |= CONFIG_PARSE_REFUSE_BOM;
e6dde451
LP
340 }
341 }
3dab2943
LP
342
343 if (continuation) {
e6dde451 344 if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
bcde742e 345 if (flags & CONFIG_PARSE_WARN)
e6dde451
LP
346 log_error("%s:%u: Continuation line too long", filename, line);
347 return -ENOBUFS;
348 }
349
92b5e605 350 if (!strextend(&continuation, l, NULL)) {
bcde742e 351 if (flags & CONFIG_PARSE_WARN)
36f822c4 352 log_oom();
245802dd 353 return -ENOMEM;
36f822c4 354 }
3dab2943 355
92b5e605 356 p = continuation;
3dab2943
LP
357 } else
358 p = l;
359
360 for (e = p; *e; e++) {
361 if (escaped)
362 escaped = false;
363 else if (*e == '\\')
364 escaped = true;
365 }
366
367 if (escaped) {
368 *(e-1) = ' ';
369
92b5e605 370 if (!continuation) {
f975e971 371 continuation = strdup(l);
36f822c4 372 if (!continuation) {
bcde742e 373 if (flags & CONFIG_PARSE_WARN)
36f822c4 374 log_oom();
245802dd 375 return -ENOMEM;
36f822c4 376 }
3dab2943
LP
377 }
378
379 continue;
380 }
381
e8e581bf
ZJS
382 r = parse_line(unit,
383 filename,
384 ++line,
385 sections,
386 lookup,
387 table,
bcde742e 388 flags,
e8e581bf 389 &section,
71a61510 390 &section_line,
342aea19 391 &section_ignored,
e8e581bf
ZJS
392 p,
393 userdata);
36f822c4 394 if (r < 0) {
bcde742e 395 if (flags & CONFIG_PARSE_WARN)
e6dde451 396 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
245802dd 397 return r;
92b5e605 398
36f822c4 399 }
92b5e605
LP
400
401 continuation = mfree(continuation);
ed5bcfbe
LP
402 }
403
245802dd 404 return 0;
ed5bcfbe
LP
405}
406
23bb31aa 407static int config_parse_many_files(
43688c49 408 const char *conf_file,
23bb31aa 409 char **files,
43688c49
ZJS
410 const char *sections,
411 ConfigItemLookup lookup,
412 const void *table,
bcde742e 413 ConfigParseFlags flags,
43688c49
ZJS
414 void *userdata) {
415
e8461023
JT
416 char **fn;
417 int r;
418
e8461023 419 if (conf_file) {
bcde742e 420 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
e8461023
JT
421 if (r < 0)
422 return r;
423 }
424
425 STRV_FOREACH(fn, files) {
bcde742e 426 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
e8461023
JT
427 if (r < 0)
428 return r;
429 }
430
431 return 0;
432}
433
23bb31aa
ZJS
434/* Parse each config file in the directories specified as nulstr. */
435int config_parse_many_nulstr(
436 const char *conf_file,
437 const char *conf_file_dirs,
438 const char *sections,
439 ConfigItemLookup lookup,
440 const void *table,
bcde742e 441 ConfigParseFlags flags,
23bb31aa
ZJS
442 void *userdata) {
443
444 _cleanup_strv_free_ char **files = NULL;
445 int r;
446
b5084605 447 r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
23bb31aa
ZJS
448 if (r < 0)
449 return r;
450
bcde742e 451 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
23bb31aa
ZJS
452}
453
454/* Parse each config file in the directories specified as strv. */
455int config_parse_many(
456 const char *conf_file,
457 const char* const* conf_file_dirs,
458 const char *dropin_dirname,
459 const char *sections,
460 ConfigItemLookup lookup,
461 const void *table,
bcde742e 462 ConfigParseFlags flags,
23bb31aa
ZJS
463 void *userdata) {
464
465 _cleanup_strv_free_ char **dropin_dirs = NULL;
466 _cleanup_strv_free_ char **files = NULL;
467 const char *suffix;
468 int r;
469
470 suffix = strjoina("/", dropin_dirname);
471 r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
472 if (r < 0)
473 return r;
474
b5084605 475 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
23bb31aa
ZJS
476 if (r < 0)
477 return r;
478
bcde742e 479 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
23bb31aa
ZJS
480}
481
eb3491d9 482#define DEFINE_PARSER(type, vartype, conv_func) \
94d75d64
LP
483 int config_parse_##type( \
484 const char *unit, \
485 const char *filename, \
486 unsigned line, \
487 const char *section, \
488 unsigned section_line, \
489 const char *lvalue, \
490 int ltype, \
491 const char *rvalue, \
492 void *data, \
493 void *userdata) { \
eb3491d9
ZJS
494 \
495 vartype *i = data; \
496 int r; \
497 \
498 assert(filename); \
499 assert(lvalue); \
500 assert(rvalue); \
501 assert(data); \
502 \
503 r = conv_func(rvalue, i); \
504 if (r < 0) \
12ca818f 505 log_syntax(unit, LOG_ERR, filename, line, r, \
e8e581bf 506 "Failed to parse %s value, ignoring: %s", \
98d75800 507 #type, rvalue); \
eb3491d9
ZJS
508 \
509 return 0; \
f6a8265b 510 }
94d75d64
LP
511
512DEFINE_PARSER(int, int, safe_atoi);
513DEFINE_PARSER(long, long, safe_atoli);
134e24e1 514DEFINE_PARSER(uint8, uint8_t, safe_atou8);
c7440e74 515DEFINE_PARSER(uint16, uint16_t, safe_atou16);
94d75d64
LP
516DEFINE_PARSER(uint32, uint32_t, safe_atou32);
517DEFINE_PARSER(uint64, uint64_t, safe_atou64);
518DEFINE_PARSER(unsigned, unsigned, safe_atou);
519DEFINE_PARSER(double, double, safe_atod);
520DEFINE_PARSER(nsec, nsec_t, parse_nsec);
521DEFINE_PARSER(sec, usec_t, parse_sec);
522DEFINE_PARSER(mode, mode_t, parse_mode);
ed5bcfbe 523
5556b5fe 524int config_parse_iec_size(const char* unit,
e8e581bf
ZJS
525 const char *filename,
526 unsigned line,
527 const char *section,
71a61510 528 unsigned section_line,
e8e581bf
ZJS
529 const char *lvalue,
530 int ltype,
531 const char *rvalue,
532 void *data,
533 void *userdata) {
ed5bcfbe
LP
534
535 size_t *sz = data;
59f448cf 536 uint64_t v;
e8e581bf 537 int r;
ed5bcfbe
LP
538
539 assert(filename);
540 assert(lvalue);
541 assert(rvalue);
542 assert(data);
543
59f448cf
LP
544 r = parse_size(rvalue, 1024, &v);
545 if (r < 0 || (uint64_t) (size_t) v != v) {
12ca818f 546 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
9ba1a159
LP
547 return 0;
548 }
549
59f448cf 550 *sz = (size_t) v;
9ba1a159
LP
551 return 0;
552}
553
78f3c4bc
LP
554int config_parse_si_size(
555 const char* unit,
556 const char *filename,
557 unsigned line,
558 const char *section,
559 unsigned section_line,
560 const char *lvalue,
561 int ltype,
562 const char *rvalue,
563 void *data,
564 void *userdata) {
5556b5fe
LP
565
566 size_t *sz = data;
59f448cf 567 uint64_t v;
5556b5fe
LP
568 int r;
569
570 assert(filename);
571 assert(lvalue);
572 assert(rvalue);
573 assert(data);
574
59f448cf
LP
575 r = parse_size(rvalue, 1000, &v);
576 if (r < 0 || (uint64_t) (size_t) v != v) {
12ca818f 577 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
5556b5fe
LP
578 return 0;
579 }
580
59f448cf 581 *sz = (size_t) v;
5556b5fe
LP
582 return 0;
583}
9ba1a159 584
78f3c4bc
LP
585int config_parse_iec_uint64(
586 const char* unit,
587 const char *filename,
588 unsigned line,
589 const char *section,
590 unsigned section_line,
591 const char *lvalue,
592 int ltype,
593 const char *rvalue,
594 void *data,
595 void *userdata) {
9ba1a159 596
59f448cf 597 uint64_t *bytes = data;
e8e581bf 598 int r;
9ba1a159
LP
599
600 assert(filename);
601 assert(lvalue);
602 assert(rvalue);
603 assert(data);
604
5556b5fe 605 r = parse_size(rvalue, 1024, bytes);
e8e581bf 606 if (r < 0)
59f448cf 607 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
ed5bcfbe 608
ed5bcfbe
LP
609 return 0;
610}
611
e8e581bf
ZJS
612int config_parse_bool(const char* unit,
613 const char *filename,
614 unsigned line,
615 const char *section,
71a61510 616 unsigned section_line,
e8e581bf
ZJS
617 const char *lvalue,
618 int ltype,
619 const char *rvalue,
620 void *data,
621 void *userdata) {
ed5bcfbe
LP
622
623 int k;
624 bool *b = data;
2c75fb73 625 bool fatal = ltype;
ed5bcfbe
LP
626
627 assert(filename);
628 assert(lvalue);
629 assert(rvalue);
630 assert(data);
631
e8e581bf
ZJS
632 k = parse_boolean(rvalue);
633 if (k < 0) {
2c75fb73
ZJS
634 log_syntax(unit, LOG_ERR, filename, line, k,
635 "Failed to parse boolean value%s: %s",
636 fatal ? "" : ", ignoring", rvalue);
637 return fatal ? -ENOEXEC : 0;
ed5bcfbe
LP
638 }
639
640 *b = !!k;
641 return 0;
642}
643
f757855e
LP
644int config_parse_tristate(
645 const char* unit,
646 const char *filename,
647 unsigned line,
648 const char *section,
649 unsigned section_line,
650 const char *lvalue,
651 int ltype,
652 const char *rvalue,
653 void *data,
654 void *userdata) {
655
656 int k, *t = data;
657
658 assert(filename);
659 assert(lvalue);
660 assert(rvalue);
661 assert(data);
662
663 /* A tristate is pretty much a boolean, except that it can
664 * also take the special value -1, indicating "uninitialized",
665 * much like NULL is for a pointer type. */
666
667 k = parse_boolean(rvalue);
668 if (k < 0) {
669 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
670 return 0;
671 }
672
673 *t = !!k;
674 return 0;
675}
676
8f2665a4
LP
677int config_parse_string(
678 const char *unit,
679 const char *filename,
680 unsigned line,
681 const char *section,
682 unsigned section_line,
683 const char *lvalue,
684 int ltype,
685 const char *rvalue,
686 void *data,
687 void *userdata) {
688
689 char **s = data, *n;
ed5bcfbe
LP
690
691 assert(filename);
692 assert(lvalue);
693 assert(rvalue);
694 assert(data);
695
8f2665a4 696 if (!utf8_is_valid(rvalue)) {
0e05ee04 697 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
7f110ff9
LP
698 return 0;
699 }
ed5bcfbe 700
8f2665a4
LP
701 if (isempty(rvalue))
702 n = NULL;
7f110ff9 703 else {
8f2665a4
LP
704 n = strdup(rvalue);
705 if (!n)
706 return log_oom();
7f110ff9 707 }
ed5bcfbe 708
8f2665a4
LP
709 free(*s);
710 *s = n;
711
ed5bcfbe
LP
712 return 0;
713}
57d42a5f 714
8f2665a4
LP
715int config_parse_path(
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) {
034c6ed7 726
3b436292 727 char **s = data, *n;
2c75fb73 728 bool fatal = ltype;
034c6ed7
LP
729
730 assert(filename);
731 assert(lvalue);
732 assert(rvalue);
733 assert(data);
734
0fe50629
YW
735 if (isempty(rvalue)) {
736 n = NULL;
737 goto finalize;
738 }
739
7f110ff9 740 if (!utf8_is_valid(rvalue)) {
0e05ee04 741 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
2c75fb73 742 return fatal ? -ENOEXEC : 0;
7f110ff9
LP
743 }
744
3b436292 745 if (!path_is_absolute(rvalue)) {
2c75fb73
ZJS
746 log_syntax(unit, LOG_ERR, filename, line, 0,
747 "Not an absolute path%s: %s",
748 fatal ? "" : ", ignoring", rvalue);
749 return fatal ? -ENOEXEC : 0;
034c6ed7
LP
750 }
751
7f110ff9
LP
752 n = strdup(rvalue);
753 if (!n)
74051b9b 754 return log_oom();
034c6ed7 755
01f78473
LP
756 path_kill_slashes(n);
757
0fe50629 758finalize:
034c6ed7
LP
759 free(*s);
760 *s = n;
761
762 return 0;
763}
57d42a5f 764
8249bb72
LP
765int config_parse_strv(
766 const char *unit,
767 const char *filename,
768 unsigned line,
769 const char *section,
770 unsigned section_line,
771 const char *lvalue,
772 int ltype,
773 const char *rvalue,
774 void *data,
775 void *userdata) {
57d42a5f 776
a2a5291b 777 char ***sv = data;
00d0fd06 778 int r;
57d42a5f
LP
779
780 assert(filename);
781 assert(lvalue);
782 assert(rvalue);
783 assert(data);
784
74051b9b 785 if (isempty(rvalue)) {
8249bb72 786 *sv = strv_free(*sv);
853b8397 787 return 0;
74051b9b
LP
788 }
789
34f253f0
DR
790 for (;;) {
791 char *word = NULL;
00d0fd06 792
9a82ab95 793 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
34f253f0
DR
794 if (r == 0)
795 break;
796 if (r == -ENOMEM)
853b8397 797 return log_oom();
34f253f0
DR
798 if (r < 0) {
799 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
800 break;
801 }
853b8397 802
34f253f0 803 if (!utf8_is_valid(word)) {
b4958f42 804 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
34f253f0 805 free(word);
7f110ff9
LP
806 continue;
807 }
8249bb72 808
34f253f0 809 r = strv_consume(sv, word);
853b8397
LP
810 if (r < 0)
811 return log_oom();
7f110ff9 812 }
57d42a5f 813
57d42a5f 814 return 0;
57d42a5f 815}
15ae422b 816
1e35c5ab
RP
817int config_parse_warn_compat(
818 const char *unit,
819 const char *filename,
820 unsigned line,
821 const char *section,
822 unsigned section_line,
823 const char *lvalue,
824 int ltype,
825 const char *rvalue,
826 void *data,
827 void *userdata) {
828 Disabled reason = ltype;
829
830 switch(reason) {
831 case DISABLED_CONFIGURATION:
832 log_syntax(unit, LOG_DEBUG, filename, line, 0,
833 "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
834 break;
835 case DISABLED_LEGACY:
836 log_syntax(unit, LOG_INFO, filename, line, 0,
837 "Support for option %s= has been removed and it is ignored", lvalue);
838 break;
839 case DISABLED_EXPERIMENTAL:
840 log_syntax(unit, LOG_INFO, filename, line, 0,
841 "Support for option %s= has not yet been enabled and it is ignored", lvalue);
842 break;
843 };
844
845 return 0;
846}
847
ca37242e
LP
848int config_parse_log_facility(
849 const char *unit,
850 const char *filename,
851 unsigned line,
852 const char *section,
853 unsigned section_line,
854 const char *lvalue,
855 int ltype,
856 const char *rvalue,
857 void *data,
858 void *userdata) {
213ba152 859
213ba152
LP
860 int *o = data, x;
861
862 assert(filename);
863 assert(lvalue);
864 assert(rvalue);
865 assert(data);
866
867 x = log_facility_unshifted_from_string(rvalue);
868 if (x < 0) {
12ca818f 869 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
213ba152
LP
870 return 0;
871 }
872
873 *o = (x << 3) | LOG_PRI(*o);
874
875 return 0;
876}
877
ca37242e
LP
878int config_parse_log_level(
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) {
213ba152 889
213ba152
LP
890 int *o = data, x;
891
892 assert(filename);
893 assert(lvalue);
894 assert(rvalue);
895 assert(data);
896
897 x = log_level_from_string(rvalue);
898 if (x < 0) {
12ca818f 899 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
213ba152
LP
900 return 0;
901 }
902
d3070fbd
LP
903 if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
904 *o = x;
905 else
906 *o = (*o & LOG_FACMASK) | x;
907
213ba152
LP
908 return 0;
909}
f757855e
LP
910
911int config_parse_signal(
912 const char *unit,
913 const char *filename,
914 unsigned line,
915 const char *section,
916 unsigned section_line,
917 const char *lvalue,
918 int ltype,
919 const char *rvalue,
920 void *data,
921 void *userdata) {
922
923 int *sig = data, r;
924
925 assert(filename);
926 assert(lvalue);
927 assert(rvalue);
928 assert(sig);
929
930 r = signal_from_string_try_harder(rvalue);
931 if (r <= 0) {
12ca818f 932 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
f757855e
LP
933 return 0;
934 }
935
936 *sig = r;
937 return 0;
938}
939
940int config_parse_personality(
941 const char *unit,
942 const char *filename,
943 unsigned line,
944 const char *section,
945 unsigned section_line,
946 const char *lvalue,
947 int ltype,
948 const char *rvalue,
949 void *data,
950 void *userdata) {
951
952 unsigned long *personality = data, p;
953
954 assert(filename);
955 assert(lvalue);
956 assert(rvalue);
957 assert(personality);
958
40fdd636
LP
959 if (isempty(rvalue))
960 p = PERSONALITY_INVALID;
961 else {
962 p = personality_from_string(rvalue);
963 if (p == PERSONALITY_INVALID) {
964 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
965 return 0;
966 }
f757855e
LP
967 }
968
969 *personality = p;
970 return 0;
971}
d31645ad
LP
972
973int config_parse_ifname(
974 const char *unit,
975 const char *filename,
976 unsigned line,
977 const char *section,
978 unsigned section_line,
979 const char *lvalue,
980 int ltype,
981 const char *rvalue,
982 void *data,
983 void *userdata) {
984
985 char **s = data;
986 int r;
987
988 assert(filename);
989 assert(lvalue);
990 assert(rvalue);
991 assert(data);
992
993 if (isempty(rvalue)) {
994 *s = mfree(*s);
995 return 0;
996 }
997
998 if (!ifname_valid(rvalue)) {
999 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1000 return 0;
1001 }
1002
1003 r = free_and_strdup(s, rvalue);
1004 if (r < 0)
1005 return log_oom();
1006
1007 return 0;
1008}
177d0b20
SS
1009
1010int config_parse_ip_port(
1011 const char *unit,
1012 const char *filename,
1013 unsigned line,
1014 const char *section,
1015 unsigned section_line,
1016 const char *lvalue,
1017 int ltype,
1018 const char *rvalue,
1019 void *data,
1020 void *userdata) {
1021
1022 uint16_t *s = data;
1023 uint16_t port;
1024 int r;
1025
1026 assert(filename);
1027 assert(lvalue);
1028 assert(rvalue);
1029 assert(data);
1030
1031 if (isempty(rvalue)) {
1032 *s = 0;
1033 return 0;
1034 }
1035
1036 r = parse_ip_port(rvalue, &port);
1037 if (r < 0) {
1038 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1039 return 0;
1040 }
1041
1042 *s = port;
1043
1044 return 0;
1045}
9ecdba8c
ZJS
1046
1047int config_parse_join_controllers(
1048 const char *unit,
1049 const char *filename,
1050 unsigned line,
1051 const char *section,
1052 unsigned section_line,
1053 const char *lvalue,
1054 int ltype,
1055 const char *rvalue,
1056 void *data,
1057 void *userdata) {
1058
1059 char ****ret = data;
1060 const char *whole_rvalue = rvalue;
1061 unsigned n = 0;
1062 _cleanup_(strv_free_freep) char ***controllers = NULL;
1063
1064 assert(filename);
1065 assert(lvalue);
1066 assert(rvalue);
1067 assert(ret);
1068
1069 for (;;) {
1070 _cleanup_free_ char *word = NULL;
1071 char **l;
1072 int r;
1073
1074 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1075 if (r < 0) {
1076 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1077 return r;
1078 }
1079 if (r == 0)
1080 break;
1081
1082 l = strv_split(word, ",");
1083 if (!l)
1084 return log_oom();
1085 strv_uniq(l);
1086
1087 if (strv_length(l) <= 1) {
1088 strv_free(l);
1089 continue;
1090 }
1091
1092 if (!controllers) {
1093 controllers = new(char**, 2);
1094 if (!controllers) {
1095 strv_free(l);
1096 return log_oom();
1097 }
1098
1099 controllers[0] = l;
1100 controllers[1] = NULL;
1101
1102 n = 1;
1103 } else {
1104 char ***a;
1105 char ***t;
1106
1107 t = new0(char**, n+2);
1108 if (!t) {
1109 strv_free(l);
1110 return log_oom();
1111 }
1112
1113 n = 0;
1114
1115 for (a = controllers; *a; a++)
1116 if (strv_overlap(*a, l)) {
1117 if (strv_extend_strv(&l, *a, false) < 0) {
1118 strv_free(l);
1119 strv_free_free(t);
1120 return log_oom();
1121 }
1122
1123 } else {
1124 char **c;
1125
1126 c = strv_copy(*a);
1127 if (!c) {
1128 strv_free(l);
1129 strv_free_free(t);
1130 return log_oom();
1131 }
1132
1133 t[n++] = c;
1134 }
1135
1136 t[n++] = strv_uniq(l);
1137
1138 strv_free_free(controllers);
1139 controllers = t;
1140 }
1141 }
1142 if (!isempty(rvalue))
1143 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1144
56c8d744
ZJS
1145 /* As a special case, return a single empty strv, to override the default */
1146 if (!controllers) {
1147 controllers = new(char**, 2);
1148 if (!controllers)
1149 return log_oom();
1150 controllers[0] = strv_new(NULL, NULL);
1151 if (!controllers[0])
1152 return log_oom();
1153 controllers[1] = NULL;
1154 }
1155
9ecdba8c 1156 strv_free_free(*ret);
1cc6c93a 1157 *ret = TAKE_PTR(controllers);
9ecdba8c
ZJS
1158
1159 return 0;
1160}