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