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