]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
core: properly validate environment data from Environment= lines in unit 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 *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_bytes_size(
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 size_t *sz = data;
481 off_t o;
482
483 assert(filename);
484 assert(lvalue);
485 assert(rvalue);
486 assert(data);
487
488 if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
489 log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
490 return 0;
491 }
492
493 *sz = (size_t) o;
494 return 0;
495 }
496
497
498 int config_parse_bytes_off(
499 const char *filename,
500 unsigned line,
501 const char *section,
502 const char *lvalue,
503 int ltype,
504 const char *rvalue,
505 void *data,
506 void *userdata) {
507
508 off_t *bytes = data;
509
510 assert(filename);
511 assert(lvalue);
512 assert(rvalue);
513 assert(data);
514
515 assert_cc(sizeof(off_t) == sizeof(uint64_t));
516
517 if (parse_bytes(rvalue, bytes) < 0) {
518 log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
519 return 0;
520 }
521
522 return 0;
523 }
524
525 int config_parse_bool(
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 int k;
536 bool *b = data;
537
538 assert(filename);
539 assert(lvalue);
540 assert(rvalue);
541 assert(data);
542
543 if ((k = parse_boolean(rvalue)) < 0) {
544 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
545 return 0;
546 }
547
548 *b = !!k;
549 return 0;
550 }
551
552 int config_parse_tristate(
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 int *b = data;
564
565 assert(filename);
566 assert(lvalue);
567 assert(rvalue);
568 assert(data);
569
570 /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
571
572 k = parse_boolean(rvalue);
573 if (k < 0) {
574 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
575 return 0;
576 }
577
578 *b = !!k;
579 return 0;
580 }
581
582 int config_parse_string(
583 const char *filename,
584 unsigned line,
585 const char *section,
586 const char *lvalue,
587 int ltype,
588 const char *rvalue,
589 void *data,
590 void *userdata) {
591
592 char **s = data;
593 char *n;
594
595 assert(filename);
596 assert(lvalue);
597 assert(rvalue);
598 assert(data);
599
600 n = strdup(rvalue);
601 if (!n)
602 return log_oom();
603
604 if (!utf8_is_valid(n)) {
605 log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
606 free(n);
607 return 0;
608 }
609
610 free(*s);
611 if (*n)
612 *s = n;
613 else {
614 free(n);
615 *s = NULL;
616 }
617
618 return 0;
619 }
620
621 int config_parse_path(
622 const char *filename,
623 unsigned line,
624 const char *section,
625 const char *lvalue,
626 int ltype,
627 const char *rvalue,
628 void *data,
629 void *userdata) {
630
631 char **s = data;
632 char *n;
633
634 assert(filename);
635 assert(lvalue);
636 assert(rvalue);
637 assert(data);
638
639 if (!utf8_is_valid(rvalue)) {
640 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
641 return 0;
642 }
643
644 if (!path_is_absolute(rvalue)) {
645 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
646 return 0;
647 }
648
649 n = strdup(rvalue);
650 if (!n)
651 return log_oom();
652
653 path_kill_slashes(n);
654
655 free(*s);
656 *s = n;
657
658 return 0;
659 }
660
661 int config_parse_strv(
662 const char *filename,
663 unsigned line,
664 const char *section,
665 const char *lvalue,
666 int ltype,
667 const char *rvalue,
668 void *data,
669 void *userdata) {
670
671 char *** sv = data, *w, *state;
672 size_t l;
673 int r;
674
675 assert(filename);
676 assert(lvalue);
677 assert(rvalue);
678 assert(data);
679
680 if (isempty(rvalue)) {
681 /* Empty assignment resets the list */
682 strv_free(*sv);
683 *sv = NULL;
684 return 0;
685 }
686
687 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
688 _cleanup_free_ char *n;
689
690 n = cunescape_length(w, l);
691 if (!n)
692 return log_oom();
693
694 if (!utf8_is_valid(n)) {
695 log_error("[%s:%u] String is not UTF-8 clean, ignoring: %s", filename, line, rvalue);
696 continue;
697 }
698
699 r = strv_extend(sv, n);
700 if (r < 0)
701 return log_oom();
702 }
703
704 return 0;
705 }
706
707 int config_parse_path_strv(
708 const char *filename,
709 unsigned line,
710 const char *section,
711 const char *lvalue,
712 int ltype,
713 const char *rvalue,
714 void *data,
715 void *userdata) {
716
717 char*** sv = data, *w, *state;
718 size_t l;
719 int r;
720
721 assert(filename);
722 assert(lvalue);
723 assert(rvalue);
724 assert(data);
725
726 if (isempty(rvalue)) {
727 /* Empty assignment resets the list */
728 strv_free(*sv);
729 *sv = NULL;
730 return 0;
731 }
732
733 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
734 _cleanup_free_ char *n;
735
736 n = strndup(w, l);
737 if (!n)
738 return log_oom();
739
740 if (!utf8_is_valid(n)) {
741 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
742 continue;
743 }
744
745 if (!path_is_absolute(n)) {
746 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
747 continue;
748 }
749
750 path_kill_slashes(n);
751 r = strv_extend(sv, n);
752 if (r < 0)
753 return log_oom();
754 }
755
756 return 0;
757 }
758
759 int config_parse_usec(
760 const char *filename,
761 unsigned line,
762 const char *section,
763 const char *lvalue,
764 int ltype,
765 const char *rvalue,
766 void *data,
767 void *userdata) {
768
769 usec_t *usec = data;
770
771 assert(filename);
772 assert(lvalue);
773 assert(rvalue);
774 assert(data);
775
776 if (parse_usec(rvalue, usec) < 0) {
777 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
778 return 0;
779 }
780
781 return 0;
782 }
783
784 int config_parse_nsec(
785 const char *filename,
786 unsigned line,
787 const char *section,
788 const char *lvalue,
789 int ltype,
790 const char *rvalue,
791 void *data,
792 void *userdata) {
793
794 nsec_t *nsec = data;
795
796 assert(filename);
797 assert(lvalue);
798 assert(rvalue);
799 assert(data);
800
801 if (parse_nsec(rvalue, nsec) < 0) {
802 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
803 return 0;
804 }
805
806 return 0;
807 }
808
809 int config_parse_mode(
810 const char *filename,
811 unsigned line,
812 const char *section,
813 const char *lvalue,
814 int ltype,
815 const char *rvalue,
816 void *data,
817 void *userdata) {
818
819 mode_t *m = data;
820 long l;
821 char *x = NULL;
822
823 assert(filename);
824 assert(lvalue);
825 assert(rvalue);
826 assert(data);
827
828 errno = 0;
829 l = strtol(rvalue, &x, 8);
830 if (!x || x == rvalue || *x || errno) {
831 log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
832 return 0;
833 }
834
835 if (l < 0000 || l > 07777) {
836 log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
837 return 0;
838 }
839
840 *m = (mode_t) l;
841 return 0;
842 }
843
844 int config_parse_facility(
845 const char *filename,
846 unsigned line,
847 const char *section,
848 const char *lvalue,
849 int ltype,
850 const char *rvalue,
851 void *data,
852 void *userdata) {
853
854
855 int *o = data, x;
856
857 assert(filename);
858 assert(lvalue);
859 assert(rvalue);
860 assert(data);
861
862 x = log_facility_unshifted_from_string(rvalue);
863 if (x < 0) {
864 log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
865 return 0;
866 }
867
868 *o = (x << 3) | LOG_PRI(*o);
869
870 return 0;
871 }
872
873 int config_parse_level(
874 const char *filename,
875 unsigned line,
876 const char *section,
877 const char *lvalue,
878 int ltype,
879 const char *rvalue,
880 void *data,
881 void *userdata) {
882
883
884 int *o = data, x;
885
886 assert(filename);
887 assert(lvalue);
888 assert(rvalue);
889 assert(data);
890
891 x = log_level_from_string(rvalue);
892 if (x < 0) {
893 log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
894 return 0;
895 }
896
897 *o = (*o & LOG_FACMASK) | x;
898 return 0;
899 }
900
901 int config_parse_set_status(
902 const char *filename,
903 unsigned line,
904 const char *section,
905 const char *lvalue,
906 int ltype,
907 const char *rvalue,
908 void *data,
909 void *userdata) {
910
911 char *w;
912 size_t l;
913 char *state;
914 int r;
915 ExitStatusSet *status_set = data;
916
917 assert(filename);
918 assert(lvalue);
919 assert(rvalue);
920 assert(data);
921
922 if (isempty(rvalue)) {
923 /* Empty assignment resets the list */
924
925 set_free(status_set->signal);
926 set_free(status_set->code);
927
928 status_set->signal = status_set->code = NULL;
929 return 0;
930 }
931
932 FOREACH_WORD(w, l, rvalue, state) {
933 int val;
934 char *temp;
935
936 temp = strndup(w, l);
937 if (!temp)
938 return log_oom();
939
940 r = safe_atoi(temp, &val);
941 if (r < 0) {
942 val = signal_from_string_try_harder(temp);
943 free(temp);
944
945 if (val > 0) {
946 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
947 if (r < 0)
948 return log_oom();
949
950 r = set_put(status_set->signal, INT_TO_PTR(val));
951 if (r < 0) {
952 log_error("[%s:%u] Unable to store: %s", filename, line, w);
953 return r;
954 }
955 } else {
956 log_error("[%s:%u] Failed to parse value, ignoring: %s", filename, line, w);
957 return 0;
958 }
959 } else {
960 free(temp);
961
962 if (val < 0 || val > 255)
963 log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
964 else {
965 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
966 if (r < 0)
967 return log_oom();
968
969 r = set_put(status_set->code, INT_TO_PTR(val));
970 if (r < 0) {
971 log_error("[%s:%u] Unable to store: %s", filename, line, w);
972 return r;
973 }
974 }
975 }
976 }
977
978 return 0;
979 }