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