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