]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/conf-parser.c
man: document ARM root partition types
[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 void *table,
80 const char *section,
81 const char *lvalue,
82 ConfigParserCallback *func,
83 int *ltype,
84 void **data,
85 void *userdata) {
86
87 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 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 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 void *table,
203 bool relaxed,
204 bool allow_include,
205 char **section,
206 unsigned *section_line,
207 char *l,
208 void *userdata) {
209
210 char *e;
211
212 assert(filename);
213 assert(line > 0);
214 assert(lookup);
215 assert(l);
216
217 l = strstrip(l);
218
219 if (!*l)
220 return 0;
221
222 if (strchr(COMMENTS "\n", *l))
223 return 0;
224
225 if (startswith(l, ".include ")) {
226 _cleanup_free_ char *fn = NULL;
227
228 /* .includes are a bad idea, we only support them here
229 * for historical reasons. They create cyclic include
230 * problems and make it difficult to detect
231 * configuration file changes with an easy
232 * stat(). Better approaches, such as .d/ drop-in
233 * snippets exist.
234 *
235 * Support for them should be eventually removed. */
236
237 if (!allow_include) {
238 log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
239 ".include not allowed here. Ignoring.");
240 return 0;
241 }
242
243 fn = file_in_same_dir(filename, strstrip(l+9));
244 if (!fn)
245 return -ENOMEM;
246
247 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, userdata);
248 }
249
250 if (*l == '[') {
251 size_t k;
252 char *n;
253
254 k = strlen(l);
255 assert(k > 0);
256
257 if (l[k-1] != ']') {
258 log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
259 "Invalid section header '%s'", l);
260 return -EBADMSG;
261 }
262
263 n = strndup(l+1, k-2);
264 if (!n)
265 return -ENOMEM;
266
267 if (sections && !nulstr_contains(sections, n)) {
268
269 if (!relaxed)
270 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
271 "Unknown section '%s'. Ignoring.", n);
272
273 free(n);
274 free(*section);
275 *section = NULL;
276 *section_line = 0;
277 } else {
278 free(*section);
279 *section = n;
280 *section_line = line;
281 }
282
283 return 0;
284 }
285
286 if (sections && !*section) {
287
288 if (!relaxed)
289 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
290 "Assignment outside of section. Ignoring.");
291
292 return 0;
293 }
294
295 e = strchr(l, '=');
296 if (!e) {
297 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
298 return -EBADMSG;
299 }
300
301 *e = 0;
302 e++;
303
304 return next_assignment(unit,
305 filename,
306 line,
307 lookup,
308 table,
309 *section,
310 *section_line,
311 strstrip(l),
312 strstrip(e),
313 relaxed,
314 userdata);
315 }
316
317 /* Go through the file and parse each line */
318 int config_parse(const char *unit,
319 const char *filename,
320 FILE *f,
321 const char *sections,
322 ConfigItemLookup lookup,
323 void *table,
324 bool relaxed,
325 bool allow_include,
326 void *userdata) {
327
328 _cleanup_free_ char *section = NULL, *continuation = NULL;
329 _cleanup_fclose_ FILE *ours = NULL;
330 unsigned line = 0, section_line = 0;
331 int r;
332
333 assert(filename);
334 assert(lookup);
335
336 if (!f) {
337 f = ours = fopen(filename, "re");
338 if (!f) {
339 log_error("Failed to open configuration file '%s': %m", filename);
340 return -errno;
341 }
342 }
343
344 fd_warn_permissions(filename, fileno(f));
345
346 while (!feof(f)) {
347 char l[LINE_MAX], *p, *c = NULL, *e;
348 bool escaped = false;
349
350 if (!fgets(l, sizeof(l), f)) {
351 if (feof(f))
352 break;
353
354 log_error("Failed to read configuration file '%s': %m", filename);
355 return -errno;
356 }
357
358 truncate_nl(l);
359
360 if (continuation) {
361 c = strappend(continuation, l);
362 if (!c)
363 return -ENOMEM;
364
365 free(continuation);
366 continuation = NULL;
367 p = c;
368 } else
369 p = l;
370
371 for (e = p; *e; e++) {
372 if (escaped)
373 escaped = false;
374 else if (*e == '\\')
375 escaped = true;
376 }
377
378 if (escaped) {
379 *(e-1) = ' ';
380
381 if (c)
382 continuation = c;
383 else {
384 continuation = strdup(l);
385 if (!continuation)
386 return -ENOMEM;
387 }
388
389 continue;
390 }
391
392 r = parse_line(unit,
393 filename,
394 ++line,
395 sections,
396 lookup,
397 table,
398 relaxed,
399 allow_include,
400 &section,
401 &section_line,
402 p,
403 userdata);
404 free(c);
405
406 if (r < 0)
407 return r;
408 }
409
410 return 0;
411 }
412
413 #define DEFINE_PARSER(type, vartype, conv_func) \
414 int config_parse_##type(const char *unit, \
415 const char *filename, \
416 unsigned line, \
417 const char *section, \
418 unsigned section_line, \
419 const char *lvalue, \
420 int ltype, \
421 const char *rvalue, \
422 void *data, \
423 void *userdata) { \
424 \
425 vartype *i = data; \
426 int r; \
427 \
428 assert(filename); \
429 assert(lvalue); \
430 assert(rvalue); \
431 assert(data); \
432 \
433 r = conv_func(rvalue, i); \
434 if (r < 0) \
435 log_syntax(unit, LOG_ERR, filename, line, -r, \
436 "Failed to parse %s value, ignoring: %s", \
437 #vartype, rvalue); \
438 \
439 return 0; \
440 }
441
442 DEFINE_PARSER(int, int, safe_atoi)
443 DEFINE_PARSER(long, long, safe_atoli)
444 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
445 DEFINE_PARSER(unsigned, unsigned, safe_atou)
446 DEFINE_PARSER(double, double, safe_atod)
447 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
448 DEFINE_PARSER(sec, usec_t, parse_sec)
449
450 int config_parse_iec_size(const char* unit,
451 const char *filename,
452 unsigned line,
453 const char *section,
454 unsigned section_line,
455 const char *lvalue,
456 int ltype,
457 const char *rvalue,
458 void *data,
459 void *userdata) {
460
461 size_t *sz = data;
462 off_t o;
463 int r;
464
465 assert(filename);
466 assert(lvalue);
467 assert(rvalue);
468 assert(data);
469
470 r = parse_size(rvalue, 1024, &o);
471 if (r < 0 || (off_t) (size_t) o != o) {
472 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
473 return 0;
474 }
475
476 *sz = (size_t) o;
477 return 0;
478 }
479
480 int config_parse_si_size(const char* unit,
481 const char *filename,
482 unsigned line,
483 const char *section,
484 unsigned section_line,
485 const char *lvalue,
486 int ltype,
487 const char *rvalue,
488 void *data,
489 void *userdata) {
490
491 size_t *sz = data;
492 off_t o;
493 int r;
494
495 assert(filename);
496 assert(lvalue);
497 assert(rvalue);
498 assert(data);
499
500 r = parse_size(rvalue, 1000, &o);
501 if (r < 0 || (off_t) (size_t) o != o) {
502 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
503 return 0;
504 }
505
506 *sz = (size_t) o;
507 return 0;
508 }
509
510 int config_parse_iec_off(const char* unit,
511 const char *filename,
512 unsigned line,
513 const char *section,
514 unsigned section_line,
515 const char *lvalue,
516 int ltype,
517 const char *rvalue,
518 void *data,
519 void *userdata) {
520
521 off_t *bytes = data;
522 int r;
523
524 assert(filename);
525 assert(lvalue);
526 assert(rvalue);
527 assert(data);
528
529 assert_cc(sizeof(off_t) == sizeof(uint64_t));
530
531 r = parse_size(rvalue, 1024, bytes);
532 if (r < 0)
533 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
534
535 return 0;
536 }
537
538 int config_parse_bool(const char* unit,
539 const char *filename,
540 unsigned line,
541 const char *section,
542 unsigned section_line,
543 const char *lvalue,
544 int ltype,
545 const char *rvalue,
546 void *data,
547 void *userdata) {
548
549 int k;
550 bool *b = data;
551
552 assert(filename);
553 assert(lvalue);
554 assert(rvalue);
555 assert(data);
556
557 k = parse_boolean(rvalue);
558 if (k < 0) {
559 log_syntax(unit, LOG_ERR, filename, line, -k,
560 "Failed to parse boolean value, ignoring: %s", rvalue);
561 return 0;
562 }
563
564 *b = !!k;
565 return 0;
566 }
567
568 int config_parse_string(
569 const char *unit,
570 const char *filename,
571 unsigned line,
572 const char *section,
573 unsigned section_line,
574 const char *lvalue,
575 int ltype,
576 const char *rvalue,
577 void *data,
578 void *userdata) {
579
580 char **s = data, *n;
581
582 assert(filename);
583 assert(lvalue);
584 assert(rvalue);
585 assert(data);
586
587 if (!utf8_is_valid(rvalue)) {
588 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
589 return 0;
590 }
591
592 if (isempty(rvalue))
593 n = NULL;
594 else {
595 n = strdup(rvalue);
596 if (!n)
597 return log_oom();
598 }
599
600 free(*s);
601 *s = n;
602
603 return 0;
604 }
605
606 int config_parse_path(
607 const char *unit,
608 const char *filename,
609 unsigned line,
610 const char *section,
611 unsigned section_line,
612 const char *lvalue,
613 int ltype,
614 const char *rvalue,
615 void *data,
616 void *userdata) {
617
618 char **s = data, *n;
619
620 assert(filename);
621 assert(lvalue);
622 assert(rvalue);
623 assert(data);
624
625 if (!utf8_is_valid(rvalue)) {
626 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
627 return 0;
628 }
629
630 if (!path_is_absolute(rvalue)) {
631 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
632 return 0;
633 }
634
635 n = strdup(rvalue);
636 if (!n)
637 return log_oom();
638
639 path_kill_slashes(n);
640
641 free(*s);
642 *s = n;
643
644 return 0;
645 }
646
647 int config_parse_strv(const char *unit,
648 const char *filename,
649 unsigned line,
650 const char *section,
651 unsigned section_line,
652 const char *lvalue,
653 int ltype,
654 const char *rvalue,
655 void *data,
656 void *userdata) {
657
658 char *** sv = data, *w, *state;
659 size_t l;
660 int r;
661
662 assert(filename);
663 assert(lvalue);
664 assert(rvalue);
665 assert(data);
666
667 if (isempty(rvalue)) {
668 char **empty;
669
670 /* Empty assignment resets the list. As a special rule
671 * we actually fill in a real empty array here rather
672 * than NULL, since some code wants to know if
673 * something was set at all... */
674 empty = strv_new(NULL, NULL);
675 if (!empty)
676 return log_oom();
677
678 strv_free(*sv);
679 *sv = empty;
680 return 0;
681 }
682
683 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
684 char *n;
685
686 n = cunescape_length(w, l);
687 if (!n)
688 return log_oom();
689
690 if (!utf8_is_valid(n)) {
691 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
692 continue;
693 }
694
695 r = strv_consume(sv, n);
696 if (r < 0)
697 return log_oom();
698 }
699
700 return 0;
701 }
702
703 int config_parse_mode(const char *unit,
704 const char *filename,
705 unsigned line,
706 const char *section,
707 unsigned section_line,
708 const char *lvalue,
709 int ltype,
710 const char *rvalue,
711 void *data,
712 void *userdata) {
713
714 mode_t *m = data;
715 long l;
716 char *x = NULL;
717
718 assert(filename);
719 assert(lvalue);
720 assert(rvalue);
721 assert(data);
722
723 errno = 0;
724 l = strtol(rvalue, &x, 8);
725 if (!x || x == rvalue || *x || errno) {
726 log_syntax(unit, LOG_ERR, filename, line, errno,
727 "Failed to parse mode value, ignoring: %s", rvalue);
728 return 0;
729 }
730
731 if (l < 0000 || l > 07777) {
732 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
733 "Mode value out of range, ignoring: %s", rvalue);
734 return 0;
735 }
736
737 *m = (mode_t) l;
738 return 0;
739 }
740
741 int config_parse_log_facility(
742 const char *unit,
743 const char *filename,
744 unsigned line,
745 const char *section,
746 unsigned section_line,
747 const char *lvalue,
748 int ltype,
749 const char *rvalue,
750 void *data,
751 void *userdata) {
752
753
754 int *o = data, x;
755
756 assert(filename);
757 assert(lvalue);
758 assert(rvalue);
759 assert(data);
760
761 x = log_facility_unshifted_from_string(rvalue);
762 if (x < 0) {
763 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
764 "Failed to parse log facility, ignoring: %s", rvalue);
765 return 0;
766 }
767
768 *o = (x << 3) | LOG_PRI(*o);
769
770 return 0;
771 }
772
773 int config_parse_log_level(
774 const char *unit,
775 const char *filename,
776 unsigned line,
777 const char *section,
778 unsigned section_line,
779 const char *lvalue,
780 int ltype,
781 const char *rvalue,
782 void *data,
783 void *userdata) {
784
785
786 int *o = data, x;
787
788 assert(filename);
789 assert(lvalue);
790 assert(rvalue);
791 assert(data);
792
793 x = log_level_from_string(rvalue);
794 if (x < 0) {
795 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
796 "Failed to parse log level, ignoring: %s", rvalue);
797 return 0;
798 }
799
800 *o = (*o & LOG_FACMASK) | x;
801 return 0;
802 }