chiark / gitweb /
build-sys: make PolicyKit support compile-time optional (was runtime-optional already)
[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_bytes_size(
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         size_t *sz = data;
481         off_t o;
482
483         assert(filename);
484         assert(lvalue);
485         assert(rvalue);
486         assert(data);
487
488         if (parse_bytes(rvalue, &o) < 0 || (off_t) (size_t) o != o) {
489                 log_error("[%s:%u] Failed to parse byte value, ignoring: %s", filename, line, rvalue);
490                 return 0;
491         }
492
493         *sz = (size_t) o;
494         return 0;
495 }
496
497
498 int config_parse_bytes_off(
499                 const char *filename,
500                 unsigned line,
501                 const char *section,
502                 const char *lvalue,
503                 int ltype,
504                 const char *rvalue,
505                 void *data,
506                 void *userdata) {
507
508         off_t *bytes = data;
509
510         assert(filename);
511         assert(lvalue);
512         assert(rvalue);
513         assert(data);
514
515         assert_cc(sizeof(off_t) == sizeof(uint64_t));
516
517         if (parse_bytes(rvalue, bytes) < 0) {
518                 log_error("[%s:%u] Failed to parse bytes value, ignoring: %s", filename, line, rvalue);
519                 return 0;
520         }
521
522         return 0;
523 }
524
525 int config_parse_bool(
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         int k;
536         bool *b = data;
537
538         assert(filename);
539         assert(lvalue);
540         assert(rvalue);
541         assert(data);
542
543         if ((k = parse_boolean(rvalue)) < 0) {
544                 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
545                 return 0;
546         }
547
548         *b = !!k;
549         return 0;
550 }
551
552 int config_parse_tristate(
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         int *b = data;
564
565         assert(filename);
566         assert(lvalue);
567         assert(rvalue);
568         assert(data);
569
570         /* Tristates are like booleans, but can also take the 'default' value, i.e. "-1" */
571
572         k = parse_boolean(rvalue);
573         if (k < 0) {
574                 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
575                 return 0;
576         }
577
578         *b = !!k;
579         return 0;
580 }
581
582 int config_parse_string(
583                 const char *filename,
584                 unsigned line,
585                 const char *section,
586                 const char *lvalue,
587                 int ltype,
588                 const char *rvalue,
589                 void *data,
590                 void *userdata) {
591
592         char **s = data;
593         char *n;
594
595         assert(filename);
596         assert(lvalue);
597         assert(rvalue);
598         assert(data);
599
600         n = strdup(rvalue);
601         if (!n)
602                 return log_oom();
603
604         if (!utf8_is_valid(n)) {
605                 log_error("[%s:%u] String is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
606                 free(n);
607                 return 0;
608         }
609
610         free(*s);
611         if (*n)
612                 *s = n;
613         else {
614                 free(n);
615                 *s = NULL;
616         }
617
618         return 0;
619 }
620
621 int config_parse_path(
622                 const char *filename,
623                 unsigned line,
624                 const char *section,
625                 const char *lvalue,
626                 int ltype,
627                 const char *rvalue,
628                 void *data,
629                 void *userdata) {
630
631         char **s = data;
632         char *n;
633
634         assert(filename);
635         assert(lvalue);
636         assert(rvalue);
637         assert(data);
638
639         if (!utf8_is_valid(rvalue)) {
640                 log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
641                 return 0;
642         }
643
644         if (!path_is_absolute(rvalue)) {
645                 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
646                 return 0;
647         }
648
649         n = strdup(rvalue);
650         if (!n)
651                 return log_oom();
652
653         path_kill_slashes(n);
654
655         free(*s);
656         *s = n;
657
658         return 0;
659 }
660
661 int config_parse_strv(
662                 const char *filename,
663                 unsigned line,
664                 const char *section,
665                 const char *lvalue,
666                 int ltype,
667                 const char *rvalue,
668                 void *data,
669                 void *userdata) {
670
671         char *** sv = data, *w, *state;
672         size_t l;
673         int r;
674
675         assert(filename);
676         assert(lvalue);
677         assert(rvalue);
678         assert(data);
679
680         if (isempty(rvalue)) {
681                 /* Empty assignment resets the list */
682                 strv_free(*sv);
683                 *sv = NULL;
684                 return 0;
685         }
686
687         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
688                 _cleanup_free_ char *n;
689
690                 n = cunescape_length(w, l);
691                 if (!n)
692                         return log_oom();
693
694                 if (!utf8_is_valid(n)) {
695                         log_error("[%s:%u] String is not UTF-8 clean, ignoring: %s", filename, line, rvalue);
696                         continue;
697                 }
698
699                 r = strv_extend(sv, n);
700                 if (r < 0)
701                         return log_oom();
702         }
703
704         return 0;
705 }
706
707 int config_parse_path_strv(
708                 const char *filename,
709                 unsigned line,
710                 const char *section,
711                 const char *lvalue,
712                 int ltype,
713                 const char *rvalue,
714                 void *data,
715                 void *userdata) {
716
717         char*** sv = data, *w, *state;
718         size_t l;
719         int r;
720
721         assert(filename);
722         assert(lvalue);
723         assert(rvalue);
724         assert(data);
725
726         if (isempty(rvalue)) {
727                 /* Empty assignment resets the list */
728                 strv_free(*sv);
729                 *sv = NULL;
730                 return 0;
731         }
732
733         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
734                 _cleanup_free_ char *n;
735
736                 n = strndup(w, l);
737                 if (!n)
738                         return log_oom();
739
740                 if (!utf8_is_valid(n)) {
741                         log_error("[%s:%u] Path is not UTF-8 clean, ignoring assignment: %s", filename, line, rvalue);
742                         continue;
743                 }
744
745                 if (!path_is_absolute(n)) {
746                         log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
747                         continue;
748                 }
749
750                 path_kill_slashes(n);
751                 r = strv_extend(sv, n);
752                 if (r < 0)
753                         return log_oom();
754         }
755
756         return 0;
757 }
758
759 int config_parse_usec(
760                 const char *filename,
761                 unsigned line,
762                 const char *section,
763                 const char *lvalue,
764                 int ltype,
765                 const char *rvalue,
766                 void *data,
767                 void *userdata) {
768
769         usec_t *usec = data;
770
771         assert(filename);
772         assert(lvalue);
773         assert(rvalue);
774         assert(data);
775
776         if (parse_usec(rvalue, usec) < 0) {
777                 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
778                 return 0;
779         }
780
781         return 0;
782 }
783
784 int config_parse_nsec(
785                 const char *filename,
786                 unsigned line,
787                 const char *section,
788                 const char *lvalue,
789                 int ltype,
790                 const char *rvalue,
791                 void *data,
792                 void *userdata) {
793
794         nsec_t *nsec = data;
795
796         assert(filename);
797         assert(lvalue);
798         assert(rvalue);
799         assert(data);
800
801         if (parse_nsec(rvalue, nsec) < 0) {
802                 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
803                 return 0;
804         }
805
806         return 0;
807 }
808
809 int config_parse_mode(
810                 const char *filename,
811                 unsigned line,
812                 const char *section,
813                 const char *lvalue,
814                 int ltype,
815                 const char *rvalue,
816                 void *data,
817                 void *userdata) {
818
819         mode_t *m = data;
820         long l;
821         char *x = NULL;
822
823         assert(filename);
824         assert(lvalue);
825         assert(rvalue);
826         assert(data);
827
828         errno = 0;
829         l = strtol(rvalue, &x, 8);
830         if (!x || x == rvalue || *x || errno) {
831                 log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
832                 return 0;
833         }
834
835         if (l < 0000 || l > 07777) {
836                 log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
837                 return 0;
838         }
839
840         *m = (mode_t) l;
841         return 0;
842 }
843
844 int config_parse_facility(
845                 const char *filename,
846                 unsigned line,
847                 const char *section,
848                 const char *lvalue,
849                 int ltype,
850                 const char *rvalue,
851                 void *data,
852                 void *userdata) {
853
854
855         int *o = data, x;
856
857         assert(filename);
858         assert(lvalue);
859         assert(rvalue);
860         assert(data);
861
862         x = log_facility_unshifted_from_string(rvalue);
863         if (x < 0) {
864                 log_error("[%s:%u] Failed to parse log facility, ignoring: %s", filename, line, rvalue);
865                 return 0;
866         }
867
868         *o = (x << 3) | LOG_PRI(*o);
869
870         return 0;
871 }
872
873 int config_parse_level(
874                 const char *filename,
875                 unsigned line,
876                 const char *section,
877                 const char *lvalue,
878                 int ltype,
879                 const char *rvalue,
880                 void *data,
881                 void *userdata) {
882
883
884         int *o = data, x;
885
886         assert(filename);
887         assert(lvalue);
888         assert(rvalue);
889         assert(data);
890
891         x = log_level_from_string(rvalue);
892         if (x < 0) {
893                 log_error("[%s:%u] Failed to parse log level, ignoring: %s", filename, line, rvalue);
894                 return 0;
895         }
896
897         *o = (*o & LOG_FACMASK) | x;
898         return 0;
899 }
900
901 int config_parse_set_status(
902                 const char *filename,
903                 unsigned line,
904                 const char *section,
905                 const char *lvalue,
906                 int ltype,
907                 const char *rvalue,
908                 void *data,
909                 void *userdata) {
910
911         char *w;
912         size_t l;
913         char *state;
914         int r;
915         ExitStatusSet *status_set = data;
916
917         assert(filename);
918         assert(lvalue);
919         assert(rvalue);
920         assert(data);
921
922         if (isempty(rvalue)) {
923                 /* Empty assignment resets the list */
924
925                 set_free(status_set->signal);
926                 set_free(status_set->code);
927
928                 status_set->signal = status_set->code = NULL;
929                 return 0;
930         }
931
932         FOREACH_WORD(w, l, rvalue, state) {
933                 int val;
934                 char *temp;
935
936                 temp = strndup(w, l);
937                 if (!temp)
938                         return log_oom();
939
940                 r = safe_atoi(temp, &val);
941                 if (r < 0) {
942                         val = signal_from_string_try_harder(temp);
943                         free(temp);
944
945                         if (val > 0) {
946                                 r = set_ensure_allocated(&status_set->signal, trivial_hash_func, trivial_compare_func);
947                                 if (r < 0)
948                                         return log_oom();
949
950                                 r = set_put(status_set->signal, INT_TO_PTR(val));
951                                 if (r < 0) {
952                                         log_error("[%s:%u] Unable to store: %s", filename, line, w);
953                                         return r;
954                                 }
955                         } else {
956                                 log_error("[%s:%u] Failed to parse value, ignoring: %s", filename, line, w);
957                                 return 0;
958                         }
959                 } else {
960                         free(temp);
961
962                         if (val < 0 || val > 255)
963                                 log_warning("[%s:%u] Value %d is outside range 0-255, ignoring", filename, line, val);
964                         else {
965                                 r = set_ensure_allocated(&status_set->code, trivial_hash_func, trivial_compare_func);
966                                 if (r < 0)
967                                         return log_oom();
968
969                                 r = set_put(status_set->code, INT_TO_PTR(val));
970                                 if (r < 0) {
971                                         log_error("[%s:%u] Unable to store: %s", filename, line, w);
972                                         return r;
973                                 }
974                         }
975                 }
976         }
977
978         return 0;
979 }