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