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