chiark / gitweb /
bootchart: use conf-parser & CamelCase names in .conf
[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, *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 *section = NULL;
266         int r;
267         bool ours = false;
268         char *continuation = NULL;
269
270         assert(filename);
271         assert(lookup);
272
273         if (!f) {
274                 f = fopen(filename, "re");
275                 if (!f) {
276                         r = -errno;
277                         log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
278                         goto finish;
279                 }
280
281                 ours = true;
282         }
283
284         while (!feof(f)) {
285                 char l[LINE_MAX], *p, *c = NULL, *e;
286                 bool escaped = false;
287
288                 if (!fgets(l, sizeof(l), f)) {
289                         if (feof(f))
290                                 break;
291
292                         r = -errno;
293                         log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
294                         goto finish;
295                 }
296
297                 truncate_nl(l);
298
299                 if (continuation) {
300                         c = strappend(continuation, l);
301                         if (!c) {
302                                 r = -ENOMEM;
303                                 goto finish;
304                         }
305
306                         free(continuation);
307                         continuation = NULL;
308                         p = c;
309                 } else
310                         p = l;
311
312                 for (e = p; *e; e++) {
313                         if (escaped)
314                                 escaped = false;
315                         else if (*e == '\\')
316                                 escaped = true;
317                 }
318
319                 if (escaped) {
320                         *(e-1) = ' ';
321
322                         if (c)
323                                 continuation = c;
324                         else {
325                                 continuation = strdup(l);
326                                 if (!continuation) {
327                                         r = -ENOMEM;
328                                         goto finish;
329                                 }
330                         }
331
332                         continue;
333                 }
334
335                 r = parse_line(filename,
336                                 ++line,
337                                 sections,
338                                 lookup,
339                                 table,
340                                 relaxed,
341                                 &section,
342                                 p,
343                                 userdata);
344                 free(c);
345
346                 if (r < 0)
347                         goto finish;
348         }
349
350         r = 0;
351
352 finish:
353         free(section);
354         free(continuation);
355
356         if (f && ours)
357                 fclose(f);
358
359         return r;
360 }
361
362 int config_parse_int(
363                 const char *filename,
364                 unsigned line,
365                 const char *section,
366                 const char *lvalue,
367                 int ltype,
368                 const char *rvalue,
369                 void *data,
370                 void *userdata) {
371
372         int *i = data;
373         int r;
374
375         assert(filename);
376         assert(lvalue);
377         assert(rvalue);
378         assert(data);
379
380         r = safe_atoi(rvalue, i);
381         if (r < 0) {
382                 log_error("[%s:%u] Failed to parse numeric value, ingoring: %s", filename, line, rvalue);
383                 return 0;
384         }
385
386         return 0;
387 }
388
389 int config_parse_long(
390                 const char *filename,
391                 unsigned line,
392                 const char *section,
393                 const char *lvalue,
394                 int ltype,
395                 const char *rvalue,
396                 void *data,
397                 void *userdata) {
398
399         long *i = data;
400         int r;
401
402         assert(filename);
403         assert(lvalue);
404         assert(rvalue);
405         assert(data);
406
407         r = safe_atoli(rvalue, i);
408         if (r < 0) {
409                 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
410                 return 0;
411         }
412
413         return 0;
414 }
415
416 int config_parse_uint64(
417                 const char *filename,
418                 unsigned line,
419                 const char *section,
420                 const char *lvalue,
421                 int ltype,
422                 const char *rvalue,
423                 void *data,
424                 void *userdata) {
425
426         uint64_t *u = data;
427         int r;
428
429         assert(filename);
430         assert(lvalue);
431         assert(rvalue);
432         assert(data);
433
434         r = safe_atou64(rvalue, u);
435         if (r < 0) {
436                 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
437                 return 0;
438         }
439
440         return 0;
441 }
442
443 int config_parse_unsigned(
444                 const char *filename,
445                 unsigned line,
446                 const char *section,
447                 const char *lvalue,
448                 int ltype,
449                 const char *rvalue,
450                 void *data,
451                 void *userdata) {
452
453         unsigned *u = data;
454         int r;
455
456         assert(filename);
457         assert(lvalue);
458         assert(rvalue);
459         assert(data);
460
461         r = safe_atou(rvalue, u);
462         if (r < 0) {
463                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
464                 return r;
465         }
466
467         return 0;
468 }
469
470 int config_parse_double(
471                 const char *filename,
472                 unsigned line,
473                 const char *section,
474                 const char *lvalue,
475                 int ltype,
476                 const char *rvalue,
477                 void *data,
478                 void *userdata) {
479
480         double *d = data;
481         int r;
482
483         assert(filename);
484         assert(lvalue);
485         assert(rvalue);
486         assert(data);
487
488         r = safe_atod(rvalue, d);
489         if (r < 0) {
490                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
491                 return r;
492         }
493
494         return 0;
495 }
496
497 int config_parse_bytes_size(
498                 const char *filename,
499                 unsigned line,
500                 const char *section,
501                 const char *lvalue,
502                 int ltype,
503                 const char *rvalue,
504                 void *data,
505                 void *userdata) {
506
507         size_t *sz = data;
508         off_t o;
509
510         assert(filename);
511         assert(lvalue);
512         assert(rvalue);
513         assert(data);
514
515         if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
516                 log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
517                 return 0;
518         }
519
520         *sz = (size_t) o;
521         return 0;
522 }
523
524
525 int config_parse_bytes_off(
526                 const char *filename,
527                 unsigned line,
528                 const char *section,
529                 const char *lvalue,
530                 int ltype,
531                 const char *rvalue,
532                 void *data,
533                 void *userdata) {
534
535         off_t *bytes = data;
536
537         assert(filename);
538         assert(lvalue);
539         assert(rvalue);
540         assert(data);
541
542         assert_cc(sizeof(off_t) == sizeof(uint64_t));
543
544         if (parse_bytes(rvalue, bytes) < 0) {
545                 log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
546                 return 0;
547         }
548
549         return 0;
550 }
551
552 int config_parse_bool(
553                 const char *filename,
554                 unsigned line,
555                 const char *section,
556                 const char *lvalue,
557                 int ltype,
558                 const char *rvalue,
559                 void *data,
560                 void *userdata) {
561
562         int k;
563         bool *b = data;
564
565         assert(filename);
566         assert(lvalue);
567         assert(rvalue);
568         assert(data);
569
570         if ((k = parse_boolean(rvalue)) < 0) {
571                 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
572                 return 0;
573         }
574
575         *b = !!k;
576         return 0;
577 }
578
579 int config_parse_tristate(
580                 const char *filename,
581                 unsigned line,
582                 const char *section,
583                 const char *lvalue,
584                 int ltype,
585                 const char *rvalue,
586                 void *data,
587                 void *userdata) {
588
589         int k;
590         int *b = data;
591
592         assert(filename);
593         assert(lvalue);
594         assert(rvalue);
595         assert(data);
596
597         /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
598
599         k = parse_boolean(rvalue);
600         if (k < 0) {
601                 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
602                 return 0;
603         }
604
605         *b = !!k;
606         return 0;
607 }
608
609 int config_parse_string(
610                 const char *filename,
611                 unsigned line,
612                 const char *section,
613                 const char *lvalue,
614                 int ltype,
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
627         n = strdup(rvalue);
628         if (!n)
629                 return log_oom();
630
631         if (!utf8_is_valid(n)) {
632                 log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
633                 free(n);
634                 return 0;
635         }
636
637         free(*s);
638         if (*n)
639                 *s = n;
640         else {
641                 free(n);
642                 *s = NULL;
643         }
644
645         return 0;
646 }
647
648 int config_parse_path(
649                 const char *filename,
650                 unsigned line,
651                 const char *section,
652                 const char *lvalue,
653                 int ltype,
654                 const char *rvalue,
655                 void *data,
656                 void *userdata) {
657
658         char **s = data;
659         char *n;
660
661         assert(filename);
662         assert(lvalue);
663         assert(rvalue);
664         assert(data);
665
666         if (!utf8_is_valid(rvalue)) {
667                 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
668                 return 0;
669         }
670
671         if (!path_is_absolute(rvalue)) {
672                 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
673                 return 0;
674         }
675
676         n = strdup(rvalue);
677         if (!n)
678                 return log_oom();
679
680         path_kill_slashes(n);
681
682         free(*s);
683         *s = n;
684
685         return 0;
686 }
687
688 int config_parse_strv(
689                 const char *filename,
690                 unsigned line,
691                 const char *section,
692                 const char *lvalue,
693                 int ltype,
694                 const char *rvalue,
695                 void *data,
696                 void *userdata) {
697
698         char *** sv = data, *w, *state;
699         size_t l;
700         int r;
701
702         assert(filename);
703         assert(lvalue);
704         assert(rvalue);
705         assert(data);
706
707         if (isempty(rvalue)) {
708                 /* Empty assignment resets the list */
709                 strv_free(*sv);
710                 *sv = NULL;
711                 return 0;
712         }
713
714         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
715                 _cleanup_free_ char *n;
716
717                 n = cunescape_length(w, l);
718                 if (!n)
719                         return log_oom();
720
721                 if (!utf8_is_valid(n)) {
722                         log_error("[%s:%u] String is not UTF-8 clean, ignoring: %s", filename, line, rvalue);
723                         continue;
724                 }
725
726                 r = strv_extend(sv, n);
727                 if (r < 0)
728                         return log_oom();
729         }
730
731         return 0;
732 }
733
734 int config_parse_path_strv(
735                 const char *filename,
736                 unsigned line,
737                 const char *section,
738                 const char *lvalue,
739                 int ltype,
740                 const char *rvalue,
741                 void *data,
742                 void *userdata) {
743
744         char*** sv = data, *w, *state;
745         size_t l;
746         int r;
747
748         assert(filename);
749         assert(lvalue);
750         assert(rvalue);
751         assert(data);
752
753         if (isempty(rvalue)) {
754                 /* Empty assignment resets the list */
755                 strv_free(*sv);
756                 *sv = NULL;
757                 return 0;
758         }
759
760         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
761                 _cleanup_free_ char *n;
762
763                 n = strndup(w, l);
764                 if (!n)
765                         return log_oom();
766
767                 if (!utf8_is_valid(n)) {
768                         log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
769                         continue;
770                 }
771
772                 if (!path_is_absolute(n)) {
773                         log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
774                         continue;
775                 }
776
777                 path_kill_slashes(n);
778                 r = strv_extend(sv, n);
779                 if (r < 0)
780                         return log_oom();
781         }
782
783         return 0;
784 }
785
786 int config_parse_usec(
787                 const char *filename,
788                 unsigned line,
789                 const char *section,
790                 const char *lvalue,
791                 int ltype,
792                 const char *rvalue,
793                 void *data,
794                 void *userdata) {
795
796         usec_t *usec = data;
797
798         assert(filename);
799         assert(lvalue);
800         assert(rvalue);
801         assert(data);
802
803         if (parse_usec(rvalue, usec) < 0) {
804                 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
805                 return 0;
806         }
807
808         return 0;
809 }
810
811 int config_parse_nsec(
812                 const char *filename,
813                 unsigned line,
814                 const char *section,
815                 const char *lvalue,
816                 int ltype,
817                 const char *rvalue,
818                 void *data,
819                 void *userdata) {
820
821         nsec_t *nsec = data;
822
823         assert(filename);
824         assert(lvalue);
825         assert(rvalue);
826         assert(data);
827
828         if (parse_nsec(rvalue, nsec) < 0) {
829                 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
830                 return 0;
831         }
832
833         return 0;
834 }
835
836 int config_parse_mode(
837                 const char *filename,
838                 unsigned line,
839                 const char *section,
840                 const char *lvalue,
841                 int ltype,
842                 const char *rvalue,
843                 void *data,
844                 void *userdata) {
845
846         mode_t *m = data;
847         long l;
848         char *x = NULL;
849
850         assert(filename);
851         assert(lvalue);
852         assert(rvalue);
853         assert(data);
854
855         errno = 0;
856         l = strtol(rvalue, &x, 8);
857         if (!x || x == rvalue || *x || errno) {
858                 log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
859                 return 0;
860         }
861
862         if (l < 0000 || l > 07777) {
863                 log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
864                 return 0;
865         }
866
867         *m = (mode_t) l;
868         return 0;
869 }
870
871 int config_parse_facility(
872                 const char *filename,
873                 unsigned line,
874                 const char *section,
875                 const char *lvalue,
876                 int ltype,
877                 const char *rvalue,
878                 void *data,
879                 void *userdata) {
880
881
882         int *o = data, x;
883
884         assert(filename);
885         assert(lvalue);
886         assert(rvalue);
887         assert(data);
888
889         x = log_facility_unshifted_from_string(rvalue);
890         if (x < 0) {
891                 log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
892                 return 0;
893         }
894
895         *o = (x << 3) | LOG_PRI(*o);
896
897         return 0;
898 }
899
900 int config_parse_level(
901                 const char *filename,
902                 unsigned line,
903                 const char *section,
904                 const char *lvalue,
905                 int ltype,
906                 const char *rvalue,
907                 void *data,
908                 void *userdata) {
909
910
911         int *o = data, x;
912
913         assert(filename);
914         assert(lvalue);
915         assert(rvalue);
916         assert(data);
917
918         x = log_level_from_string(rvalue);
919         if (x < 0) {
920                 log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
921                 return 0;
922         }
923
924         *o = (*o & LOG_FACMASK) | x;
925         return 0;
926 }
927
928 int config_parse_set_status(
929                 const char *filename,
930                 unsigned line,
931                 const char *section,
932                 const char *lvalue,
933                 int ltype,
934                 const char *rvalue,
935                 void *data,
936                 void *userdata) {
937
938         char *w;
939         size_t l;
940         char *state;
941         int r;
942         ExitStatusSet *status_set = data;
943
944         assert(filename);
945         assert(lvalue);
946         assert(rvalue);
947         assert(data);
948
949         if (isempty(rvalue)) {
950                 /* Empty assignment resets the list */
951
952                 set_free(status_set->signal);
953                 set_free(status_set->code);
954
955                 status_set->signal = status_set->code = NULL;
956                 return 0;
957         }
958
959         FOREACH_WORD(w, l, rvalue, state) {
960                 int val;
961                 char *temp;
962
963                 temp = strndup(w, l);
964                 if (!temp)
965                         return log_oom();
966
967                 r = safe_atoi(temp, &val);
968                 if (r < 0) {
969                         val = signal_from_string_try_harder(temp);
970                         free(temp);
971
972                         if (val > 0) {
973                                 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
974                                 if (r < 0)
975                                         return log_oom();
976
977                                 r = set_put(status_set->signal, INT_TO_PTR(val));
978                                 if (r < 0) {
979                                         log_error("[%s:%u] Unable to store: %s", filename, line, w);
980                                         return r;
981                                 }
982                         } else {
983                                 log_error("[%s:%u] Failed to parse value, ignoring: %s", filename, line, w);
984                                 return 0;
985                         }
986                 } else {
987                         free(temp);
988
989                         if (val < 0 || val > 255)
990                                 log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
991                         else {
992                                 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
993                                 if (r < 0)
994                                         return log_oom();
995
996                                 r = set_put(status_set->code, INT_TO_PTR(val));
997                                 if (r < 0) {
998                                         log_error("[%s:%u] Unable to store: %s", filename, line, w);
999                                         return r;
1000                                 }
1001                         }
1002                 }
1003         }
1004
1005         return 0;
1006 }