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