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