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