]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
conf-parser: when we parse a string list, always fill in something
[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 *section = NULL;
266 int r;
267 bool ours = false;
268 char *continuation = NULL;
269
270 assert(filename);
271 assert(lookup);
272
273 if (!f) {
274 f = fopen(filename, "re");
275 if (!f) {
276 r = -errno;
277 log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
278 goto finish;
279 }
280
281 ours = true;
282 }
283
284 while (!feof(f)) {
285 char l[LINE_MAX], *p, *c = NULL, *e;
286 bool escaped = false;
287
288 if (!fgets(l, sizeof(l), f)) {
289 if (feof(f))
290 break;
291
292 r = -errno;
293 log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
294 goto finish;
295 }
296
297 truncate_nl(l);
298
299 if (continuation) {
300 c = strappend(continuation, l);
301 if (!c) {
302 r = -ENOMEM;
303 goto finish;
304 }
305
306 free(continuation);
307 continuation = NULL;
308 p = c;
309 } else
310 p = l;
311
312 for (e = p; *e; e++) {
313 if (escaped)
314 escaped = false;
315 else if (*e == '\\')
316 escaped = true;
317 }
318
319 if (escaped) {
320 *(e-1) = ' ';
321
322 if (c)
323 continuation = c;
324 else {
325 continuation = strdup(l);
326 if (!continuation) {
327 r = -ENOMEM;
328 goto finish;
329 }
330 }
331
332 continue;
333 }
334
335 r = parse_line(filename,
336 ++line,
337 sections,
338 lookup,
339 table,
340 relaxed,
341 &section,
342 p,
343 userdata);
344 free(c);
345
346 if (r < 0)
347 goto finish;
348 }
349
350 r = 0;
351
352 finish:
353 free(section);
354 free(continuation);
355
356 if (f && ours)
357 fclose(f);
358
359 return r;
360 }
361
362 int config_parse_int(
363 const char *filename,
364 unsigned line,
365 const char *section,
366 const char *lvalue,
367 int ltype,
368 const char *rvalue,
369 void *data,
370 void *userdata) {
371
372 int *i = data;
373 int r;
374
375 assert(filename);
376 assert(lvalue);
377 assert(rvalue);
378 assert(data);
379
380 r = safe_atoi(rvalue, i);
381 if (r < 0) {
382 log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
383 return 0;
384 }
385
386 return 0;
387 }
388
389 int config_parse_long(
390 const char *filename,
391 unsigned line,
392 const char *section,
393 const char *lvalue,
394 int ltype,
395 const char *rvalue,
396 void *data,
397 void *userdata) {
398
399 long *i = data;
400 int r;
401
402 assert(filename);
403 assert(lvalue);
404 assert(rvalue);
405 assert(data);
406
407 r = safe_atoli(rvalue, i);
408 if (r < 0) {
409 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
410 return 0;
411 }
412
413 return 0;
414 }
415
416 int config_parse_uint64(
417 const char *filename,
418 unsigned line,
419 const char *section,
420 const char *lvalue,
421 int ltype,
422 const char *rvalue,
423 void *data,
424 void *userdata) {
425
426 uint64_t *u = data;
427 int r;
428
429 assert(filename);
430 assert(lvalue);
431 assert(rvalue);
432 assert(data);
433
434 r = safe_atou64(rvalue, u);
435 if (r < 0) {
436 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
437 return 0;
438 }
439
440 return 0;
441 }
442
443 int config_parse_unsigned(
444 const char *filename,
445 unsigned line,
446 const char *section,
447 const char *lvalue,
448 int ltype,
449 const char *rvalue,
450 void *data,
451 void *userdata) {
452
453 unsigned *u = data;
454 int r;
455
456 assert(filename);
457 assert(lvalue);
458 assert(rvalue);
459 assert(data);
460
461 r = safe_atou(rvalue, u);
462 if (r < 0) {
463 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
464 return r;
465 }
466
467 return 0;
468 }
469
470 int config_parse_double(
471 const char *filename,
472 unsigned line,
473 const char *section,
474 const char *lvalue,
475 int ltype,
476 const char *rvalue,
477 void *data,
478 void *userdata) {
479
480 double *d = data;
481 int r;
482
483 assert(filename);
484 assert(lvalue);
485 assert(rvalue);
486 assert(data);
487
488 r = safe_atod(rvalue, d);
489 if (r < 0) {
490 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
491 return r;
492 }
493
494 return 0;
495 }
496
497 int config_parse_bytes_size(
498 const char *filename,
499 unsigned line,
500 const char *section,
501 const char *lvalue,
502 int ltype,
503 const char *rvalue,
504 void *data,
505 void *userdata) {
506
507 size_t *sz = data;
508 off_t o;
509
510 assert(filename);
511 assert(lvalue);
512 assert(rvalue);
513 assert(data);
514
515 if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
516 log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
517 return 0;
518 }
519
520 *sz = (size_t) o;
521 return 0;
522 }
523
524
525 int config_parse_bytes_off(
526 const char *filename,
527 unsigned line,
528 const char *section,
529 const char *lvalue,
530 int ltype,
531 const char *rvalue,
532 void *data,
533 void *userdata) {
534
535 off_t *bytes = data;
536
537 assert(filename);
538 assert(lvalue);
539 assert(rvalue);
540 assert(data);
541
542 assert_cc(sizeof(off_t) == sizeof(uint64_t));
543
544 if (parse_bytes(rvalue, bytes) < 0) {
545 log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
546 return 0;
547 }
548
549 return 0;
550 }
551
552 int config_parse_bool(
553 const char *filename,
554 unsigned line,
555 const char *section,
556 const char *lvalue,
557 int ltype,
558 const char *rvalue,
559 void *data,
560 void *userdata) {
561
562 int k;
563 bool *b = data;
564
565 assert(filename);
566 assert(lvalue);
567 assert(rvalue);
568 assert(data);
569
570 if ((k = parse_boolean(rvalue)) < 0) {
571 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
572 return 0;
573 }
574
575 *b = !!k;
576 return 0;
577 }
578
579 int config_parse_tristate(
580 const char *filename,
581 unsigned line,
582 const char *section,
583 const char *lvalue,
584 int ltype,
585 const char *rvalue,
586 void *data,
587 void *userdata) {
588
589 int k;
590 int *b = data;
591
592 assert(filename);
593 assert(lvalue);
594 assert(rvalue);
595 assert(data);
596
597 /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
598
599 k = parse_boolean(rvalue);
600 if (k < 0) {
601 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
602 return 0;
603 }
604
605 *b = !!k;
606 return 0;
607 }
608
609 int config_parse_string(
610 const char *filename,
611 unsigned line,
612 const char *section,
613 const char *lvalue,
614 int ltype,
615 const char *rvalue,
616 void *data,
617 void *userdata) {
618
619 char **s = data;
620 char *n;
621
622 assert(filename);
623 assert(lvalue);
624 assert(rvalue);
625 assert(data);
626
627 n = strdup(rvalue);
628 if (!n)
629 return log_oom();
630
631 if (!utf8_is_valid(n)) {
632 log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
633 free(n);
634 return 0;
635 }
636
637 free(*s);
638 if (*n)
639 *s = n;
640 else {
641 free(n);
642 *s = NULL;
643 }
644
645 return 0;
646 }
647
648 int config_parse_path(
649 const char *filename,
650 unsigned line,
651 const char *section,
652 const char *lvalue,
653 int ltype,
654 const char *rvalue,
655 void *data,
656 void *userdata) {
657
658 char **s = data;
659 char *n;
660
661 assert(filename);
662 assert(lvalue);
663 assert(rvalue);
664 assert(data);
665
666 if (!utf8_is_valid(rvalue)) {
667 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
668 return 0;
669 }
670
671 if (!path_is_absolute(rvalue)) {
672 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
673 return 0;
674 }
675
676 n = strdup(rvalue);
677 if (!n)
678 return log_oom();
679
680 path_kill_slashes(n);
681
682 free(*s);
683 *s = n;
684
685 return 0;
686 }
687
688 int config_parse_strv(
689 const char *filename,
690 unsigned line,
691 const char *section,
692 const char *lvalue,
693 int ltype,
694 const char *rvalue,
695 void *data,
696 void *userdata) {
697
698 char *** sv = data, *w, *state;
699 size_t l;
700 int r;
701
702 assert(filename);
703 assert(lvalue);
704 assert(rvalue);
705 assert(data);
706
707 if (isempty(rvalue)) {
708 char **empty;
709
710 /* Empty assignment resets the list. As a special rule
711 * we actually fill in a real empty array here rather
712 * than NULL, since some code wants to know if
713 * something was set at all... */
714 empty = strv_new(NULL, NULL);
715 if (!empty)
716 return log_oom();
717
718 strv_free(*sv);
719 *sv = empty;
720 return 0;
721 }
722
723 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
724 _cleanup_free_ char *n;
725
726 n = cunescape_length(w, l);
727 if (!n)
728 return log_oom();
729
730 if (!utf8_is_valid(n)) {
731 log_error("[%s:%u] String is not UTF-8 clean, ignoring: %s", filename, line, rvalue);
732 continue;
733 }
734
735 r = strv_extend(sv, n);
736 if (r < 0)
737 return log_oom();
738 }
739
740 return 0;
741 }
742
743 int config_parse_path_strv(
744 const char *filename,
745 unsigned line,
746 const char *section,
747 const char *lvalue,
748 int ltype,
749 const char *rvalue,
750 void *data,
751 void *userdata) {
752
753 char*** sv = data, *w, *state;
754 size_t l;
755 int r;
756
757 assert(filename);
758 assert(lvalue);
759 assert(rvalue);
760 assert(data);
761
762 if (isempty(rvalue)) {
763 /* Empty assignment resets the list */
764 strv_free(*sv);
765 *sv = NULL;
766 return 0;
767 }
768
769 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
770 _cleanup_free_ char *n;
771
772 n = strndup(w, l);
773 if (!n)
774 return log_oom();
775
776 if (!utf8_is_valid(n)) {
777 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
778 continue;
779 }
780
781 if (!path_is_absolute(n)) {
782 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
783 continue;
784 }
785
786 path_kill_slashes(n);
787 r = strv_extend(sv, n);
788 if (r < 0)
789 return log_oom();
790 }
791
792 return 0;
793 }
794
795 int config_parse_usec(
796 const char *filename,
797 unsigned line,
798 const char *section,
799 const char *lvalue,
800 int ltype,
801 const char *rvalue,
802 void *data,
803 void *userdata) {
804
805 usec_t *usec = data;
806
807 assert(filename);
808 assert(lvalue);
809 assert(rvalue);
810 assert(data);
811
812 if (parse_usec(rvalue, usec) < 0) {
813 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
814 return 0;
815 }
816
817 return 0;
818 }
819
820 int config_parse_nsec(
821 const char *filename,
822 unsigned line,
823 const char *section,
824 const char *lvalue,
825 int ltype,
826 const char *rvalue,
827 void *data,
828 void *userdata) {
829
830 nsec_t *nsec = data;
831
832 assert(filename);
833 assert(lvalue);
834 assert(rvalue);
835 assert(data);
836
837 if (parse_nsec(rvalue, nsec) < 0) {
838 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
839 return 0;
840 }
841
842 return 0;
843 }
844
845 int config_parse_mode(
846 const char *filename,
847 unsigned line,
848 const char *section,
849 const char *lvalue,
850 int ltype,
851 const char *rvalue,
852 void *data,
853 void *userdata) {
854
855 mode_t *m = data;
856 long l;
857 char *x = NULL;
858
859 assert(filename);
860 assert(lvalue);
861 assert(rvalue);
862 assert(data);
863
864 errno = 0;
865 l = strtol(rvalue, &x, 8);
866 if (!x || x == rvalue || *x || errno) {
867 log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
868 return 0;
869 }
870
871 if (l < 0000 || l > 07777) {
872 log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
873 return 0;
874 }
875
876 *m = (mode_t) l;
877 return 0;
878 }
879
880 int config_parse_facility(
881 const char *filename,
882 unsigned line,
883 const char *section,
884 const char *lvalue,
885 int ltype,
886 const char *rvalue,
887 void *data,
888 void *userdata) {
889
890
891 int *o = data, x;
892
893 assert(filename);
894 assert(lvalue);
895 assert(rvalue);
896 assert(data);
897
898 x = log_facility_unshifted_from_string(rvalue);
899 if (x < 0) {
900 log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
901 return 0;
902 }
903
904 *o = (x << 3) | LOG_PRI(*o);
905
906 return 0;
907 }
908
909 int config_parse_level(
910 const char *filename,
911 unsigned line,
912 const char *section,
913 const char *lvalue,
914 int ltype,
915 const char *rvalue,
916 void *data,
917 void *userdata) {
918
919
920 int *o = data, x;
921
922 assert(filename);
923 assert(lvalue);
924 assert(rvalue);
925 assert(data);
926
927 x = log_level_from_string(rvalue);
928 if (x < 0) {
929 log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
930 return 0;
931 }
932
933 *o = (*o & LOG_FACMASK) | x;
934 return 0;
935 }
936
937 int config_parse_set_status(
938 const char *filename,
939 unsigned line,
940 const char *section,
941 const char *lvalue,
942 int ltype,
943 const char *rvalue,
944 void *data,
945 void *userdata) {
946
947 char *w;
948 size_t l;
949 char *state;
950 int r;
951 ExitStatusSet *status_set = data;
952
953 assert(filename);
954 assert(lvalue);
955 assert(rvalue);
956 assert(data);
957
958 if (isempty(rvalue)) {
959 /* Empty assignment resets the list */
960
961 set_free(status_set->signal);
962 set_free(status_set->code);
963
964 status_set->signal = status_set->code = NULL;
965 return 0;
966 }
967
968 FOREACH_WORD(w, l, rvalue, state) {
969 int val;
970 char *temp;
971
972 temp = strndup(w, l);
973 if (!temp)
974 return log_oom();
975
976 r = safe_atoi(temp, &val);
977 if (r < 0) {
978 val = signal_from_string_try_harder(temp);
979 free(temp);
980
981 if (val > 0) {
982 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
983 if (r < 0)
984 return log_oom();
985
986 r = set_put(status_set->signal, INT_TO_PTR(val));
987 if (r < 0) {
988 log_error("[%s:%u] Unable to store: %s", filename, line, w);
989 return r;
990 }
991 } else {
992 log_error("[%s:%u] Failed to parse value, ignoring: %s", filename, line, w);
993 return 0;
994 }
995 } else {
996 free(temp);
997
998 if (val < 0 || val > 255)
999 log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
1000 else {
1001 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
1002 if (r < 0)
1003 return log_oom();
1004
1005 r = set_put(status_set->code, INT_TO_PTR(val));
1006 if (r < 0) {
1007 log_error("[%s:%u] Unable to store: %s", filename, line, w);
1008 return r;
1009 }
1010 }
1011 }
1012 }
1013
1014 return 0;
1015 }