dbus: minor coding style fixes
[systemd_ALT/systemd_imz.git] / src / shared / conf-parser.c
blob68ab80470e5348ae0d7c0cc160edb74f136324d0
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3 /***
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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 ***/
22 #include <string.h>
23 #include <stdio.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <stdlib.h>
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"
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) {
47 ConfigTableItem *t;
49 assert(table);
50 assert(lvalue);
51 assert(func);
52 assert(ltype);
53 assert(data);
55 for (t = table; t->lvalue; t++) {
57 if (!streq(lvalue, t->lvalue))
58 continue;
60 if (!streq_ptr(section, t->section))
61 continue;
63 *func = t->parse;
64 *ltype = t->ltype;
65 *data = t->data;
66 return 1;
69 return 0;
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) {
81 ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
82 const ConfigPerfItem *p;
84 assert(table);
85 assert(lvalue);
86 assert(func);
87 assert(ltype);
88 assert(data);
90 if (!section)
91 p = lookup(lvalue, strlen(lvalue));
92 else {
93 char *key;
95 key = strjoin(section, ".", lvalue, NULL);
96 if (!key)
97 return -ENOMEM;
99 p = lookup(key, strlen(key));
100 free(key);
103 if (!p)
104 return 0;
106 *func = p->parse;
107 *ltype = p->ltype;
108 *data = (uint8_t*) userdata + p->offset;
109 return 1;
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) {
124 ConfigParserCallback func = NULL;
125 int ltype = 0;
126 void *data = NULL;
127 int r;
129 assert(filename);
130 assert(line > 0);
131 assert(lookup);
132 assert(lvalue);
133 assert(rvalue);
135 r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
136 if (r < 0)
137 return r;
139 if (r > 0) {
140 if (func)
141 return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
143 return 0;
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);
150 return 0;
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) {
165 char *e;
167 assert(filename);
168 assert(line > 0);
169 assert(lookup);
170 assert(l);
172 l = strstrip(l);
174 if (!*l)
175 return 0;
177 if (strchr(COMMENTS, *l))
178 return 0;
180 if (startswith(l, ".include ")) {
181 char *fn;
182 int r;
184 fn = file_in_same_dir(filename, strstrip(l+9));
185 if (!fn)
186 return -ENOMEM;
188 r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
189 free(fn);
191 return r;
194 if (*l == '[') {
195 size_t k;
196 char *n;
198 k = strlen(l);
199 assert(k > 0);
201 if (l[k-1] != ']') {
202 log_error("[%s:%u] Invalid section header.", filename, line);
203 return -EBADMSG;
206 n = strndup(l+1, k-2);
207 if (!n)
208 return -ENOMEM;
210 if (sections && !nulstr_contains(sections, n)) {
212 if (!relaxed)
213 log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
215 free(n);
216 *section = NULL;
217 } else {
218 free(*section);
219 *section = n;
222 return 0;
225 if (sections && !*section) {
227 if (!relaxed)
228 log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line);
230 return 0;
233 e = strchr(l, '=');
234 if (!e) {
235 log_error("[%s:%u] Missing '='.", filename, line);
236 return -EBADMSG;
239 *e = 0;
240 e++;
242 return next_assignment(
243 filename,
244 line,
245 lookup,
246 table,
247 *section,
248 strstrip(l),
249 strstrip(e),
250 relaxed,
251 userdata);
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) {
264 unsigned line = 0;
265 char *section = NULL;
266 int r;
267 bool ours = false;
268 char *continuation = NULL;
270 assert(filename);
271 assert(lookup);
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;
281 ours = true;
284 while (!feof(f)) {
285 char l[LINE_MAX], *p, *c = NULL, *e;
286 bool escaped = false;
288 if (!fgets(l, sizeof(l), f)) {
289 if (feof(f))
290 break;
292 r = -errno;
293 log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
294 goto finish;
297 truncate_nl(l);
299 if (continuation) {
300 c = strappend(continuation, l);
301 if (!c) {
302 r = -ENOMEM;
303 goto finish;
306 free(continuation);
307 continuation = NULL;
308 p = c;
309 } else
310 p = l;
312 for (e = p; *e; e++) {
313 if (escaped)
314 escaped = false;
315 else if (*e == '\\')
316 escaped = true;
319 if (escaped) {
320 *(e-1) = ' ';
322 if (c)
323 continuation = c;
324 else {
325 continuation = strdup(l);
326 if (!continuation) {
327 r = -ENOMEM;
328 goto finish;
332 continue;
335 r = parse_line(filename,
336 ++line,
337 sections,
338 lookup,
339 table,
340 relaxed,
341 &section,
343 userdata);
344 free(c);
346 if (r < 0)
347 goto finish;
350 r = 0;
352 finish:
353 free(section);
354 free(continuation);
356 if (f && ours)
357 fclose(f);
359 return r;
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) {
372 int *i = data;
373 int r;
375 assert(filename);
376 assert(lvalue);
377 assert(rvalue);
378 assert(data);
380 if ((r = safe_atoi(rvalue, i)) < 0) {
381 log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
382 return 0;
385 return 0;
388 int config_parse_long(
389 const char *filename,
390 unsigned line,
391 const char *section,
392 const char *lvalue,
393 int ltype,
394 const char *rvalue,
395 void *data,
396 void *userdata) {
398 long *i = data;
399 int r;
401 assert(filename);
402 assert(lvalue);
403 assert(rvalue);
404 assert(data);
406 if ((r = safe_atoli(rvalue, i)) < 0) {
407 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
408 return 0;
411 return 0;
414 int config_parse_uint64(
415 const char *filename,
416 unsigned line,
417 const char *section,
418 const char *lvalue,
419 int ltype,
420 const char *rvalue,
421 void *data,
422 void *userdata) {
424 uint64_t *u = data;
425 int r;
427 assert(filename);
428 assert(lvalue);
429 assert(rvalue);
430 assert(data);
432 if ((r = safe_atou64(rvalue, u)) < 0) {
433 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
434 return 0;
437 return 0;
440 int config_parse_unsigned(
441 const char *filename,
442 unsigned line,
443 const char *section,
444 const char *lvalue,
445 int ltype,
446 const char *rvalue,
447 void *data,
448 void *userdata) {
450 unsigned *u = data;
451 int r;
453 assert(filename);
454 assert(lvalue);
455 assert(rvalue);
456 assert(data);
458 if ((r = safe_atou(rvalue, u)) < 0) {
459 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
460 return r;
463 return 0;
466 int config_parse_bytes_size(
467 const char *filename,
468 unsigned line,
469 const char *section,
470 const char *lvalue,
471 int ltype,
472 const char *rvalue,
473 void *data,
474 void *userdata) {
476 size_t *sz = data;
477 off_t o;
479 assert(filename);
480 assert(lvalue);
481 assert(rvalue);
482 assert(data);
484 if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
485 log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
486 return 0;
489 *sz = (size_t) o;
490 return 0;
494 int config_parse_bytes_off(
495 const char *filename,
496 unsigned line,
497 const char *section,
498 const char *lvalue,
499 int ltype,
500 const char *rvalue,
501 void *data,
502 void *userdata) {
504 off_t *bytes = data;
506 assert(filename);
507 assert(lvalue);
508 assert(rvalue);
509 assert(data);
511 assert_cc(sizeof(off_t) == sizeof(uint64_t));
513 if (parse_bytes(rvalue, bytes) < 0) {
514 log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
515 return 0;
518 return 0;
521 int config_parse_bool(
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) {
531 int k;
532 bool *b = data;
534 assert(filename);
535 assert(lvalue);
536 assert(rvalue);
537 assert(data);
539 if ((k = parse_boolean(rvalue)) < 0) {
540 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
541 return 0;
544 *b = !!k;
545 return 0;
548 int config_parse_tristate(
549 const char *filename,
550 unsigned line,
551 const char *section,
552 const char *lvalue,
553 int ltype,
554 const char *rvalue,
555 void *data,
556 void *userdata) {
558 int k;
559 int *b = data;
561 assert(filename);
562 assert(lvalue);
563 assert(rvalue);
564 assert(data);
566 /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
568 k = parse_boolean(rvalue);
569 if (k < 0) {
570 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
571 return 0;
574 *b = !!k;
575 return 0;
578 int config_parse_string(
579 const char *filename,
580 unsigned line,
581 const char *section,
582 const char *lvalue,
583 int ltype,
584 const char *rvalue,
585 void *data,
586 void *userdata) {
588 char **s = data;
589 char *n;
591 assert(filename);
592 assert(lvalue);
593 assert(rvalue);
594 assert(data);
596 n = cunescape(rvalue);
597 if (!n)
598 return -ENOMEM;
600 if (!utf8_is_valid(n)) {
601 log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
602 free(n);
603 return 0;
606 free(*s);
607 if (*n)
608 *s = n;
609 else {
610 free(n);
611 *s = NULL;
614 return 0;
617 int config_parse_path(
618 const char *filename,
619 unsigned line,
620 const char *section,
621 const char *lvalue,
622 int ltype,
623 const char *rvalue,
624 void *data,
625 void *userdata) {
627 char **s = data;
628 char *n;
630 assert(filename);
631 assert(lvalue);
632 assert(rvalue);
633 assert(data);
635 if (!utf8_is_valid(rvalue)) {
636 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
637 return 0;
640 if (!path_is_absolute(rvalue)) {
641 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
642 return 0;
645 n = strdup(rvalue);
646 if (!n)
647 return -ENOMEM;
649 path_kill_slashes(n);
651 free(*s);
652 *s = n;
654 return 0;
657 int config_parse_strv(
658 const char *filename,
659 unsigned line,
660 const char *section,
661 const char *lvalue,
662 int ltype,
663 const char *rvalue,
664 void *data,
665 void *userdata) {
667 char*** sv = data;
668 char **n;
669 char *w;
670 unsigned k;
671 size_t l;
672 char *state;
673 int r;
675 assert(filename);
676 assert(lvalue);
677 assert(rvalue);
678 assert(data);
680 k = strv_length(*sv);
681 FOREACH_WORD_QUOTED(w, l, rvalue, state)
682 k++;
684 n = new(char*, k+1);
685 if (!n)
686 return -ENOMEM;
688 if (*sv)
689 for (k = 0; (*sv)[k]; k++)
690 n[k] = (*sv)[k];
691 else
692 k = 0;
694 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
695 n[k] = cunescape_length(w, l);
696 if (!n[k]) {
697 r = -ENOMEM;
698 goto fail;
701 if (!utf8_is_valid(n[k])) {
702 log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
703 free(n[k]);
704 continue;
707 k++;
710 n[k] = NULL;
711 free(*sv);
712 *sv = n;
714 return 0;
716 fail:
717 for (; k > 0; k--)
718 free(n[k-1]);
719 free(n);
721 return r;
724 int config_parse_path_strv(
725 const char *filename,
726 unsigned line,
727 const char *section,
728 const char *lvalue,
729 int ltype,
730 const char *rvalue,
731 void *data,
732 void *userdata) {
734 char*** sv = data;
735 char **n;
736 char *w;
737 unsigned k;
738 size_t l;
739 char *state;
740 int r;
742 assert(filename);
743 assert(lvalue);
744 assert(rvalue);
745 assert(data);
747 k = strv_length(*sv);
748 FOREACH_WORD_QUOTED(w, l, rvalue, state)
749 k++;
751 n = new(char*, k+1);
752 if (!n)
753 return -ENOMEM;
755 k = 0;
756 if (*sv)
757 for (; (*sv)[k]; k++)
758 n[k] = (*sv)[k];
760 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
761 n[k] = strndup(w, l);
762 if (!n[k]) {
763 r = -ENOMEM;
764 goto fail;
767 if (!utf8_is_valid(n[k])) {
768 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
769 free(n[k]);
770 continue;
773 if (!path_is_absolute(n[k])) {
774 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
775 free(n[k]);
776 continue;
779 path_kill_slashes(n[k]);
780 k++;
783 n[k] = NULL;
784 free(*sv);
785 *sv = n;
787 return 0;
789 fail:
790 for (; k > 0; k--)
791 free(n[k-1]);
792 free(n);
794 return r;
797 int config_parse_usec(
798 const char *filename,
799 unsigned line,
800 const char *section,
801 const char *lvalue,
802 int ltype,
803 const char *rvalue,
804 void *data,
805 void *userdata) {
807 usec_t *usec = data;
809 assert(filename);
810 assert(lvalue);
811 assert(rvalue);
812 assert(data);
814 if (parse_usec(rvalue, usec) < 0) {
815 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
816 return 0;
819 return 0;
822 int config_parse_nsec(
823 const char *filename,
824 unsigned line,
825 const char *section,
826 const char *lvalue,
827 int ltype,
828 const char *rvalue,
829 void *data,
830 void *userdata) {
832 nsec_t *nsec = data;
834 assert(filename);
835 assert(lvalue);
836 assert(rvalue);
837 assert(data);
839 if (parse_nsec(rvalue, nsec) < 0) {
840 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
841 return 0;
844 return 0;
847 int config_parse_mode(
848 const char *filename,
849 unsigned line,
850 const char *section,
851 const char *lvalue,
852 int ltype,
853 const char *rvalue,
854 void *data,
855 void *userdata) {
857 mode_t *m = data;
858 long l;
859 char *x = NULL;
861 assert(filename);
862 assert(lvalue);
863 assert(rvalue);
864 assert(data);
866 errno = 0;
867 l = strtol(rvalue, &x, 8);
868 if (!x || *x || errno) {
869 log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
870 return 0;
873 if (l < 0000 || l > 07777) {
874 log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
875 return 0;
878 *m = (mode_t) l;
879 return 0;
882 int config_parse_facility(
883 const char *filename,
884 unsigned line,
885 const char *section,
886 const char *lvalue,
887 int ltype,
888 const char *rvalue,
889 void *data,
890 void *userdata) {
893 int *o = data, x;
895 assert(filename);
896 assert(lvalue);
897 assert(rvalue);
898 assert(data);
900 x = log_facility_unshifted_from_string(rvalue);
901 if (x < 0) {
902 log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
903 return 0;
906 *o = (x << 3) | LOG_PRI(*o);
908 return 0;
911 int config_parse_level(
912 const char *filename,
913 unsigned line,
914 const char *section,
915 const char *lvalue,
916 int ltype,
917 const char *rvalue,
918 void *data,
919 void *userdata) {
922 int *o = data, x;
924 assert(filename);
925 assert(lvalue);
926 assert(rvalue);
927 assert(data);
929 x = log_level_from_string(rvalue);
930 if (x < 0) {
931 log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
932 return 0;
935 *o = (*o & LOG_FACMASK) | x;
936 return 0;
939 int config_parse_set_status(
940 const char *filename,
941 unsigned line,
942 const char *section,
943 const char *lvalue,
944 int ltype,
945 const char *rvalue,
946 void *data,
947 void *userdata) {
949 char *w;
950 size_t l;
951 char *state;
952 int r;
953 ExitStatusSet *status_set = data;
955 assert(filename);
956 assert(lvalue);
957 assert(rvalue);
958 assert(data);
960 FOREACH_WORD(w, l, rvalue, state) {
961 int val;
962 char *temp;
964 temp = strndup(w, l);
965 if (!temp)
966 return log_oom();
968 r = safe_atoi(temp, &val);
969 if (r < 0) {
970 val = signal_from_string_try_harder(temp);
971 free(temp);
973 if (val > 0) {
974 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
975 if (r < 0)
976 return log_oom();
978 r = set_put(status_set->signal, INT_TO_PTR(val));
979 if (r < 0) {
980 log_error("[%s:%u] Unable to store: %s", filename, line, w);
981 return r;
983 } else {
984 log_error("[%s:%u] Failed to parse value, ignoring: %s", filename, line, w);
985 return 0;
987 } else {
988 free(temp);
990 if (val < 0 || val > 255)
991 log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
992 else {
993 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
994 if (r < 0)
995 return log_oom();
997 r = set_put(status_set->code, INT_TO_PTR(val));
998 if (r < 0) {
999 log_error("[%s:%u] Unable to store: %s", filename, line, w);
1000 return r;
1006 return 0;