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