]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
util-lib: split out fd-related operations into fd-util.[ch]
[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 <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "sd-messages.h"
28
29 #include "conf-files.h"
30 #include "log.h"
31 #include "macro.h"
32 #include "path-util.h"
33 #include "signal-util.h"
34 #include "string-util.h"
35 #include "strv.h"
36 #include "utf8.h"
37 #include "util.h"
38 #include "conf-parser.h"
39 #include "fd-util.h"
40
41 int config_item_table_lookup(
42 const void *table,
43 const char *section,
44 const char *lvalue,
45 ConfigParserCallback *func,
46 int *ltype,
47 void **data,
48 void *userdata) {
49
50 const ConfigTableItem *t;
51
52 assert(table);
53 assert(lvalue);
54 assert(func);
55 assert(ltype);
56 assert(data);
57
58 for (t = table; t->lvalue; t++) {
59
60 if (!streq(lvalue, t->lvalue))
61 continue;
62
63 if (!streq_ptr(section, t->section))
64 continue;
65
66 *func = t->parse;
67 *ltype = t->ltype;
68 *data = t->data;
69 return 1;
70 }
71
72 return 0;
73 }
74
75 int config_item_perf_lookup(
76 const void *table,
77 const char *section,
78 const char *lvalue,
79 ConfigParserCallback *func,
80 int *ltype,
81 void **data,
82 void *userdata) {
83
84 ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
85 const ConfigPerfItem *p;
86
87 assert(table);
88 assert(lvalue);
89 assert(func);
90 assert(ltype);
91 assert(data);
92
93 if (!section)
94 p = lookup(lvalue, strlen(lvalue));
95 else {
96 char *key;
97
98 key = strjoin(section, ".", lvalue, NULL);
99 if (!key)
100 return -ENOMEM;
101
102 p = lookup(key, strlen(key));
103 free(key);
104 }
105
106 if (!p)
107 return 0;
108
109 *func = p->parse;
110 *ltype = p->ltype;
111 *data = (uint8_t*) userdata + p->offset;
112 return 1;
113 }
114
115 /* Run the user supplied parser for an assignment */
116 static int next_assignment(const char *unit,
117 const char *filename,
118 unsigned line,
119 ConfigItemLookup lookup,
120 const void *table,
121 const char *section,
122 unsigned section_line,
123 const char *lvalue,
124 const char *rvalue,
125 bool relaxed,
126 void *userdata) {
127
128 ConfigParserCallback func = NULL;
129 int ltype = 0;
130 void *data = NULL;
131 int r;
132
133 assert(filename);
134 assert(line > 0);
135 assert(lookup);
136 assert(lvalue);
137 assert(rvalue);
138
139 r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
140 if (r < 0)
141 return r;
142
143 if (r > 0) {
144 if (func)
145 return func(unit, filename, line, section, section_line,
146 lvalue, ltype, rvalue, data, userdata);
147
148 return 0;
149 }
150
151 /* Warn about unknown non-extension fields. */
152 if (!relaxed && !startswith(lvalue, "X-"))
153 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown lvalue '%s' in section '%s'", lvalue, section);
154
155 return 0;
156 }
157
158 /* Parse a variable assignment line */
159 static int parse_line(const char* unit,
160 const char *filename,
161 unsigned line,
162 const char *sections,
163 ConfigItemLookup lookup,
164 const void *table,
165 bool relaxed,
166 bool allow_include,
167 char **section,
168 unsigned *section_line,
169 bool *section_ignored,
170 char *l,
171 void *userdata) {
172
173 char *e;
174
175 assert(filename);
176 assert(line > 0);
177 assert(lookup);
178 assert(l);
179
180 l = strstrip(l);
181
182 if (!*l)
183 return 0;
184
185 if (strchr(COMMENTS "\n", *l))
186 return 0;
187
188 if (startswith(l, ".include ")) {
189 _cleanup_free_ char *fn = NULL;
190
191 /* .includes are a bad idea, we only support them here
192 * for historical reasons. They create cyclic include
193 * problems and make it difficult to detect
194 * configuration file changes with an easy
195 * stat(). Better approaches, such as .d/ drop-in
196 * snippets exist.
197 *
198 * Support for them should be eventually removed. */
199
200 if (!allow_include) {
201 log_syntax(unit, LOG_ERR, filename, line, 0, ".include not allowed here. Ignoring.");
202 return 0;
203 }
204
205 fn = file_in_same_dir(filename, strstrip(l+9));
206 if (!fn)
207 return -ENOMEM;
208
209 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata);
210 }
211
212 if (*l == '[') {
213 size_t k;
214 char *n;
215
216 k = strlen(l);
217 assert(k > 0);
218
219 if (l[k-1] != ']') {
220 log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
221 return -EBADMSG;
222 }
223
224 n = strndup(l+1, k-2);
225 if (!n)
226 return -ENOMEM;
227
228 if (sections && !nulstr_contains(sections, n)) {
229
230 if (!relaxed && !startswith(n, "X-"))
231 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
232
233 free(n);
234 *section = mfree(*section);
235 *section_line = 0;
236 *section_ignored = true;
237 } else {
238 free(*section);
239 *section = n;
240 *section_line = line;
241 *section_ignored = false;
242 }
243
244 return 0;
245 }
246
247 if (sections && !*section) {
248
249 if (!relaxed && !*section_ignored)
250 log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
251
252 return 0;
253 }
254
255 e = strchr(l, '=');
256 if (!e) {
257 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
258 return -EINVAL;
259 }
260
261 *e = 0;
262 e++;
263
264 return next_assignment(unit,
265 filename,
266 line,
267 lookup,
268 table,
269 *section,
270 *section_line,
271 strstrip(l),
272 strstrip(e),
273 relaxed,
274 userdata);
275 }
276
277 /* Go through the file and parse each line */
278 int config_parse(const char *unit,
279 const char *filename,
280 FILE *f,
281 const char *sections,
282 ConfigItemLookup lookup,
283 const void *table,
284 bool relaxed,
285 bool allow_include,
286 bool warn,
287 void *userdata) {
288
289 _cleanup_free_ char *section = NULL, *continuation = NULL;
290 _cleanup_fclose_ FILE *ours = NULL;
291 unsigned line = 0, section_line = 0;
292 bool section_ignored = false;
293 int r;
294
295 assert(filename);
296 assert(lookup);
297
298 if (!f) {
299 f = ours = fopen(filename, "re");
300 if (!f) {
301 /* Only log on request, except for ENOENT,
302 * since we return 0 to the caller. */
303 if (warn || errno == ENOENT)
304 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
305 "Failed to open configuration file '%s': %m", filename);
306 return errno == ENOENT ? 0 : -errno;
307 }
308 }
309
310 fd_warn_permissions(filename, fileno(f));
311
312 while (!feof(f)) {
313 char l[LINE_MAX], *p, *c = NULL, *e;
314 bool escaped = false;
315
316 if (!fgets(l, sizeof(l), f)) {
317 if (feof(f))
318 break;
319
320 log_error_errno(errno, "Failed to read configuration file '%s': %m", filename);
321 return -errno;
322 }
323
324 truncate_nl(l);
325
326 if (continuation) {
327 c = strappend(continuation, l);
328 if (!c) {
329 if (warn)
330 log_oom();
331 return -ENOMEM;
332 }
333
334 continuation = mfree(continuation);
335 p = c;
336 } else
337 p = l;
338
339 for (e = p; *e; e++) {
340 if (escaped)
341 escaped = false;
342 else if (*e == '\\')
343 escaped = true;
344 }
345
346 if (escaped) {
347 *(e-1) = ' ';
348
349 if (c)
350 continuation = c;
351 else {
352 continuation = strdup(l);
353 if (!continuation) {
354 if (warn)
355 log_oom();
356 return -ENOMEM;
357 }
358 }
359
360 continue;
361 }
362
363 r = parse_line(unit,
364 filename,
365 ++line,
366 sections,
367 lookup,
368 table,
369 relaxed,
370 allow_include,
371 &section,
372 &section_line,
373 &section_ignored,
374 p,
375 userdata);
376 free(c);
377
378 if (r < 0) {
379 if (warn)
380 log_warning_errno(r, "Failed to parse file '%s': %m",
381 filename);
382 return r;
383 }
384 }
385
386 return 0;
387 }
388
389 /* Parse each config file in the specified directories. */
390 int config_parse_many(const char *conf_file,
391 const char *conf_file_dirs,
392 const char *sections,
393 ConfigItemLookup lookup,
394 const void *table,
395 bool relaxed,
396 void *userdata) {
397 _cleanup_strv_free_ char **files = NULL;
398 char **fn;
399 int r;
400
401 r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
402 if (r < 0)
403 return r;
404
405 if (conf_file) {
406 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
407 if (r < 0)
408 return r;
409 }
410
411 STRV_FOREACH(fn, files) {
412 r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
413 if (r < 0)
414 return r;
415 }
416
417 return 0;
418 }
419
420 #define DEFINE_PARSER(type, vartype, conv_func) \
421 int config_parse_##type( \
422 const char *unit, \
423 const char *filename, \
424 unsigned line, \
425 const char *section, \
426 unsigned section_line, \
427 const char *lvalue, \
428 int ltype, \
429 const char *rvalue, \
430 void *data, \
431 void *userdata) { \
432 \
433 vartype *i = data; \
434 int r; \
435 \
436 assert(filename); \
437 assert(lvalue); \
438 assert(rvalue); \
439 assert(data); \
440 \
441 r = conv_func(rvalue, i); \
442 if (r < 0) \
443 log_syntax(unit, LOG_ERR, filename, line, r, \
444 "Failed to parse %s value, ignoring: %s", \
445 #type, rvalue); \
446 \
447 return 0; \
448 } \
449 struct __useless_struct_to_allow_trailing_semicolon__
450
451 DEFINE_PARSER(int, int, safe_atoi);
452 DEFINE_PARSER(long, long, safe_atoli);
453 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
454 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
455 DEFINE_PARSER(unsigned, unsigned, safe_atou);
456 DEFINE_PARSER(double, double, safe_atod);
457 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
458 DEFINE_PARSER(sec, usec_t, parse_sec);
459 DEFINE_PARSER(mode, mode_t, parse_mode);
460
461 int config_parse_iec_size(const char* unit,
462 const char *filename,
463 unsigned line,
464 const char *section,
465 unsigned section_line,
466 const char *lvalue,
467 int ltype,
468 const char *rvalue,
469 void *data,
470 void *userdata) {
471
472 size_t *sz = data;
473 uint64_t v;
474 int r;
475
476 assert(filename);
477 assert(lvalue);
478 assert(rvalue);
479 assert(data);
480
481 r = parse_size(rvalue, 1024, &v);
482 if (r < 0 || (uint64_t) (size_t) v != v) {
483 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
484 return 0;
485 }
486
487 *sz = (size_t) v;
488 return 0;
489 }
490
491 int config_parse_si_size(const char* unit,
492 const char *filename,
493 unsigned line,
494 const char *section,
495 unsigned section_line,
496 const char *lvalue,
497 int ltype,
498 const char *rvalue,
499 void *data,
500 void *userdata) {
501
502 size_t *sz = data;
503 uint64_t v;
504 int r;
505
506 assert(filename);
507 assert(lvalue);
508 assert(rvalue);
509 assert(data);
510
511 r = parse_size(rvalue, 1000, &v);
512 if (r < 0 || (uint64_t) (size_t) v != v) {
513 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
514 return 0;
515 }
516
517 *sz = (size_t) v;
518 return 0;
519 }
520
521 int config_parse_iec_uint64(const char* unit,
522 const char *filename,
523 unsigned line,
524 const char *section,
525 unsigned section_line,
526 const char *lvalue,
527 int ltype,
528 const char *rvalue,
529 void *data,
530 void *userdata) {
531
532 uint64_t *bytes = data;
533 int r;
534
535 assert(filename);
536 assert(lvalue);
537 assert(rvalue);
538 assert(data);
539
540 r = parse_size(rvalue, 1024, bytes);
541 if (r < 0)
542 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
543
544 return 0;
545 }
546
547 int config_parse_bool(const char* unit,
548 const char *filename,
549 unsigned line,
550 const char *section,
551 unsigned section_line,
552 const char *lvalue,
553 int ltype,
554 const char *rvalue,
555 void *data,
556 void *userdata) {
557
558 int k;
559 bool *b = data;
560
561 assert(filename);
562 assert(lvalue);
563 assert(rvalue);
564 assert(data);
565
566 k = parse_boolean(rvalue);
567 if (k < 0) {
568 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
569 return 0;
570 }
571
572 *b = !!k;
573 return 0;
574 }
575
576 int config_parse_tristate(
577 const char* unit,
578 const char *filename,
579 unsigned line,
580 const char *section,
581 unsigned section_line,
582 const char *lvalue,
583 int ltype,
584 const char *rvalue,
585 void *data,
586 void *userdata) {
587
588 int k, *t = data;
589
590 assert(filename);
591 assert(lvalue);
592 assert(rvalue);
593 assert(data);
594
595 /* A tristate is pretty much a boolean, except that it can
596 * also take the special value -1, indicating "uninitialized",
597 * much like NULL is for a pointer type. */
598
599 k = parse_boolean(rvalue);
600 if (k < 0) {
601 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
602 return 0;
603 }
604
605 *t = !!k;
606 return 0;
607 }
608
609 int config_parse_string(
610 const char *unit,
611 const char *filename,
612 unsigned line,
613 const char *section,
614 unsigned section_line,
615 const char *lvalue,
616 int ltype,
617 const char *rvalue,
618 void *data,
619 void *userdata) {
620
621 char **s = data, *n;
622
623 assert(filename);
624 assert(lvalue);
625 assert(rvalue);
626 assert(data);
627
628 if (!utf8_is_valid(rvalue)) {
629 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
630 return 0;
631 }
632
633 if (isempty(rvalue))
634 n = NULL;
635 else {
636 n = strdup(rvalue);
637 if (!n)
638 return log_oom();
639 }
640
641 free(*s);
642 *s = n;
643
644 return 0;
645 }
646
647 int config_parse_path(
648 const char *unit,
649 const char *filename,
650 unsigned line,
651 const char *section,
652 unsigned section_line,
653 const char *lvalue,
654 int ltype,
655 const char *rvalue,
656 void *data,
657 void *userdata) {
658
659 char **s = data, *n;
660
661 assert(filename);
662 assert(lvalue);
663 assert(rvalue);
664 assert(data);
665
666 if (!utf8_is_valid(rvalue)) {
667 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
668 return 0;
669 }
670
671 if (!path_is_absolute(rvalue)) {
672 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", 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(const char *unit,
689 const char *filename,
690 unsigned line,
691 const char *section,
692 unsigned section_line,
693 const char *lvalue,
694 int ltype,
695 const char *rvalue,
696 void *data,
697 void *userdata) {
698
699 char ***sv = data;
700 const char *word, *state;
701 size_t l;
702 int r;
703
704 assert(filename);
705 assert(lvalue);
706 assert(rvalue);
707 assert(data);
708
709 if (isempty(rvalue)) {
710 char **empty;
711
712 /* Empty assignment resets the list. As a special rule
713 * we actually fill in a real empty array here rather
714 * than NULL, since some code wants to know if
715 * something was set at all... */
716 empty = strv_new(NULL, NULL);
717 if (!empty)
718 return log_oom();
719
720 strv_free(*sv);
721 *sv = empty;
722 return 0;
723 }
724
725 FOREACH_WORD_QUOTED(word, l, rvalue, state) {
726 char *n;
727
728 n = strndup(word, l);
729 if (!n)
730 return log_oom();
731
732 if (!utf8_is_valid(n)) {
733 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
734 free(n);
735 continue;
736 }
737
738 r = strv_consume(sv, n);
739 if (r < 0)
740 return log_oom();
741 }
742 if (!isempty(state))
743 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
744
745 return 0;
746 }
747
748 int config_parse_log_facility(
749 const char *unit,
750 const char *filename,
751 unsigned line,
752 const char *section,
753 unsigned section_line,
754 const char *lvalue,
755 int ltype,
756 const char *rvalue,
757 void *data,
758 void *userdata) {
759
760
761 int *o = data, x;
762
763 assert(filename);
764 assert(lvalue);
765 assert(rvalue);
766 assert(data);
767
768 x = log_facility_unshifted_from_string(rvalue);
769 if (x < 0) {
770 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
771 return 0;
772 }
773
774 *o = (x << 3) | LOG_PRI(*o);
775
776 return 0;
777 }
778
779 int config_parse_log_level(
780 const char *unit,
781 const char *filename,
782 unsigned line,
783 const char *section,
784 unsigned section_line,
785 const char *lvalue,
786 int ltype,
787 const char *rvalue,
788 void *data,
789 void *userdata) {
790
791
792 int *o = data, x;
793
794 assert(filename);
795 assert(lvalue);
796 assert(rvalue);
797 assert(data);
798
799 x = log_level_from_string(rvalue);
800 if (x < 0) {
801 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
802 return 0;
803 }
804
805 *o = (*o & LOG_FACMASK) | x;
806 return 0;
807 }
808
809 int config_parse_signal(
810 const char *unit,
811 const char *filename,
812 unsigned line,
813 const char *section,
814 unsigned section_line,
815 const char *lvalue,
816 int ltype,
817 const char *rvalue,
818 void *data,
819 void *userdata) {
820
821 int *sig = data, r;
822
823 assert(filename);
824 assert(lvalue);
825 assert(rvalue);
826 assert(sig);
827
828 r = signal_from_string_try_harder(rvalue);
829 if (r <= 0) {
830 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
831 return 0;
832 }
833
834 *sig = r;
835 return 0;
836 }
837
838 int config_parse_personality(
839 const char *unit,
840 const char *filename,
841 unsigned line,
842 const char *section,
843 unsigned section_line,
844 const char *lvalue,
845 int ltype,
846 const char *rvalue,
847 void *data,
848 void *userdata) {
849
850 unsigned long *personality = data, p;
851
852 assert(filename);
853 assert(lvalue);
854 assert(rvalue);
855 assert(personality);
856
857 p = personality_from_string(rvalue);
858 if (p == PERSONALITY_INVALID) {
859 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
860 return 0;
861 }
862
863 *personality = p;
864 return 0;
865 }