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