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