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