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