]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
Merge pull request #2495 from heftig/master
[thirdparty/systemd.git] / src / shared / conf-parser.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27
28 #include "alloc-util.h"
29 #include "conf-files.h"
30 #include "conf-parser.h"
31 #include "extract-word.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 "time-util.h"
44 #include "utf8.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 }