]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
486122b0fd68ac9bfd35c0212aa7fc87222c1945
[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 <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "sd-messages.h"
28
29 #include "alloc-util.h"
30 #include "conf-files.h"
31 #include "conf-parser.h"
32 #include "fd-util.h"
33 #include "fs-util.h"
34 #include "log.h"
35 #include "macro.h"
36 #include "parse-util.h"
37 #include "path-util.h"
38 #include "process-util.h"
39 #include "signal-util.h"
40 #include "string-util.h"
41 #include "strv.h"
42 #include "syslog-util.h"
43 #include "utf8.h"
44 #include "util.h"
45
46 int config_item_table_lookup(
47 const void *table,
48 const char *section,
49 const char *lvalue,
50 ConfigParserCallback *func,
51 int *ltype,
52 void **data,
53 void *userdata) {
54
55 const ConfigTableItem *t;
56
57 assert(table);
58 assert(lvalue);
59 assert(func);
60 assert(ltype);
61 assert(data);
62
63 for (t = table; t->lvalue; t++) {
64
65 if (!streq(lvalue, t->lvalue))
66 continue;
67
68 if (!streq_ptr(section, t->section))
69 continue;
70
71 *func = t->parse;
72 *ltype = t->ltype;
73 *data = t->data;
74 return 1;
75 }
76
77 return 0;
78 }
79
80 int config_item_perf_lookup(
81 const void *table,
82 const char *section,
83 const char *lvalue,
84 ConfigParserCallback *func,
85 int *ltype,
86 void **data,
87 void *userdata) {
88
89 ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
90 const ConfigPerfItem *p;
91
92 assert(table);
93 assert(lvalue);
94 assert(func);
95 assert(ltype);
96 assert(data);
97
98 if (!section)
99 p = lookup(lvalue, strlen(lvalue));
100 else {
101 char *key;
102
103 key = strjoin(section, ".", lvalue, NULL);
104 if (!key)
105 return -ENOMEM;
106
107 p = lookup(key, strlen(key));
108 free(key);
109 }
110
111 if (!p)
112 return 0;
113
114 *func = p->parse;
115 *ltype = p->ltype;
116 *data = (uint8_t*) userdata + p->offset;
117 return 1;
118 }
119
120 /* Run the user supplied parser for an assignment */
121 static int next_assignment(const char *unit,
122 const char *filename,
123 unsigned line,
124 ConfigItemLookup lookup,
125 const void *table,
126 const char *section,
127 unsigned section_line,
128 const char *lvalue,
129 const char *rvalue,
130 bool relaxed,
131 void *userdata) {
132
133 ConfigParserCallback func = NULL;
134 int ltype = 0;
135 void *data = NULL;
136 int r;
137
138 assert(filename);
139 assert(line > 0);
140 assert(lookup);
141 assert(lvalue);
142 assert(rvalue);
143
144 r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
145 if (r < 0)
146 return r;
147
148 if (r > 0) {
149 if (func)
150 return func(unit, filename, line, section, section_line,
151 lvalue, ltype, rvalue, data, userdata);
152
153 return 0;
154 }
155
156 /* Warn about unknown non-extension fields. */
157 if (!relaxed && !startswith(lvalue, "X-"))
158 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
159
160 return 0;
161 }
162
163 /* Parse a variable assignment line */
164 static int parse_line(const char* unit,
165 const char *filename,
166 unsigned line,
167 const char *sections,
168 ConfigItemLookup lookup,
169 const void *table,
170 bool relaxed,
171 bool allow_include,
172 char **section,
173 unsigned *section_line,
174 bool *section_ignored,
175 char *l,
176 void *userdata) {
177
178 char *e;
179
180 assert(filename);
181 assert(line > 0);
182 assert(lookup);
183 assert(l);
184
185 l = strstrip(l);
186
187 if (!*l)
188 return 0;
189
190 if (strchr(COMMENTS "\n", *l))
191 return 0;
192
193 if (startswith(l, ".include ")) {
194 _cleanup_free_ char *fn = NULL;
195
196 /* .includes are a bad idea, we only support them here
197 * for historical reasons. They create cyclic include
198 * problems and make it difficult to detect
199 * configuration file changes with an easy
200 * stat(). Better approaches, such as .d/ drop-in
201 * snippets exist.
202 *
203 * Support for them should be eventually removed. */
204
205 if (!allow_include) {
206 log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
207 return 0;
208 }
209
210 fn = file_in_same_dir(filename, strstrip(l+9));
211 if (!fn)
212 return -ENOMEM;
213
214 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata);
215 }
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 (!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 (!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 relaxed,
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 bool relaxed,
290 bool allow_include,
291 bool warn,
292 void *userdata) {
293
294 _cleanup_free_ char *section = NULL, *continuation = NULL;
295 _cleanup_fclose_ FILE *ours = NULL;
296 unsigned line = 0, section_line = 0;
297 bool section_ignored = false;
298 int r;
299
300 assert(filename);
301 assert(lookup);
302
303 if (!f) {
304 f = ours = fopen(filename, "re");
305 if (!f) {
306 /* Only log on request, except for ENOENT,
307 * since we return 0 to the caller. */
308 if (warn || errno == ENOENT)
309 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
310 "Failed to open configuration file '%s': %m", filename);
311 return errno == ENOENT ? 0 : -errno;
312 }
313 }
314
315 fd_warn_permissions(filename, fileno(f));
316
317 while (!feof(f)) {
318 char l[LINE_MAX], *p, *c = NULL, *e;
319 bool escaped = false;
320
321 if (!fgets(l, sizeof(l), f)) {
322 if (feof(f))
323 break;
324
325 log_error_errno(errno, "Failed to read configuration file '%s': %m", filename);
326 return -errno;
327 }
328
329 truncate_nl(l);
330
331 if (continuation) {
332 c = strappend(continuation, l);
333 if (!c) {
334 if (warn)
335 log_oom();
336 return -ENOMEM;
337 }
338
339 continuation = mfree(continuation);
340 p = c;
341 } else
342 p = l;
343
344 for (e = p; *e; e++) {
345 if (escaped)
346 escaped = false;
347 else if (*e == '\\')
348 escaped = true;
349 }
350
351 if (escaped) {
352 *(e-1) = ' ';
353
354 if (c)
355 continuation = c;
356 else {
357 continuation = strdup(l);
358 if (!continuation) {
359 if (warn)
360 log_oom();
361 return -ENOMEM;
362 }
363 }
364
365 continue;
366 }
367
368 r = parse_line(unit,
369 filename,
370 ++line,
371 sections,
372 lookup,
373 table,
374 relaxed,
375 allow_include,
376 &section,
377 &section_line,
378 &section_ignored,
379 p,
380 userdata);
381 free(c);
382
383 if (r < 0) {
384 if (warn)
385 log_warning_errno(r, "Failed to parse file '%s': %m",
386 filename);
387 return r;
388 }
389 }
390
391 return 0;
392 }
393
394 /* Parse each config file in the specified directories. */
395 int config_parse_many(const char *conf_file,
396 const char *conf_file_dirs,
397 const char *sections,
398 ConfigItemLookup lookup,
399 const void *table,
400 bool relaxed,
401 void *userdata) {
402 _cleanup_strv_free_ char **files = NULL;
403 char **fn;
404 int r;
405
406 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
407 if (r < 0)
408 return r;
409
410 if (conf_file) {
411 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
412 if (r < 0)
413 return r;
414 }
415
416 STRV_FOREACH(fn, files) {
417 r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
418 if (r < 0)
419 return r;
420 }
421
422 return 0;
423 }
424
425 #define DEFINE_PARSER(type, vartype, conv_func) \
426 int config_parse_##type( \
427 const char *unit, \
428 const char *filename, \
429 unsigned line, \
430 const char *section, \
431 unsigned section_line, \
432 const char *lvalue, \
433 int ltype, \
434 const char *rvalue, \
435 void *data, \
436 void *userdata) { \
437 \
438 vartype *i = data; \
439 int r; \
440 \
441 assert(filename); \
442 assert(lvalue); \
443 assert(rvalue); \
444 assert(data); \
445 \
446 r = conv_func(rvalue, i); \
447 if (r < 0) \
448 log_syntax(unit, LOG_ERR, filename, line, r, \
449 "Failed to parse %s value, ignoring: %s", \
450 #type, rvalue); \
451 \
452 return 0; \
453 } \
454 struct __useless_struct_to_allow_trailing_semicolon__
455
456 DEFINE_PARSER(int, int, safe_atoi);
457 DEFINE_PARSER(long, long, safe_atoli);
458 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
459 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
460 DEFINE_PARSER(unsigned, unsigned, safe_atou);
461 DEFINE_PARSER(double, double, safe_atod);
462 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
463 DEFINE_PARSER(sec, usec_t, parse_sec);
464 DEFINE_PARSER(mode, mode_t, parse_mode);
465
466 int config_parse_iec_size(const char* unit,
467 const char *filename,
468 unsigned line,
469 const char *section,
470 unsigned section_line,
471 const char *lvalue,
472 int ltype,
473 const char *rvalue,
474 void *data,
475 void *userdata) {
476
477 size_t *sz = data;
478 uint64_t v;
479 int r;
480
481 assert(filename);
482 assert(lvalue);
483 assert(rvalue);
484 assert(data);
485
486 r = parse_size(rvalue, 1024, &v);
487 if (r < 0 || (uint64_t) (size_t) v != v) {
488 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
489 return 0;
490 }
491
492 *sz = (size_t) v;
493 return 0;
494 }
495
496 int config_parse_si_size(const char* unit,
497 const char *filename,
498 unsigned line,
499 const char *section,
500 unsigned section_line,
501 const char *lvalue,
502 int ltype,
503 const char *rvalue,
504 void *data,
505 void *userdata) {
506
507 size_t *sz = data;
508 uint64_t v;
509 int r;
510
511 assert(filename);
512 assert(lvalue);
513 assert(rvalue);
514 assert(data);
515
516 r = parse_size(rvalue, 1000, &v);
517 if (r < 0 || (uint64_t) (size_t) v != v) {
518 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
519 return 0;
520 }
521
522 *sz = (size_t) v;
523 return 0;
524 }
525
526 int config_parse_iec_uint64(const char* unit,
527 const char *filename,
528 unsigned line,
529 const char *section,
530 unsigned section_line,
531 const char *lvalue,
532 int ltype,
533 const char *rvalue,
534 void *data,
535 void *userdata) {
536
537 uint64_t *bytes = data;
538 int r;
539
540 assert(filename);
541 assert(lvalue);
542 assert(rvalue);
543 assert(data);
544
545 r = parse_size(rvalue, 1024, bytes);
546 if (r < 0)
547 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
548
549 return 0;
550 }
551
552 int config_parse_bool(const char* unit,
553 const char *filename,
554 unsigned line,
555 const char *section,
556 unsigned section_line,
557 const char *lvalue,
558 int ltype,
559 const char *rvalue,
560 void *data,
561 void *userdata) {
562
563 int k;
564 bool *b = data;
565
566 assert(filename);
567 assert(lvalue);
568 assert(rvalue);
569 assert(data);
570
571 k = parse_boolean(rvalue);
572 if (k < 0) {
573 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
574 return 0;
575 }
576
577 *b = !!k;
578 return 0;
579 }
580
581 int config_parse_tristate(
582 const char* unit,
583 const char *filename,
584 unsigned line,
585 const char *section,
586 unsigned section_line,
587 const char *lvalue,
588 int ltype,
589 const char *rvalue,
590 void *data,
591 void *userdata) {
592
593 int k, *t = data;
594
595 assert(filename);
596 assert(lvalue);
597 assert(rvalue);
598 assert(data);
599
600 /* A tristate is pretty much a boolean, except that it can
601 * also take the special value -1, indicating "uninitialized",
602 * much like NULL is for a pointer type. */
603
604 k = parse_boolean(rvalue);
605 if (k < 0) {
606 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
607 return 0;
608 }
609
610 *t = !!k;
611 return 0;
612 }
613
614 int config_parse_string(
615 const char *unit,
616 const char *filename,
617 unsigned line,
618 const char *section,
619 unsigned section_line,
620 const char *lvalue,
621 int ltype,
622 const char *rvalue,
623 void *data,
624 void *userdata) {
625
626 char **s = data, *n;
627
628 assert(filename);
629 assert(lvalue);
630 assert(rvalue);
631 assert(data);
632
633 if (!utf8_is_valid(rvalue)) {
634 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
635 return 0;
636 }
637
638 if (isempty(rvalue))
639 n = NULL;
640 else {
641 n = strdup(rvalue);
642 if (!n)
643 return log_oom();
644 }
645
646 free(*s);
647 *s = n;
648
649 return 0;
650 }
651
652 int config_parse_path(
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 char **s = data, *n;
665
666 assert(filename);
667 assert(lvalue);
668 assert(rvalue);
669 assert(data);
670
671 if (!utf8_is_valid(rvalue)) {
672 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
673 return 0;
674 }
675
676 if (!path_is_absolute(rvalue)) {
677 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", rvalue);
678 return 0;
679 }
680
681 n = strdup(rvalue);
682 if (!n)
683 return log_oom();
684
685 path_kill_slashes(n);
686
687 free(*s);
688 *s = n;
689
690 return 0;
691 }
692
693 int config_parse_strv(const char *unit,
694 const char *filename,
695 unsigned line,
696 const char *section,
697 unsigned section_line,
698 const char *lvalue,
699 int ltype,
700 const char *rvalue,
701 void *data,
702 void *userdata) {
703
704 char ***sv = data;
705
706 assert(filename);
707 assert(lvalue);
708 assert(rvalue);
709 assert(data);
710
711 if (isempty(rvalue)) {
712 char **empty;
713
714 /* Empty assignment resets the list. As a special rule
715 * we actually fill in a real empty array here rather
716 * than NULL, since some code wants to know if
717 * something was set at all... */
718 empty = strv_new(NULL, NULL);
719 if (!empty)
720 return log_oom();
721
722 strv_free(*sv);
723 *sv = empty;
724 return 0;
725 }
726
727 for (;;) {
728 char *word = NULL;
729 int r;
730 r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
731 if (r == 0)
732 break;
733 if (r == -ENOMEM)
734 return log_oom();
735 if (r < 0) {
736 log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
737 break;
738 }
739
740 if (!utf8_is_valid(word)) {
741 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
742 free(word);
743 continue;
744 }
745 r = strv_consume(sv, word);
746 if (r < 0)
747 return log_oom();
748 }
749
750 return 0;
751 }
752
753 int config_parse_log_facility(
754 const char *unit,
755 const char *filename,
756 unsigned line,
757 const char *section,
758 unsigned section_line,
759 const char *lvalue,
760 int ltype,
761 const char *rvalue,
762 void *data,
763 void *userdata) {
764
765
766 int *o = data, x;
767
768 assert(filename);
769 assert(lvalue);
770 assert(rvalue);
771 assert(data);
772
773 x = log_facility_unshifted_from_string(rvalue);
774 if (x < 0) {
775 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
776 return 0;
777 }
778
779 *o = (x << 3) | LOG_PRI(*o);
780
781 return 0;
782 }
783
784 int config_parse_log_level(
785 const char *unit,
786 const char *filename,
787 unsigned line,
788 const char *section,
789 unsigned section_line,
790 const char *lvalue,
791 int ltype,
792 const char *rvalue,
793 void *data,
794 void *userdata) {
795
796
797 int *o = data, x;
798
799 assert(filename);
800 assert(lvalue);
801 assert(rvalue);
802 assert(data);
803
804 x = log_level_from_string(rvalue);
805 if (x < 0) {
806 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
807 return 0;
808 }
809
810 *o = (*o & LOG_FACMASK) | x;
811 return 0;
812 }
813
814 int config_parse_signal(
815 const char *unit,
816 const char *filename,
817 unsigned line,
818 const char *section,
819 unsigned section_line,
820 const char *lvalue,
821 int ltype,
822 const char *rvalue,
823 void *data,
824 void *userdata) {
825
826 int *sig = data, r;
827
828 assert(filename);
829 assert(lvalue);
830 assert(rvalue);
831 assert(sig);
832
833 r = signal_from_string_try_harder(rvalue);
834 if (r <= 0) {
835 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
836 return 0;
837 }
838
839 *sig = r;
840 return 0;
841 }
842
843 int config_parse_personality(
844 const char *unit,
845 const char *filename,
846 unsigned line,
847 const char *section,
848 unsigned section_line,
849 const char *lvalue,
850 int ltype,
851 const char *rvalue,
852 void *data,
853 void *userdata) {
854
855 unsigned long *personality = data, p;
856
857 assert(filename);
858 assert(lvalue);
859 assert(rvalue);
860 assert(personality);
861
862 p = personality_from_string(rvalue);
863 if (p == PERSONALITY_INVALID) {
864 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
865 return 0;
866 }
867
868 *personality = p;
869 return 0;
870 }