]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
Merge pull request #8824 from keszybz/analyze-show-config
[thirdparty/systemd.git] / src / shared / conf-parser.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2010 Lennart Poettering
6 ***/
7
8 #include <errno.h>
9 #include <limits.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/types.h>
15
16 #include "alloc-util.h"
17 #include "conf-files.h"
18 #include "conf-parser.h"
19 #include "def.h"
20 #include "extract-word.h"
21 #include "fd-util.h"
22 #include "fileio.h"
23 #include "fs-util.h"
24 #include "log.h"
25 #include "macro.h"
26 #include "parse-util.h"
27 #include "path-util.h"
28 #include "process-util.h"
29 #include "signal-util.h"
30 #include "socket-util.h"
31 #include "string-util.h"
32 #include "strv.h"
33 #include "syslog-util.h"
34 #include "time-util.h"
35 #include "utf8.h"
36
37 int config_item_table_lookup(
38 const void *table,
39 const char *section,
40 const char *lvalue,
41 ConfigParserCallback *func,
42 int *ltype,
43 void **data,
44 void *userdata) {
45
46 const ConfigTableItem *t;
47
48 assert(table);
49 assert(lvalue);
50 assert(func);
51 assert(ltype);
52 assert(data);
53
54 for (t = table; t->lvalue; t++) {
55
56 if (!streq(lvalue, t->lvalue))
57 continue;
58
59 if (!streq_ptr(section, t->section))
60 continue;
61
62 *func = t->parse;
63 *ltype = t->ltype;
64 *data = t->data;
65 return 1;
66 }
67
68 return 0;
69 }
70
71 int config_item_perf_lookup(
72 const void *table,
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
94 key = strjoin(section, ".", lvalue);
95 if (!key)
96 return -ENOMEM;
97
98 p = lookup(key, strlen(key));
99 free(key);
100 }
101
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 */
112 static 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) {
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
140 if (r > 0) {
141 if (func)
142 return func(unit, filename, line, section, section_line,
143 lvalue, ltype, rvalue, data, userdata);
144
145 return 0;
146 }
147
148 /* Warn about unknown non-extension fields. */
149 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(lvalue, "X-"))
150 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
151
152 return 0;
153 }
154
155 /* Parse a single logical line */
156 static 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) {
169
170 char *e, *include;
171
172 assert(filename);
173 assert(line > 0);
174 assert(lookup);
175 assert(l);
176
177 l = strstrip(l);
178 if (!*l)
179 return 0;
180
181 if (strchr(COMMENTS "\n", *l))
182 return 0;
183
184 include = first_word(l, ".include");
185 if (include) {
186 _cleanup_free_ char *fn = NULL;
187
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
197 if (!(flags & CONFIG_PARSE_ALLOW_INCLUDE)) {
198 log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
199 return 0;
200 }
201
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
206 fn = file_in_same_dir(filename, strstrip(include));
207 if (!fn)
208 return -ENOMEM;
209
210 return config_parse(unit, fn, NULL, sections, lookup, table, flags, userdata);
211 }
212
213 if (*l == '[') {
214 size_t k;
215 char *n;
216
217 k = strlen(l);
218 assert(k > 0);
219
220 if (l[k-1] != ']') {
221 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
222 return -EBADMSG;
223 }
224
225 n = strndup(l+1, k-2);
226 if (!n)
227 return -ENOMEM;
228
229 if (sections && !nulstr_contains(sections, n)) {
230
231 if (!(flags & CONFIG_PARSE_RELAXED) && !startswith(n, "X-"))
232 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
233
234 free(n);
235 *section = mfree(*section);
236 *section_line = 0;
237 *section_ignored = true;
238 } else {
239 free(*section);
240 *section = n;
241 *section_line = line;
242 *section_ignored = false;
243 }
244
245 return 0;
246 }
247
248 if (sections && !*section) {
249
250 if (!(flags & CONFIG_PARSE_RELAXED) && !*section_ignored)
251 log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
252
253 return 0;
254 }
255
256 e = strchr(l, '=');
257 if (!e) {
258 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
259 return -EINVAL;
260 }
261
262 *e = 0;
263 e++;
264
265 return next_assignment(unit,
266 filename,
267 line,
268 lookup,
269 table,
270 *section,
271 *section_line,
272 strstrip(l),
273 strstrip(e),
274 flags,
275 userdata);
276 }
277
278 /* Go through the file and parse each line */
279 int config_parse(const char *unit,
280 const char *filename,
281 FILE *f,
282 const char *sections,
283 ConfigItemLookup lookup,
284 const void *table,
285 ConfigParseFlags flags,
286 void *userdata) {
287
288 _cleanup_free_ char *section = NULL, *continuation = NULL;
289 _cleanup_fclose_ FILE *ours = NULL;
290 unsigned line = 0, section_line = 0;
291 bool section_ignored = false;
292 int r;
293
294 assert(filename);
295 assert(lookup);
296
297 if (!f) {
298 f = ours = fopen(filename, "re");
299 if (!f) {
300 /* Only log on request, except for ENOENT,
301 * since we return 0 to the caller. */
302 if ((flags & CONFIG_PARSE_WARN) || errno == ENOENT)
303 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
304 "Failed to open configuration file '%s': %m", filename);
305 return errno == ENOENT ? 0 : -errno;
306 }
307 }
308
309 fd_warn_permissions(filename, fileno(f));
310
311 for (;;) {
312 _cleanup_free_ char *buf = NULL;
313 bool escaped = false;
314 char *l, *p, *e;
315
316 r = read_line(f, LONG_LINE_MAX, &buf);
317 if (r == 0)
318 break;
319 if (r == -ENOBUFS) {
320 if (flags & CONFIG_PARSE_WARN)
321 log_error_errno(r, "%s:%u: Line too long", filename, line);
322
323 return r;
324 }
325 if (r < 0) {
326 if (CONFIG_PARSE_WARN)
327 log_error_errno(r, "%s:%u: Error while reading configuration file: %m", filename, line);
328
329 return r;
330 }
331
332 l = buf;
333 if (!(flags & CONFIG_PARSE_REFUSE_BOM)) {
334 char *q;
335
336 q = startswith(buf, UTF8_BYTE_ORDER_MARK);
337 if (q) {
338 l = q;
339 flags |= CONFIG_PARSE_REFUSE_BOM;
340 }
341 }
342
343 if (continuation) {
344 if (strlen(continuation) + strlen(l) > LONG_LINE_MAX) {
345 if (flags & CONFIG_PARSE_WARN)
346 log_error("%s:%u: Continuation line too long", filename, line);
347 return -ENOBUFS;
348 }
349
350 if (!strextend(&continuation, l, NULL)) {
351 if (flags & CONFIG_PARSE_WARN)
352 log_oom();
353 return -ENOMEM;
354 }
355
356 p = continuation;
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
370 if (!continuation) {
371 continuation = strdup(l);
372 if (!continuation) {
373 if (flags & CONFIG_PARSE_WARN)
374 log_oom();
375 return -ENOMEM;
376 }
377 }
378
379 continue;
380 }
381
382 r = parse_line(unit,
383 filename,
384 ++line,
385 sections,
386 lookup,
387 table,
388 flags,
389 &section,
390 &section_line,
391 &section_ignored,
392 p,
393 userdata);
394 if (r < 0) {
395 if (flags & CONFIG_PARSE_WARN)
396 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
397 return r;
398
399 }
400
401 continuation = mfree(continuation);
402 }
403
404 if (continuation) {
405 r = parse_line(unit,
406 filename,
407 ++line,
408 sections,
409 lookup,
410 table,
411 flags,
412 &section,
413 &section_line,
414 &section_ignored,
415 continuation,
416 userdata);
417 if (r < 0) {
418 if (flags & CONFIG_PARSE_WARN)
419 log_warning_errno(r, "%s:%u: Failed to parse file: %m", filename, line);
420 return r;
421
422 }
423 }
424
425 return 0;
426 }
427
428 static int config_parse_many_files(
429 const char *conf_file,
430 char **files,
431 const char *sections,
432 ConfigItemLookup lookup,
433 const void *table,
434 ConfigParseFlags flags,
435 void *userdata) {
436
437 char **fn;
438 int r;
439
440 if (conf_file) {
441 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, flags, userdata);
442 if (r < 0)
443 return r;
444 }
445
446 STRV_FOREACH(fn, files) {
447 r = config_parse(NULL, *fn, NULL, sections, lookup, table, flags, userdata);
448 if (r < 0)
449 return r;
450 }
451
452 return 0;
453 }
454
455 /* Parse each config file in the directories specified as nulstr. */
456 int config_parse_many_nulstr(
457 const char *conf_file,
458 const char *conf_file_dirs,
459 const char *sections,
460 ConfigItemLookup lookup,
461 const void *table,
462 ConfigParseFlags flags,
463 void *userdata) {
464
465 _cleanup_strv_free_ char **files = NULL;
466 int r;
467
468 r = conf_files_list_nulstr(&files, ".conf", NULL, 0, conf_file_dirs);
469 if (r < 0)
470 return r;
471
472 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
473 }
474
475 /* Parse each config file in the directories specified as strv. */
476 int config_parse_many(
477 const char *conf_file,
478 const char* const* conf_file_dirs,
479 const char *dropin_dirname,
480 const char *sections,
481 ConfigItemLookup lookup,
482 const void *table,
483 ConfigParseFlags flags,
484 void *userdata) {
485
486 _cleanup_strv_free_ char **dropin_dirs = NULL;
487 _cleanup_strv_free_ char **files = NULL;
488 const char *suffix;
489 int r;
490
491 suffix = strjoina("/", dropin_dirname);
492 r = strv_extend_strv_concat(&dropin_dirs, (char**) conf_file_dirs, suffix);
493 if (r < 0)
494 return r;
495
496 r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char* const*) dropin_dirs);
497 if (r < 0)
498 return r;
499
500 return config_parse_many_files(conf_file, files, sections, lookup, table, flags, userdata);
501 }
502
503 #define DEFINE_PARSER(type, vartype, conv_func) \
504 int config_parse_##type( \
505 const char *unit, \
506 const char *filename, \
507 unsigned line, \
508 const char *section, \
509 unsigned section_line, \
510 const char *lvalue, \
511 int ltype, \
512 const char *rvalue, \
513 void *data, \
514 void *userdata) { \
515 \
516 vartype *i = data; \
517 int r; \
518 \
519 assert(filename); \
520 assert(lvalue); \
521 assert(rvalue); \
522 assert(data); \
523 \
524 r = conv_func(rvalue, i); \
525 if (r < 0) \
526 log_syntax(unit, LOG_ERR, filename, line, r, \
527 "Failed to parse %s value, ignoring: %s", \
528 #type, rvalue); \
529 \
530 return 0; \
531 }
532
533 DEFINE_PARSER(int, int, safe_atoi);
534 DEFINE_PARSER(long, long, safe_atoli);
535 DEFINE_PARSER(uint8, uint8_t, safe_atou8);
536 DEFINE_PARSER(uint16, uint16_t, safe_atou16);
537 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
538 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
539 DEFINE_PARSER(unsigned, unsigned, safe_atou);
540 DEFINE_PARSER(double, double, safe_atod);
541 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
542 DEFINE_PARSER(sec, usec_t, parse_sec);
543 DEFINE_PARSER(mode, mode_t, parse_mode);
544
545 int config_parse_iec_size(const char* unit,
546 const char *filename,
547 unsigned line,
548 const char *section,
549 unsigned section_line,
550 const char *lvalue,
551 int ltype,
552 const char *rvalue,
553 void *data,
554 void *userdata) {
555
556 size_t *sz = data;
557 uint64_t v;
558 int r;
559
560 assert(filename);
561 assert(lvalue);
562 assert(rvalue);
563 assert(data);
564
565 r = parse_size(rvalue, 1024, &v);
566 if (r < 0 || (uint64_t) (size_t) v != v) {
567 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
568 return 0;
569 }
570
571 *sz = (size_t) v;
572 return 0;
573 }
574
575 int config_parse_si_size(
576 const char* unit,
577 const char *filename,
578 unsigned line,
579 const char *section,
580 unsigned section_line,
581 const char *lvalue,
582 int ltype,
583 const char *rvalue,
584 void *data,
585 void *userdata) {
586
587 size_t *sz = data;
588 uint64_t v;
589 int r;
590
591 assert(filename);
592 assert(lvalue);
593 assert(rvalue);
594 assert(data);
595
596 r = parse_size(rvalue, 1000, &v);
597 if (r < 0 || (uint64_t) (size_t) v != v) {
598 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
599 return 0;
600 }
601
602 *sz = (size_t) v;
603 return 0;
604 }
605
606 int config_parse_iec_uint64(
607 const char* unit,
608 const char *filename,
609 unsigned line,
610 const char *section,
611 unsigned section_line,
612 const char *lvalue,
613 int ltype,
614 const char *rvalue,
615 void *data,
616 void *userdata) {
617
618 uint64_t *bytes = data;
619 int r;
620
621 assert(filename);
622 assert(lvalue);
623 assert(rvalue);
624 assert(data);
625
626 r = parse_size(rvalue, 1024, bytes);
627 if (r < 0)
628 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
629
630 return 0;
631 }
632
633 int config_parse_bool(const char* unit,
634 const char *filename,
635 unsigned line,
636 const char *section,
637 unsigned section_line,
638 const char *lvalue,
639 int ltype,
640 const char *rvalue,
641 void *data,
642 void *userdata) {
643
644 int k;
645 bool *b = data;
646 bool fatal = ltype;
647
648 assert(filename);
649 assert(lvalue);
650 assert(rvalue);
651 assert(data);
652
653 k = parse_boolean(rvalue);
654 if (k < 0) {
655 log_syntax(unit, LOG_ERR, filename, line, k,
656 "Failed to parse boolean value%s: %s",
657 fatal ? "" : ", ignoring", rvalue);
658 return fatal ? -ENOEXEC : 0;
659 }
660
661 *b = !!k;
662 return 0;
663 }
664
665 int config_parse_tristate(
666 const char* unit,
667 const char *filename,
668 unsigned line,
669 const char *section,
670 unsigned section_line,
671 const char *lvalue,
672 int ltype,
673 const char *rvalue,
674 void *data,
675 void *userdata) {
676
677 int k, *t = data;
678
679 assert(filename);
680 assert(lvalue);
681 assert(rvalue);
682 assert(data);
683
684 /* A tristate is pretty much a boolean, except that it can
685 * also take the special value -1, indicating "uninitialized",
686 * much like NULL is for a pointer type. */
687
688 k = parse_boolean(rvalue);
689 if (k < 0) {
690 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
691 return 0;
692 }
693
694 *t = !!k;
695 return 0;
696 }
697
698 int config_parse_string(
699 const char *unit,
700 const char *filename,
701 unsigned line,
702 const char *section,
703 unsigned section_line,
704 const char *lvalue,
705 int ltype,
706 const char *rvalue,
707 void *data,
708 void *userdata) {
709
710 char **s = data, *n;
711
712 assert(filename);
713 assert(lvalue);
714 assert(rvalue);
715 assert(data);
716
717 if (!utf8_is_valid(rvalue)) {
718 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
719 return 0;
720 }
721
722 if (isempty(rvalue))
723 n = NULL;
724 else {
725 n = strdup(rvalue);
726 if (!n)
727 return log_oom();
728 }
729
730 free(*s);
731 *s = n;
732
733 return 0;
734 }
735
736 int config_parse_path(
737 const char *unit,
738 const char *filename,
739 unsigned line,
740 const char *section,
741 unsigned section_line,
742 const char *lvalue,
743 int ltype,
744 const char *rvalue,
745 void *data,
746 void *userdata) {
747
748 char **s = data, *n;
749 bool fatal = ltype;
750
751 assert(filename);
752 assert(lvalue);
753 assert(rvalue);
754 assert(data);
755
756 if (isempty(rvalue)) {
757 n = NULL;
758 goto finalize;
759 }
760
761 if (!utf8_is_valid(rvalue)) {
762 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
763 return fatal ? -ENOEXEC : 0;
764 }
765
766 if (!path_is_absolute(rvalue)) {
767 log_syntax(unit, LOG_ERR, filename, line, 0,
768 "Not an absolute path%s: %s",
769 fatal ? "" : ", ignoring", rvalue);
770 return fatal ? -ENOEXEC : 0;
771 }
772
773 n = strdup(rvalue);
774 if (!n)
775 return log_oom();
776
777 path_kill_slashes(n);
778
779 finalize:
780 free(*s);
781 *s = n;
782
783 return 0;
784 }
785
786 int config_parse_strv(
787 const char *unit,
788 const char *filename,
789 unsigned line,
790 const char *section,
791 unsigned section_line,
792 const char *lvalue,
793 int ltype,
794 const char *rvalue,
795 void *data,
796 void *userdata) {
797
798 char ***sv = data;
799 int r;
800
801 assert(filename);
802 assert(lvalue);
803 assert(rvalue);
804 assert(data);
805
806 if (isempty(rvalue)) {
807 *sv = strv_free(*sv);
808 return 0;
809 }
810
811 for (;;) {
812 char *word = NULL;
813
814 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
815 if (r == 0)
816 break;
817 if (r == -ENOMEM)
818 return log_oom();
819 if (r < 0) {
820 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
821 break;
822 }
823
824 if (!utf8_is_valid(word)) {
825 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, word);
826 free(word);
827 continue;
828 }
829
830 r = strv_consume(sv, word);
831 if (r < 0)
832 return log_oom();
833 }
834
835 return 0;
836 }
837
838 int config_parse_warn_compat(
839 const char *unit,
840 const char *filename,
841 unsigned line,
842 const char *section,
843 unsigned section_line,
844 const char *lvalue,
845 int ltype,
846 const char *rvalue,
847 void *data,
848 void *userdata) {
849 Disabled reason = ltype;
850
851 switch(reason) {
852 case DISABLED_CONFIGURATION:
853 log_syntax(unit, LOG_DEBUG, filename, line, 0,
854 "Support for option %s= has been disabled at compile time and it is ignored", lvalue);
855 break;
856 case DISABLED_LEGACY:
857 log_syntax(unit, LOG_INFO, filename, line, 0,
858 "Support for option %s= has been removed and it is ignored", lvalue);
859 break;
860 case DISABLED_EXPERIMENTAL:
861 log_syntax(unit, LOG_INFO, filename, line, 0,
862 "Support for option %s= has not yet been enabled and it is ignored", lvalue);
863 break;
864 };
865
866 return 0;
867 }
868
869 int config_parse_log_facility(
870 const char *unit,
871 const char *filename,
872 unsigned line,
873 const char *section,
874 unsigned section_line,
875 const char *lvalue,
876 int ltype,
877 const char *rvalue,
878 void *data,
879 void *userdata) {
880
881 int *o = data, x;
882
883 assert(filename);
884 assert(lvalue);
885 assert(rvalue);
886 assert(data);
887
888 x = log_facility_unshifted_from_string(rvalue);
889 if (x < 0) {
890 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
891 return 0;
892 }
893
894 *o = (x << 3) | LOG_PRI(*o);
895
896 return 0;
897 }
898
899 int config_parse_log_level(
900 const char *unit,
901 const char *filename,
902 unsigned line,
903 const char *section,
904 unsigned section_line,
905 const char *lvalue,
906 int ltype,
907 const char *rvalue,
908 void *data,
909 void *userdata) {
910
911 int *o = data, x;
912
913 assert(filename);
914 assert(lvalue);
915 assert(rvalue);
916 assert(data);
917
918 x = log_level_from_string(rvalue);
919 if (x < 0) {
920 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
921 return 0;
922 }
923
924 if (*o < 0) /* if it wasn't initialized so far, assume zero facility */
925 *o = x;
926 else
927 *o = (*o & LOG_FACMASK) | x;
928
929 return 0;
930 }
931
932 int config_parse_signal(
933 const char *unit,
934 const char *filename,
935 unsigned line,
936 const char *section,
937 unsigned section_line,
938 const char *lvalue,
939 int ltype,
940 const char *rvalue,
941 void *data,
942 void *userdata) {
943
944 int *sig = data, r;
945
946 assert(filename);
947 assert(lvalue);
948 assert(rvalue);
949 assert(sig);
950
951 r = signal_from_string(rvalue);
952 if (r <= 0) {
953 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
954 return 0;
955 }
956
957 *sig = r;
958 return 0;
959 }
960
961 int config_parse_personality(
962 const char *unit,
963 const char *filename,
964 unsigned line,
965 const char *section,
966 unsigned section_line,
967 const char *lvalue,
968 int ltype,
969 const char *rvalue,
970 void *data,
971 void *userdata) {
972
973 unsigned long *personality = data, p;
974
975 assert(filename);
976 assert(lvalue);
977 assert(rvalue);
978 assert(personality);
979
980 if (isempty(rvalue))
981 p = PERSONALITY_INVALID;
982 else {
983 p = personality_from_string(rvalue);
984 if (p == PERSONALITY_INVALID) {
985 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
986 return 0;
987 }
988 }
989
990 *personality = p;
991 return 0;
992 }
993
994 int config_parse_ifname(
995 const char *unit,
996 const char *filename,
997 unsigned line,
998 const char *section,
999 unsigned section_line,
1000 const char *lvalue,
1001 int ltype,
1002 const char *rvalue,
1003 void *data,
1004 void *userdata) {
1005
1006 char **s = data;
1007 int r;
1008
1009 assert(filename);
1010 assert(lvalue);
1011 assert(rvalue);
1012 assert(data);
1013
1014 if (isempty(rvalue)) {
1015 *s = mfree(*s);
1016 return 0;
1017 }
1018
1019 if (!ifname_valid(rvalue)) {
1020 log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
1021 return 0;
1022 }
1023
1024 r = free_and_strdup(s, rvalue);
1025 if (r < 0)
1026 return log_oom();
1027
1028 return 0;
1029 }
1030
1031 int config_parse_ip_port(
1032 const char *unit,
1033 const char *filename,
1034 unsigned line,
1035 const char *section,
1036 unsigned section_line,
1037 const char *lvalue,
1038 int ltype,
1039 const char *rvalue,
1040 void *data,
1041 void *userdata) {
1042
1043 uint16_t *s = data;
1044 uint16_t port;
1045 int r;
1046
1047 assert(filename);
1048 assert(lvalue);
1049 assert(rvalue);
1050 assert(data);
1051
1052 if (isempty(rvalue)) {
1053 *s = 0;
1054 return 0;
1055 }
1056
1057 r = parse_ip_port(rvalue, &port);
1058 if (r < 0) {
1059 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse port '%s'.", rvalue);
1060 return 0;
1061 }
1062
1063 *s = port;
1064
1065 return 0;
1066 }
1067
1068 int config_parse_join_controllers(
1069 const char *unit,
1070 const char *filename,
1071 unsigned line,
1072 const char *section,
1073 unsigned section_line,
1074 const char *lvalue,
1075 int ltype,
1076 const char *rvalue,
1077 void *data,
1078 void *userdata) {
1079
1080 char ****ret = data;
1081 const char *whole_rvalue = rvalue;
1082 unsigned n = 0;
1083 _cleanup_(strv_free_freep) char ***controllers = NULL;
1084
1085 assert(filename);
1086 assert(lvalue);
1087 assert(rvalue);
1088 assert(ret);
1089
1090 for (;;) {
1091 _cleanup_free_ char *word = NULL;
1092 char **l;
1093 int r;
1094
1095 r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES);
1096 if (r < 0) {
1097 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
1098 return r;
1099 }
1100 if (r == 0)
1101 break;
1102
1103 l = strv_split(word, ",");
1104 if (!l)
1105 return log_oom();
1106 strv_uniq(l);
1107
1108 if (strv_length(l) <= 1) {
1109 strv_free(l);
1110 continue;
1111 }
1112
1113 if (!controllers) {
1114 controllers = new(char**, 2);
1115 if (!controllers) {
1116 strv_free(l);
1117 return log_oom();
1118 }
1119
1120 controllers[0] = l;
1121 controllers[1] = NULL;
1122
1123 n = 1;
1124 } else {
1125 char ***a;
1126 char ***t;
1127
1128 t = new0(char**, n+2);
1129 if (!t) {
1130 strv_free(l);
1131 return log_oom();
1132 }
1133
1134 n = 0;
1135
1136 for (a = controllers; *a; a++)
1137 if (strv_overlap(*a, l)) {
1138 if (strv_extend_strv(&l, *a, false) < 0) {
1139 strv_free(l);
1140 strv_free_free(t);
1141 return log_oom();
1142 }
1143
1144 } else {
1145 char **c;
1146
1147 c = strv_copy(*a);
1148 if (!c) {
1149 strv_free(l);
1150 strv_free_free(t);
1151 return log_oom();
1152 }
1153
1154 t[n++] = c;
1155 }
1156
1157 t[n++] = strv_uniq(l);
1158
1159 strv_free_free(controllers);
1160 controllers = t;
1161 }
1162 }
1163 if (!isempty(rvalue))
1164 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
1165
1166 /* As a special case, return a single empty strv, to override the default */
1167 if (!controllers) {
1168 controllers = new(char**, 2);
1169 if (!controllers)
1170 return log_oom();
1171 controllers[0] = strv_new(NULL, NULL);
1172 if (!controllers[0])
1173 return log_oom();
1174 controllers[1] = NULL;
1175 }
1176
1177 strv_free_free(*ret);
1178 *ret = TAKE_PTR(controllers);
1179
1180 return 0;
1181 }
1182
1183 int config_parse_mtu(
1184 const char *unit,
1185 const char *filename,
1186 unsigned line,
1187 const char *section,
1188 unsigned section_line,
1189 const char *lvalue,
1190 int ltype,
1191 const char *rvalue,
1192 void *data,
1193 void *userdata) {
1194
1195 uint32_t *mtu = data;
1196 int r;
1197
1198 assert(rvalue);
1199 assert(mtu);
1200
1201 r = parse_mtu(ltype, rvalue, mtu);
1202 if (r == -ERANGE) {
1203 log_syntax(unit, LOG_ERR, filename, line, r,
1204 "Maximum transfer unit (MTU) value out of range. Permitted range is %" PRIu32 "…%" PRIu32 ", ignoring: %s",
1205 (uint32_t) (ltype == AF_INET6 ? IPV6_MIN_MTU : IPV4_MIN_MTU), (uint32_t) UINT32_MAX,
1206 rvalue);
1207 return 0;
1208 }
1209 if (r < 0) {
1210 log_syntax(unit, LOG_ERR, filename, line, r,
1211 "Failed to parse MTU value '%s', ignoring: %m", rvalue);
1212 return 0;
1213 }
1214
1215 return 0;
1216 }