]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
Merge pull request #5 from systemd-mailing-devs/1432910411-14517-1-git-send-email...
[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 free(continuation);
337 continuation = NULL;
338 p = c;
339 } else
340 p = l;
341
342 for (e = p; *e; e++) {
343 if (escaped)
344 escaped = false;
345 else if (*e == '\\')
346 escaped = true;
347 }
348
349 if (escaped) {
350 *(e-1) = ' ';
351
352 if (c)
353 continuation = c;
354 else {
355 continuation = strdup(l);
356 if (!continuation) {
357 if (warn)
358 log_oom();
359 return -ENOMEM;
360 }
361 }
362
363 continue;
364 }
365
366 r = parse_line(unit,
367 filename,
368 ++line,
369 sections,
370 lookup,
371 table,
372 relaxed,
373 allow_include,
374 &section,
375 &section_line,
376 &section_ignored,
377 p,
378 userdata);
379 free(c);
380
381 if (r < 0) {
382 if (warn)
383 log_warning_errno(r, "Failed to parse file '%s': %m",
384 filename);
385 return r;
386 }
387 }
388
389 return 0;
390 }
391
392 /* Parse each config file in the specified directories. */
393 int config_parse_many(const char *conf_file,
394 const char *conf_file_dirs,
395 const char *sections,
396 ConfigItemLookup lookup,
397 const void *table,
398 bool relaxed,
399 void *userdata) {
400 _cleanup_strv_free_ char **files = NULL;
401 char **fn;
402 int r;
403
404 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
405 if (r < 0)
406 return r;
407
408 if (conf_file) {
409 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
410 if (r < 0)
411 return r;
412 }
413
414 STRV_FOREACH(fn, files) {
415 r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
416 if (r < 0)
417 return r;
418 }
419
420 return 0;
421 }
422
423 #define DEFINE_PARSER(type, vartype, conv_func) \
424 int config_parse_##type(const char *unit, \
425 const char *filename, \
426 unsigned line, \
427 const char *section, \
428 unsigned section_line, \
429 const char *lvalue, \
430 int ltype, \
431 const char *rvalue, \
432 void *data, \
433 void *userdata) { \
434 \
435 vartype *i = data; \
436 int r; \
437 \
438 assert(filename); \
439 assert(lvalue); \
440 assert(rvalue); \
441 assert(data); \
442 \
443 r = conv_func(rvalue, i); \
444 if (r < 0) \
445 log_syntax(unit, LOG_ERR, filename, line, -r, \
446 "Failed to parse %s value, ignoring: %s", \
447 #type, rvalue); \
448 \
449 return 0; \
450 }
451
452 DEFINE_PARSER(int, int, safe_atoi)
453 DEFINE_PARSER(long, long, safe_atoli)
454 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
455 DEFINE_PARSER(unsigned, unsigned, safe_atou)
456 DEFINE_PARSER(double, double, safe_atod)
457 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
458 DEFINE_PARSER(sec, usec_t, parse_sec)
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 off_t o;
473 int r;
474
475 assert(filename);
476 assert(lvalue);
477 assert(rvalue);
478 assert(data);
479
480 r = parse_size(rvalue, 1024, &o);
481 if (r < 0 || (off_t) (size_t) o != o) {
482 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
483 return 0;
484 }
485
486 *sz = (size_t) o;
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 off_t o;
503 int r;
504
505 assert(filename);
506 assert(lvalue);
507 assert(rvalue);
508 assert(data);
509
510 r = parse_size(rvalue, 1000, &o);
511 if (r < 0 || (off_t) (size_t) o != o) {
512 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
513 return 0;
514 }
515
516 *sz = (size_t) o;
517 return 0;
518 }
519
520 int config_parse_iec_off(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 off_t *bytes = data;
532 int r;
533
534 assert(filename);
535 assert(lvalue);
536 assert(rvalue);
537 assert(data);
538
539 assert_cc(sizeof(off_t) == sizeof(uint64_t));
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,
570 "Failed to parse boolean value, ignoring: %s", rvalue);
571 return 0;
572 }
573
574 *b = !!k;
575 return 0;
576 }
577
578 int config_parse_string(
579 const char *unit,
580 const char *filename,
581 unsigned line,
582 const char *section,
583 unsigned section_line,
584 const char *lvalue,
585 int ltype,
586 const char *rvalue,
587 void *data,
588 void *userdata) {
589
590 char **s = data, *n;
591
592 assert(filename);
593 assert(lvalue);
594 assert(rvalue);
595 assert(data);
596
597 if (!utf8_is_valid(rvalue)) {
598 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
599 return 0;
600 }
601
602 if (isempty(rvalue))
603 n = NULL;
604 else {
605 n = strdup(rvalue);
606 if (!n)
607 return log_oom();
608 }
609
610 free(*s);
611 *s = n;
612
613 return 0;
614 }
615
616 int config_parse_path(
617 const char *unit,
618 const char *filename,
619 unsigned line,
620 const char *section,
621 unsigned section_line,
622 const char *lvalue,
623 int ltype,
624 const char *rvalue,
625 void *data,
626 void *userdata) {
627
628 char **s = data, *n;
629
630 assert(filename);
631 assert(lvalue);
632 assert(rvalue);
633 assert(data);
634
635 if (!utf8_is_valid(rvalue)) {
636 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
637 return 0;
638 }
639
640 if (!path_is_absolute(rvalue)) {
641 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
642 return 0;
643 }
644
645 n = strdup(rvalue);
646 if (!n)
647 return log_oom();
648
649 path_kill_slashes(n);
650
651 free(*s);
652 *s = n;
653
654 return 0;
655 }
656
657 int config_parse_strv(const char *unit,
658 const char *filename,
659 unsigned line,
660 const char *section,
661 unsigned section_line,
662 const char *lvalue,
663 int ltype,
664 const char *rvalue,
665 void *data,
666 void *userdata) {
667
668 char ***sv = data;
669 const char *word, *state;
670 size_t l;
671 int r;
672
673 assert(filename);
674 assert(lvalue);
675 assert(rvalue);
676 assert(data);
677
678 if (isempty(rvalue)) {
679 char **empty;
680
681 /* Empty assignment resets the list. As a special rule
682 * we actually fill in a real empty array here rather
683 * than NULL, since some code wants to know if
684 * something was set at all... */
685 empty = strv_new(NULL, NULL);
686 if (!empty)
687 return log_oom();
688
689 strv_free(*sv);
690 *sv = empty;
691 return 0;
692 }
693
694 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
695 char *n;
696
697 n = strndup(word, l);
698 if (!n)
699 return log_oom();
700
701 if (!utf8_is_valid(n)) {
702 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
703 free(n);
704 continue;
705 }
706
707 r = strv_consume(sv, n);
708 if (r < 0)
709 return log_oom();
710 }
711 if (!isempty(state))
712 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
713 "Trailing garbage, ignoring.");
714
715 return 0;
716 }
717
718 int config_parse_mode(
719 const char *unit,
720 const char *filename,
721 unsigned line,
722 const char *section,
723 unsigned section_line,
724 const char *lvalue,
725 int ltype,
726 const char *rvalue,
727 void *data,
728 void *userdata) {
729
730 mode_t *m = data;
731
732 assert(filename);
733 assert(lvalue);
734 assert(rvalue);
735 assert(data);
736
737 if (parse_mode(rvalue, m) < 0) {
738 log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
739 return 0;
740 }
741
742 return 0;
743 }
744
745 int config_parse_log_facility(
746 const char *unit,
747 const char *filename,
748 unsigned line,
749 const char *section,
750 unsigned section_line,
751 const char *lvalue,
752 int ltype,
753 const char *rvalue,
754 void *data,
755 void *userdata) {
756
757
758 int *o = data, x;
759
760 assert(filename);
761 assert(lvalue);
762 assert(rvalue);
763 assert(data);
764
765 x = log_facility_unshifted_from_string(rvalue);
766 if (x < 0) {
767 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log facility, ignoring: %s", rvalue);
768 return 0;
769 }
770
771 *o = (x << 3) | LOG_PRI(*o);
772
773 return 0;
774 }
775
776 int config_parse_log_level(
777 const char *unit,
778 const char *filename,
779 unsigned line,
780 const char *section,
781 unsigned section_line,
782 const char *lvalue,
783 int ltype,
784 const char *rvalue,
785 void *data,
786 void *userdata) {
787
788
789 int *o = data, x;
790
791 assert(filename);
792 assert(lvalue);
793 assert(rvalue);
794 assert(data);
795
796 x = log_level_from_string(rvalue);
797 if (x < 0) {
798 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log level, ignoring: %s", rvalue);
799 return 0;
800 }
801
802 *o = (*o & LOG_FACMASK) | x;
803 return 0;
804 }