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