]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/conf-parser.c
util: split-out path-util.[ch]
[thirdparty/systemd.git] / src / shared / conf-parser.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
ed5bcfbe 2
a7334b09
LP
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
5430f7f2
LP
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
a7334b09
LP
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
5430f7f2 16 Lesser General Public License for more details.
a7334b09 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
a7334b09
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
ed5bcfbe
LP
22#include <string.h>
23#include <stdio.h>
24#include <errno.h>
25#include <assert.h>
26#include <stdlib.h>
27
28#include "conf-parser.h"
29#include "util.h"
30#include "macro.h"
57d42a5f 31#include "strv.h"
16354eff 32#include "log.h"
7f110ff9 33#include "utf8.h"
9eb977db 34#include "path-util.h"
ed5bcfbe 35
f975e971
LP
36int config_item_table_lookup(
37 void *table,
ed5bcfbe 38 const char *section,
ed5bcfbe 39 const char *lvalue,
f975e971
LP
40 ConfigParserCallback *func,
41 int *ltype,
42 void **data,
ed5bcfbe
LP
43 void *userdata) {
44
f975e971
LP
45 ConfigTableItem *t;
46
47 assert(table);
ed5bcfbe 48 assert(lvalue);
f975e971
LP
49 assert(func);
50 assert(ltype);
51 assert(data);
ed5bcfbe 52
f975e971 53 for (t = table; t->lvalue; t++) {
ed5bcfbe 54
f975e971 55 if (!streq(lvalue, t->lvalue))
ed5bcfbe
LP
56 continue;
57
f975e971 58 if (!streq_ptr(section, t->section))
ed5bcfbe
LP
59 continue;
60
f975e971
LP
61 *func = t->parse;
62 *ltype = t->ltype;
63 *data = t->data;
64 return 1;
65 }
ed5bcfbe 66
f975e971
LP
67 return 0;
68}
10e87ee7 69
f975e971
LP
70int config_item_perf_lookup(
71 void *table,
72 const char *section,
73 const char *lvalue,
74 ConfigParserCallback *func,
75 int *ltype,
76 void **data,
77 void *userdata) {
78
79 ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
80 const ConfigPerfItem *p;
81
82 assert(table);
83 assert(lvalue);
84 assert(func);
85 assert(ltype);
86 assert(data);
87
88 if (!section)
89 p = lookup(lvalue, strlen(lvalue));
90 else {
91 char *key;
92
44d91056
LP
93 key = join(section, ".", lvalue, NULL);
94 if (!key)
f975e971
LP
95 return -ENOMEM;
96
97 p = lookup(key, strlen(key));
98 free(key);
ed5bcfbe
LP
99 }
100
f975e971
LP
101 if (!p)
102 return 0;
103
104 *func = p->parse;
105 *ltype = p->ltype;
106 *data = (uint8_t*) userdata + p->offset;
107 return 1;
108}
109
110/* Run the user supplied parser for an assignment */
111static int next_assignment(
112 const char *filename,
113 unsigned line,
114 ConfigItemLookup lookup,
115 void *table,
116 const char *section,
117 const char *lvalue,
118 const char *rvalue,
119 bool relaxed,
120 void *userdata) {
121
122 ConfigParserCallback func = NULL;
123 int ltype = 0;
124 void *data = NULL;
125 int r;
126
127 assert(filename);
128 assert(line > 0);
129 assert(lookup);
130 assert(lvalue);
131 assert(rvalue);
132
133 r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
134 if (r < 0)
135 return r;
136
d937fbbd
LP
137 if (r > 0) {
138 if (func)
139 return func(filename, line, section, lvalue, ltype, rvalue, data, userdata);
140
141 return 0;
142 }
f975e971 143
46205bb6 144 /* Warn about unknown non-extension fields. */
10e87ee7 145 if (!relaxed && !startswith(lvalue, "X-"))
f975e971 146 log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, section);
46205bb6 147
f1857be0 148 return 0;
ed5bcfbe
LP
149}
150
ed5bcfbe 151/* Parse a variable assignment line */
f975e971
LP
152static int parse_line(
153 const char *filename,
154 unsigned line,
155 const char *sections,
156 ConfigItemLookup lookup,
157 void *table,
158 bool relaxed,
159 char **section,
160 char *l,
161 void *userdata) {
162
b2aa81ef 163 char *e;
ed5bcfbe 164
f975e971
LP
165 assert(filename);
166 assert(line > 0);
167 assert(lookup);
168 assert(l);
169
b2aa81ef 170 l = strstrip(l);
ed5bcfbe 171
b2aa81ef 172 if (!*l)
ed5bcfbe 173 return 0;
1ea86b18 174
b2aa81ef 175 if (strchr(COMMENTS, *l))
1ea86b18 176 return 0;
ed5bcfbe 177
b2aa81ef
LP
178 if (startswith(l, ".include ")) {
179 char *fn;
ed5bcfbe
LP
180 int r;
181
f975e971
LP
182 fn = file_in_same_dir(filename, strstrip(l+9));
183 if (!fn)
b2aa81ef 184 return -ENOMEM;
ed5bcfbe 185
f975e971 186 r = config_parse(fn, NULL, sections, lookup, table, relaxed, userdata);
b2aa81ef
LP
187 free(fn);
188
ed5bcfbe
LP
189 return r;
190 }
191
b2aa81ef 192 if (*l == '[') {
ed5bcfbe
LP
193 size_t k;
194 char *n;
195
b2aa81ef 196 k = strlen(l);
ed5bcfbe
LP
197 assert(k > 0);
198
b2aa81ef 199 if (l[k-1] != ']') {
16354eff 200 log_error("[%s:%u] Invalid section header.", filename, line);
ed5bcfbe
LP
201 return -EBADMSG;
202 }
203
f975e971
LP
204 n = strndup(l+1, k-2);
205 if (!n)
ed5bcfbe
LP
206 return -ENOMEM;
207
f975e971 208 if (sections && !nulstr_contains(sections, n)) {
42f4e3c4 209
f975e971
LP
210 if (!relaxed)
211 log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
212
213 free(n);
214 *section = NULL;
215 } else {
216 free(*section);
217 *section = n;
218 }
ed5bcfbe
LP
219
220 return 0;
221 }
222
62f168a0
LP
223 if (sections && !*section) {
224
225 if (!relaxed)
226 log_info("[%s:%u] Assignment outside of section. Ignoring.", filename, line);
227
10e87ee7 228 return 0;
62f168a0 229 }
10e87ee7 230
f975e971
LP
231 e = strchr(l, '=');
232 if (!e) {
16354eff 233 log_error("[%s:%u] Missing '='.", filename, line);
ed5bcfbe
LP
234 return -EBADMSG;
235 }
236
237 *e = 0;
238 e++;
239
f975e971
LP
240 return next_assignment(
241 filename,
242 line,
243 lookup,
244 table,
245 *section,
246 strstrip(l),
247 strstrip(e),
248 relaxed,
249 userdata);
ed5bcfbe
LP
250}
251
252/* Go through the file and parse each line */
f975e971
LP
253int config_parse(
254 const char *filename,
255 FILE *f,
256 const char *sections,
257 ConfigItemLookup lookup,
258 void *table,
259 bool relaxed,
260 void *userdata) {
261
ed5bcfbe
LP
262 unsigned line = 0;
263 char *section = NULL;
ed5bcfbe 264 int r;
63ad1ab4 265 bool ours = false;
3dab2943 266 char *continuation = NULL;
ed5bcfbe
LP
267
268 assert(filename);
f975e971 269 assert(lookup);
ed5bcfbe 270
87f0e418 271 if (!f) {
f975e971
LP
272 f = fopen(filename, "re");
273 if (!f) {
87f0e418
LP
274 r = -errno;
275 log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
276 goto finish;
277 }
63ad1ab4
LP
278
279 ours = true;
ed5bcfbe
LP
280 }
281
282 while (!feof(f)) {
3dab2943
LP
283 char l[LINE_MAX], *p, *c = NULL, *e;
284 bool escaped = false;
ed5bcfbe
LP
285
286 if (!fgets(l, sizeof(l), f)) {
287 if (feof(f))
288 break;
289
290 r = -errno;
16354eff 291 log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
ed5bcfbe
LP
292 goto finish;
293 }
294
3dab2943
LP
295 truncate_nl(l);
296
297 if (continuation) {
f975e971
LP
298 c = strappend(continuation, l);
299 if (!c) {
3dab2943
LP
300 r = -ENOMEM;
301 goto finish;
302 }
303
304 free(continuation);
305 continuation = NULL;
306 p = c;
307 } else
308 p = l;
309
310 for (e = p; *e; e++) {
311 if (escaped)
312 escaped = false;
313 else if (*e == '\\')
314 escaped = true;
315 }
316
317 if (escaped) {
318 *(e-1) = ' ';
319
320 if (c)
321 continuation = c;
f975e971
LP
322 else {
323 continuation = strdup(l);
8ea913b2 324 if (!continuation) {
f975e971
LP
325 r = -ENOMEM;
326 goto finish;
327 }
3dab2943
LP
328 }
329
330 continue;
331 }
332
f975e971
LP
333 r = parse_line(filename,
334 ++line,
335 sections,
336 lookup,
337 table,
338 relaxed,
339 &section,
340 p,
341 userdata);
3dab2943
LP
342 free(c);
343
344 if (r < 0)
ed5bcfbe
LP
345 goto finish;
346 }
347
348 r = 0;
349
350finish:
351 free(section);
3dab2943 352 free(continuation);
ed5bcfbe 353
63ad1ab4 354 if (f && ours)
ed5bcfbe
LP
355 fclose(f);
356
357 return r;
358}
359
360int config_parse_int(
361 const char *filename,
362 unsigned line,
363 const char *section,
364 const char *lvalue,
2b583ce6 365 int ltype,
ed5bcfbe
LP
366 const char *rvalue,
367 void *data,
368 void *userdata) {
369
370 int *i = data;
371 int r;
372
373 assert(filename);
374 assert(lvalue);
375 assert(rvalue);
376 assert(data);
377
378 if ((r = safe_atoi(rvalue, i)) < 0) {
f975e971
LP
379 log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
380 return 0;
ed5bcfbe
LP
381 }
382
383 return 0;
384}
385
916abb21
LP
386int config_parse_long(
387 const char *filename,
388 unsigned line,
389 const char *section,
390 const char *lvalue,
391 int ltype,
392 const char *rvalue,
393 void *data,
394 void *userdata) {
395
396 long *i = data;
397 int r;
398
399 assert(filename);
400 assert(lvalue);
401 assert(rvalue);
402 assert(data);
403
404 if ((r = safe_atoli(rvalue, i)) < 0) {
f975e971
LP
405 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
406 return 0;
916abb21
LP
407 }
408
409 return 0;
410}
411
ec863ba6
LP
412int config_parse_uint64(
413 const char *filename,
414 unsigned line,
415 const char *section,
416 const char *lvalue,
2b583ce6 417 int ltype,
ec863ba6
LP
418 const char *rvalue,
419 void *data,
420 void *userdata) {
421
422 uint64_t *u = data;
423 int r;
424
425 assert(filename);
426 assert(lvalue);
427 assert(rvalue);
428 assert(data);
429
430 if ((r = safe_atou64(rvalue, u)) < 0) {
f975e971
LP
431 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
432 return 0;
ec863ba6
LP
433 }
434
435 return 0;
436}
437
ed5bcfbe
LP
438int config_parse_unsigned(
439 const char *filename,
440 unsigned line,
441 const char *section,
442 const char *lvalue,
2b583ce6 443 int ltype,
ed5bcfbe
LP
444 const char *rvalue,
445 void *data,
446 void *userdata) {
447
448 unsigned *u = data;
449 int r;
450
451 assert(filename);
452 assert(lvalue);
453 assert(rvalue);
454 assert(data);
455
456 if ((r = safe_atou(rvalue, u)) < 0) {
16354eff 457 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
ed5bcfbe
LP
458 return r;
459 }
460
461 return 0;
462}
463
9ba1a159 464int config_parse_bytes_size(
ed5bcfbe
LP
465 const char *filename,
466 unsigned line,
467 const char *section,
468 const char *lvalue,
2b583ce6 469 int ltype,
ed5bcfbe
LP
470 const char *rvalue,
471 void *data,
472 void *userdata) {
473
474 size_t *sz = data;
9ba1a159 475 off_t o;
ed5bcfbe
LP
476
477 assert(filename);
478 assert(lvalue);
479 assert(rvalue);
480 assert(data);
481
9ba1a159
LP
482 if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
483 log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
484 return 0;
485 }
486
487 *sz = (size_t) o;
488 return 0;
489}
490
491
492int config_parse_bytes_off(
493 const char *filename,
494 unsigned line,
495 const char *section,
496 const char *lvalue,
497 int ltype,
498 const char *rvalue,
499 void *data,
500 void *userdata) {
501
502 off_t *bytes = data;
503
504 assert(filename);
505 assert(lvalue);
506 assert(rvalue);
507 assert(data);
508
509 assert_cc(sizeof(off_t) == sizeof(uint64_t));
510
511 if (parse_bytes(rvalue, bytes) < 0) {
512 log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
f975e971 513 return 0;
ed5bcfbe
LP
514 }
515
ed5bcfbe
LP
516 return 0;
517}
518
519int config_parse_bool(
520 const char *filename,
521 unsigned line,
522 const char *section,
523 const char *lvalue,
2b583ce6 524 int ltype,
ed5bcfbe
LP
525 const char *rvalue,
526 void *data,
527 void *userdata) {
528
529 int k;
530 bool *b = data;
531
532 assert(filename);
533 assert(lvalue);
534 assert(rvalue);
535 assert(data);
536
537 if ((k = parse_boolean(rvalue)) < 0) {
f975e971
LP
538 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
539 return 0;
ed5bcfbe
LP
540 }
541
542 *b = !!k;
543 return 0;
544}
545
8d53b453
LP
546int config_parse_tristate(
547 const char *filename,
548 unsigned line,
549 const char *section,
550 const char *lvalue,
551 int ltype,
552 const char *rvalue,
553 void *data,
554 void *userdata) {
555
556 int k;
557 int *b = data;
558
559 assert(filename);
560 assert(lvalue);
561 assert(rvalue);
562 assert(data);
563
564 /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
565
566 k = parse_boolean(rvalue);
567 if (k < 0) {
568 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
569 return 0;
570 }
571
572 *b = !!k;
573 return 0;
574}
575
ed5bcfbe
LP
576int config_parse_string(
577 const char *filename,
578 unsigned line,
579 const char *section,
580 const char *lvalue,
2b583ce6 581 int ltype,
ed5bcfbe
LP
582 const char *rvalue,
583 void *data,
584 void *userdata) {
585
586 char **s = data;
587 char *n;
588
589 assert(filename);
590 assert(lvalue);
591 assert(rvalue);
592 assert(data);
593
7f110ff9
LP
594 n = cunescape(rvalue);
595 if (!n)
596 return -ENOMEM;
597
598 if (!utf8_is_valid(n)) {
599 log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
600 free(n);
601 return 0;
602 }
ed5bcfbe
LP
603
604 free(*s);
7f110ff9
LP
605 if (*n)
606 *s = n;
607 else {
608 free(n);
609 *s = NULL;
610 }
ed5bcfbe
LP
611
612 return 0;
613}
57d42a5f 614
034c6ed7
LP
615int config_parse_path(
616 const char *filename,
617 unsigned line,
618 const char *section,
619 const char *lvalue,
2b583ce6 620 int ltype,
034c6ed7
LP
621 const char *rvalue,
622 void *data,
623 void *userdata) {
624
625 char **s = data;
626 char *n;
627
628 assert(filename);
629 assert(lvalue);
630 assert(rvalue);
631 assert(data);
632
7f110ff9
LP
633 if (!utf8_is_valid(rvalue)) {
634 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
635 return 0;
636 }
637
15ae422b 638 if (!path_is_absolute(rvalue)) {
f975e971
LP
639 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
640 return 0;
034c6ed7
LP
641 }
642
7f110ff9
LP
643 n = strdup(rvalue);
644 if (!n)
034c6ed7
LP
645 return -ENOMEM;
646
01f78473
LP
647 path_kill_slashes(n);
648
034c6ed7
LP
649 free(*s);
650 *s = n;
651
652 return 0;
653}
57d42a5f
LP
654
655int config_parse_strv(
656 const char *filename,
657 unsigned line,
658 const char *section,
659 const char *lvalue,
2b583ce6 660 int ltype,
57d42a5f
LP
661 const char *rvalue,
662 void *data,
663 void *userdata) {
664
665 char*** sv = data;
666 char **n;
667 char *w;
668 unsigned k;
669 size_t l;
670 char *state;
7f110ff9 671 int r;
57d42a5f
LP
672
673 assert(filename);
674 assert(lvalue);
675 assert(rvalue);
676 assert(data);
677
678 k = strv_length(*sv);
034c6ed7 679 FOREACH_WORD_QUOTED(w, l, rvalue, state)
57d42a5f
LP
680 k++;
681
7f110ff9
LP
682 n = new(char*, k+1);
683 if (!n)
57d42a5f
LP
684 return -ENOMEM;
685
3251df7d
LP
686 if (*sv)
687 for (k = 0; (*sv)[k]; k++)
688 n[k] = (*sv)[k];
7418040c
LP
689 else
690 k = 0;
3251df7d 691
7f110ff9
LP
692 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
693 n[k] = cunescape_length(w, l);
694 if (!n[k]) {
695 r = -ENOMEM;
57d42a5f 696 goto fail;
7f110ff9
LP
697 }
698
699 if (!utf8_is_valid(n[k])) {
700 log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
701 free(n[k]);
702 continue;
703 }
704
705 k++;
706 }
57d42a5f
LP
707
708 n[k] = NULL;
709 free(*sv);
710 *sv = n;
711
712 return 0;
713
714fail:
715 for (; k > 0; k--)
716 free(n[k-1]);
034c6ed7 717 free(n);
57d42a5f 718
7f110ff9 719 return r;
57d42a5f 720}
15ae422b
LP
721
722int config_parse_path_strv(
723 const char *filename,
724 unsigned line,
725 const char *section,
726 const char *lvalue,
2b583ce6 727 int ltype,
15ae422b
LP
728 const char *rvalue,
729 void *data,
730 void *userdata) {
731
732 char*** sv = data;
733 char **n;
734 char *w;
735 unsigned k;
736 size_t l;
737 char *state;
738 int r;
739
740 assert(filename);
741 assert(lvalue);
742 assert(rvalue);
743 assert(data);
744
745 k = strv_length(*sv);
746 FOREACH_WORD_QUOTED(w, l, rvalue, state)
747 k++;
748
7f110ff9
LP
749 n = new(char*, k+1);
750 if (!n)
15ae422b
LP
751 return -ENOMEM;
752
753 k = 0;
754 if (*sv)
755 for (; (*sv)[k]; k++)
756 n[k] = (*sv)[k];
757
758 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
7f110ff9
LP
759 n[k] = strndup(w, l);
760 if (!n[k]) {
15ae422b
LP
761 r = -ENOMEM;
762 goto fail;
763 }
764
7f110ff9
LP
765 if (!utf8_is_valid(n[k])) {
766 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
767 free(n[k]);
768 continue;
769 }
770
15ae422b 771 if (!path_is_absolute(n[k])) {
f975e971
LP
772 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
773 free(n[k]);
774 continue;
15ae422b
LP
775 }
776
01f78473 777 path_kill_slashes(n[k]);
15ae422b
LP
778 k++;
779 }
780
781 n[k] = NULL;
782 free(*sv);
783 *sv = n;
784
785 return 0;
786
787fail:
15ae422b
LP
788 for (; k > 0; k--)
789 free(n[k-1]);
790 free(n);
791
792 return r;
793}
f975e971
LP
794
795int config_parse_usec(
796 const char *filename,
797 unsigned line,
798 const char *section,
799 const char *lvalue,
800 int ltype,
801 const char *rvalue,
802 void *data,
803 void *userdata) {
804
805 usec_t *usec = data;
806
807 assert(filename);
808 assert(lvalue);
809 assert(rvalue);
810 assert(data);
811
812 if (parse_usec(rvalue, usec) < 0) {
813 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
814 return 0;
815 }
816
817 return 0;
818}
819
820int config_parse_mode(
821 const char *filename,
822 unsigned line,
823 const char *section,
824 const char *lvalue,
825 int ltype,
826 const char *rvalue,
827 void *data,
828 void *userdata) {
829
830 mode_t *m = data;
831 long l;
832 char *x = NULL;
833
834 assert(filename);
835 assert(lvalue);
836 assert(rvalue);
837 assert(data);
838
839 errno = 0;
840 l = strtol(rvalue, &x, 8);
841 if (!x || *x || errno) {
842 log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
843 return 0;
844 }
845
846 if (l < 0000 || l > 07777) {
847 log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
848 return 0;
849 }
850
851 *m = (mode_t) l;
852 return 0;
853}