]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
Merge pull request #1180 from evverx/dot-from-to-alias-handling
[thirdparty/systemd.git] / src / shared / conf-parser.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <string.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdlib.h>
26
27 #include "sd-messages.h"
28 #include "conf-files.h"
29 #include "util.h"
30 #include "macro.h"
31 #include "strv.h"
32 #include "log.h"
33 #include "utf8.h"
34 #include "path-util.h"
35 #include "signal-util.h"
36 #include "conf-parser.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, NULL);
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(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 bool relaxed,
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 (!relaxed && !startswith(lvalue, "X-"))
150 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
151 "Unknown lvalue '%s' in section '%s'", lvalue, section);
152
153 return 0;
154 }
155
156 /* Parse a variable assignment line */
157 static int parse_line(const char* unit,
158 const char *filename,
159 unsigned line,
160 const char *sections,
161 ConfigItemLookup lookup,
162 const void *table,
163 bool relaxed,
164 bool allow_include,
165 char **section,
166 unsigned *section_line,
167 bool *section_ignored,
168 char *l,
169 void *userdata) {
170
171 char *e;
172
173 assert(filename);
174 assert(line > 0);
175 assert(lookup);
176 assert(l);
177
178 l = strstrip(l);
179
180 if (!*l)
181 return 0;
182
183 if (strchr(COMMENTS "\n", *l))
184 return 0;
185
186 if (startswith(l, ".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 (!allow_include) {
199 log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
200 ".include not allowed here. Ignoring.");
201 return 0;
202 }
203
204 fn = file_in_same_dir(filename, strstrip(l+9));
205 if (!fn)
206 return -ENOMEM;
207
208 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata);
209 }
210
211 if (*l == '[') {
212 size_t k;
213 char *n;
214
215 k = strlen(l);
216 assert(k > 0);
217
218 if (l[k-1] != ']') {
219 log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
220 "Invalid section header '%s'", l);
221 return -EBADMSG;
222 }
223
224 n = strndup(l+1, k-2);
225 if (!n)
226 return -ENOMEM;
227
228 if (sections && !nulstr_contains(sections, n)) {
229
230 if (!relaxed && !startswith(n, "X-"))
231 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
232 "Unknown section '%s'. Ignoring.", n);
233
234 free(n);
235 free(*section);
236 *section = NULL;
237 *section_line = 0;
238 *section_ignored = true;
239 } else {
240 free(*section);
241 *section = n;
242 *section_line = line;
243 *section_ignored = false;
244 }
245
246 return 0;
247 }
248
249 if (sections && !*section) {
250
251 if (!relaxed && !*section_ignored)
252 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
253 "Assignment outside of section. Ignoring.");
254
255 return 0;
256 }
257
258 e = strchr(l, '=');
259 if (!e) {
260 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
261 return -EBADMSG;
262 }
263
264 *e = 0;
265 e++;
266
267 return next_assignment(unit,
268 filename,
269 line,
270 lookup,
271 table,
272 *section,
273 *section_line,
274 strstrip(l),
275 strstrip(e),
276 relaxed,
277 userdata);
278 }
279
280 /* Go through the file and parse each line */
281 int config_parse(const char *unit,
282 const char *filename,
283 FILE *f,
284 const char *sections,
285 ConfigItemLookup lookup,
286 const void *table,
287 bool relaxed,
288 bool allow_include,
289 bool warn,
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 (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 while (!feof(f)) {
316 char l[LINE_MAX], *p, *c = NULL, *e;
317 bool escaped = false;
318
319 if (!fgets(l, sizeof(l), f)) {
320 if (feof(f))
321 break;
322
323 log_error_errno(errno, "Failed to read configuration file '%s': %m", filename);
324 return -errno;
325 }
326
327 truncate_nl(l);
328
329 if (continuation) {
330 c = strappend(continuation, l);
331 if (!c) {
332 if (warn)
333 log_oom();
334 return -ENOMEM;
335 }
336
337 continuation = mfree(continuation);
338 p = c;
339 } else
340 p = l;
341
342 for (e = p; *e; e++) {
343 if (escaped)
344 escaped = false;
345 else if (*e == '\\')
346 escaped = true;
347 }
348
349 if (escaped) {
350 *(e-1) = ' ';
351
352 if (c)
353 continuation = c;
354 else {
355 continuation = strdup(l);
356 if (!continuation) {
357 if (warn)
358 log_oom();
359 return -ENOMEM;
360 }
361 }
362
363 continue;
364 }
365
366 r = parse_line(unit,
367 filename,
368 ++line,
369 sections,
370 lookup,
371 table,
372 relaxed,
373 allow_include,
374 &section,
375 &section_line,
376 &section_ignored,
377 p,
378 userdata);
379 free(c);
380
381 if (r < 0) {
382 if (warn)
383 log_warning_errno(r, "Failed to parse file '%s': %m",
384 filename);
385 return r;
386 }
387 }
388
389 return 0;
390 }
391
392 /* Parse each config file in the specified directories. */
393 int config_parse_many(const char *conf_file,
394 const char *conf_file_dirs,
395 const char *sections,
396 ConfigItemLookup lookup,
397 const void *table,
398 bool relaxed,
399 void *userdata) {
400 _cleanup_strv_free_ char **files = NULL;
401 char **fn;
402 int r;
403
404 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
405 if (r < 0)
406 return r;
407
408 if (conf_file) {
409 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
410 if (r < 0)
411 return r;
412 }
413
414 STRV_FOREACH(fn, files) {
415 r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
416 if (r < 0)
417 return r;
418 }
419
420 return 0;
421 }
422
423 #define DEFINE_PARSER(type, vartype, conv_func) \
424 int config_parse_##type(const char *unit, \
425 const char *filename, \
426 unsigned line, \
427 const char *section, \
428 unsigned section_line, \
429 const char *lvalue, \
430 int ltype, \
431 const char *rvalue, \
432 void *data, \
433 void *userdata) { \
434 \
435 vartype *i = data; \
436 int r; \
437 \
438 assert(filename); \
439 assert(lvalue); \
440 assert(rvalue); \
441 assert(data); \
442 \
443 r = conv_func(rvalue, i); \
444 if (r < 0) \
445 log_syntax(unit, LOG_ERR, filename, line, -r, \
446 "Failed to parse %s value, ignoring: %s", \
447 #type, rvalue); \
448 \
449 return 0; \
450 }
451
452 DEFINE_PARSER(int, int, safe_atoi)
453 DEFINE_PARSER(long, long, safe_atoli)
454 DEFINE_PARSER(uint32, uint32_t, safe_atou32)
455 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
456 DEFINE_PARSER(unsigned, unsigned, safe_atou)
457 DEFINE_PARSER(double, double, safe_atod)
458 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
459 DEFINE_PARSER(sec, usec_t, parse_sec)
460
461 int config_parse_iec_size(const char* unit,
462 const char *filename,
463 unsigned line,
464 const char *section,
465 unsigned section_line,
466 const char *lvalue,
467 int ltype,
468 const char *rvalue,
469 void *data,
470 void *userdata) {
471
472 size_t *sz = data;
473 off_t o;
474 int r;
475
476 assert(filename);
477 assert(lvalue);
478 assert(rvalue);
479 assert(data);
480
481 r = parse_size(rvalue, 1024, &o);
482 if (r < 0 || (off_t) (size_t) o != o) {
483 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
484 return 0;
485 }
486
487 *sz = (size_t) o;
488 return 0;
489 }
490
491 int config_parse_si_size(const char* unit,
492 const char *filename,
493 unsigned line,
494 const char *section,
495 unsigned section_line,
496 const char *lvalue,
497 int ltype,
498 const char *rvalue,
499 void *data,
500 void *userdata) {
501
502 size_t *sz = data;
503 off_t o;
504 int r;
505
506 assert(filename);
507 assert(lvalue);
508 assert(rvalue);
509 assert(data);
510
511 r = parse_size(rvalue, 1000, &o);
512 if (r < 0 || (off_t) (size_t) o != o) {
513 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
514 return 0;
515 }
516
517 *sz = (size_t) o;
518 return 0;
519 }
520
521 int config_parse_iec_off(const char* unit,
522 const char *filename,
523 unsigned line,
524 const char *section,
525 unsigned section_line,
526 const char *lvalue,
527 int ltype,
528 const char *rvalue,
529 void *data,
530 void *userdata) {
531
532 off_t *bytes = data;
533 int r;
534
535 assert(filename);
536 assert(lvalue);
537 assert(rvalue);
538 assert(data);
539
540 assert_cc(sizeof(off_t) == sizeof(uint64_t));
541
542 r = parse_size(rvalue, 1024, bytes);
543 if (r < 0)
544 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
545
546 return 0;
547 }
548
549 int config_parse_bool(const char* unit,
550 const char *filename,
551 unsigned line,
552 const char *section,
553 unsigned section_line,
554 const char *lvalue,
555 int ltype,
556 const char *rvalue,
557 void *data,
558 void *userdata) {
559
560 int k;
561 bool *b = data;
562
563 assert(filename);
564 assert(lvalue);
565 assert(rvalue);
566 assert(data);
567
568 k = parse_boolean(rvalue);
569 if (k < 0) {
570 log_syntax(unit, LOG_ERR, filename, line, -k,
571 "Failed to parse boolean value, ignoring: %s", rvalue);
572 return 0;
573 }
574
575 *b = !!k;
576 return 0;
577 }
578
579 int config_parse_tristate(
580 const char* unit,
581 const char *filename,
582 unsigned line,
583 const char *section,
584 unsigned section_line,
585 const char *lvalue,
586 int ltype,
587 const char *rvalue,
588 void *data,
589 void *userdata) {
590
591 int k, *t = data;
592
593 assert(filename);
594 assert(lvalue);
595 assert(rvalue);
596 assert(data);
597
598 /* A tristate is pretty much a boolean, except that it can
599 * also take the special value -1, indicating "uninitialized",
600 * much like NULL is for a pointer type. */
601
602 k = parse_boolean(rvalue);
603 if (k < 0) {
604 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
605 return 0;
606 }
607
608 *t = !!k;
609 return 0;
610 }
611
612 int config_parse_string(
613 const char *unit,
614 const char *filename,
615 unsigned line,
616 const char *section,
617 unsigned section_line,
618 const char *lvalue,
619 int ltype,
620 const char *rvalue,
621 void *data,
622 void *userdata) {
623
624 char **s = data, *n;
625
626 assert(filename);
627 assert(lvalue);
628 assert(rvalue);
629 assert(data);
630
631 if (!utf8_is_valid(rvalue)) {
632 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
633 return 0;
634 }
635
636 if (isempty(rvalue))
637 n = NULL;
638 else {
639 n = strdup(rvalue);
640 if (!n)
641 return log_oom();
642 }
643
644 free(*s);
645 *s = n;
646
647 return 0;
648 }
649
650 int config_parse_path(
651 const char *unit,
652 const char *filename,
653 unsigned line,
654 const char *section,
655 unsigned section_line,
656 const char *lvalue,
657 int ltype,
658 const char *rvalue,
659 void *data,
660 void *userdata) {
661
662 char **s = data, *n;
663
664 assert(filename);
665 assert(lvalue);
666 assert(rvalue);
667 assert(data);
668
669 if (!utf8_is_valid(rvalue)) {
670 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
671 return 0;
672 }
673
674 if (!path_is_absolute(rvalue)) {
675 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
676 return 0;
677 }
678
679 n = strdup(rvalue);
680 if (!n)
681 return log_oom();
682
683 path_kill_slashes(n);
684
685 free(*s);
686 *s = n;
687
688 return 0;
689 }
690
691 int config_parse_strv(const char *unit,
692 const char *filename,
693 unsigned line,
694 const char *section,
695 unsigned section_line,
696 const char *lvalue,
697 int ltype,
698 const char *rvalue,
699 void *data,
700 void *userdata) {
701
702 char ***sv = data;
703 const char *word, *state;
704 size_t l;
705 int r;
706
707 assert(filename);
708 assert(lvalue);
709 assert(rvalue);
710 assert(data);
711
712 if (isempty(rvalue)) {
713 char **empty;
714
715 /* Empty assignment resets the list. As a special rule
716 * we actually fill in a real empty array here rather
717 * than NULL, since some code wants to know if
718 * something was set at all... */
719 empty = strv_new(NULL, NULL);
720 if (!empty)
721 return log_oom();
722
723 strv_free(*sv);
724 *sv = empty;
725 return 0;
726 }
727
728 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
729 char *n;
730
731 n = strndup(word, l);
732 if (!n)
733 return log_oom();
734
735 if (!utf8_is_valid(n)) {
736 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
737 free(n);
738 continue;
739 }
740
741 r = strv_consume(sv, n);
742 if (r < 0)
743 return log_oom();
744 }
745 if (!isempty(state))
746 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
747 "Trailing garbage, ignoring.");
748
749 return 0;
750 }
751
752 int config_parse_mode(
753 const char *unit,
754 const char *filename,
755 unsigned line,
756 const char *section,
757 unsigned section_line,
758 const char *lvalue,
759 int ltype,
760 const char *rvalue,
761 void *data,
762 void *userdata) {
763
764 mode_t *m = data;
765
766 assert(filename);
767 assert(lvalue);
768 assert(rvalue);
769 assert(data);
770
771 if (parse_mode(rvalue, m) < 0) {
772 log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
773 return 0;
774 }
775
776 return 0;
777 }
778
779 int config_parse_log_facility(
780 const char *unit,
781 const char *filename,
782 unsigned line,
783 const char *section,
784 unsigned section_line,
785 const char *lvalue,
786 int ltype,
787 const char *rvalue,
788 void *data,
789 void *userdata) {
790
791
792 int *o = data, x;
793
794 assert(filename);
795 assert(lvalue);
796 assert(rvalue);
797 assert(data);
798
799 x = log_facility_unshifted_from_string(rvalue);
800 if (x < 0) {
801 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log facility, ignoring: %s", rvalue);
802 return 0;
803 }
804
805 *o = (x << 3) | LOG_PRI(*o);
806
807 return 0;
808 }
809
810 int config_parse_log_level(
811 const char *unit,
812 const char *filename,
813 unsigned line,
814 const char *section,
815 unsigned section_line,
816 const char *lvalue,
817 int ltype,
818 const char *rvalue,
819 void *data,
820 void *userdata) {
821
822
823 int *o = data, x;
824
825 assert(filename);
826 assert(lvalue);
827 assert(rvalue);
828 assert(data);
829
830 x = log_level_from_string(rvalue);
831 if (x < 0) {
832 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log level, ignoring: %s", rvalue);
833 return 0;
834 }
835
836 *o = (*o & LOG_FACMASK) | x;
837 return 0;
838 }
839
840 int config_parse_signal(
841 const char *unit,
842 const char *filename,
843 unsigned line,
844 const char *section,
845 unsigned section_line,
846 const char *lvalue,
847 int ltype,
848 const char *rvalue,
849 void *data,
850 void *userdata) {
851
852 int *sig = data, r;
853
854 assert(filename);
855 assert(lvalue);
856 assert(rvalue);
857 assert(sig);
858
859 r = signal_from_string_try_harder(rvalue);
860 if (r <= 0) {
861 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse signal name, ignoring: %s", rvalue);
862 return 0;
863 }
864
865 *sig = r;
866 return 0;
867 }
868
869 int config_parse_personality(
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 unsigned long *personality = data, p;
882
883 assert(filename);
884 assert(lvalue);
885 assert(rvalue);
886 assert(personality);
887
888 p = personality_from_string(rvalue);
889 if (p == PERSONALITY_INVALID) {
890 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse personality, ignoring: %s", rvalue);
891 return 0;
892 }
893
894 *personality = p;
895 return 0;
896 }