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