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