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