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