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