chiark / gitweb /
json: fix a mem leak
[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 <stdlib.h>
26
27 #include "conf-parser.h"
28 #include "conf-files.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 "sd-messages.h"
36
37 int config_item_table_lookup(
38                 const void *table,
39                 const char *section,
40                 const char *lvalue,
41                 ConfigParserCallback *func,
42                 int *ltype,
43                 void **data,
44                 void *userdata) {
45
46         const ConfigTableItem *t;
47
48         assert(table);
49         assert(lvalue);
50         assert(func);
51         assert(ltype);
52         assert(data);
53
54         for (t = table; t->lvalue; t++) {
55
56                 if (!streq(lvalue, t->lvalue))
57                         continue;
58
59                 if (!streq_ptr(section, t->section))
60                         continue;
61
62                 *func = t->parse;
63                 *ltype = t->ltype;
64                 *data = t->data;
65                 return 1;
66         }
67
68         return 0;
69 }
70
71 int config_item_perf_lookup(
72                 const void *table,
73                 const char *section,
74                 const char *lvalue,
75                 ConfigParserCallback *func,
76                 int *ltype,
77                 void **data,
78                 void *userdata) {
79
80         ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
81         const ConfigPerfItem *p;
82
83         assert(table);
84         assert(lvalue);
85         assert(func);
86         assert(ltype);
87         assert(data);
88
89         if (!section)
90                 p = lookup(lvalue, strlen(lvalue));
91         else {
92                 char *key;
93
94                 key = strjoin(section, ".", lvalue, NULL);
95                 if (!key)
96                         return -ENOMEM;
97
98                 p = lookup(key, strlen(key));
99                 free(key);
100         }
101
102         if (!p)
103                 return 0;
104
105         *func = p->parse;
106         *ltype = p->ltype;
107         *data = (uint8_t*) userdata + p->offset;
108         return 1;
109 }
110
111 /* Run the user supplied parser for an assignment */
112 static int next_assignment(const char *unit,
113                            const char *filename,
114                            unsigned line,
115                            ConfigItemLookup lookup,
116                            const void *table,
117                            const char *section,
118                            unsigned section_line,
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(unit, filename, line, section, section_line,
142                                     lvalue, ltype, rvalue, data, userdata);
143
144                 return 0;
145         }
146
147         /* Warn about unknown non-extension fields. */
148         if (!relaxed && !startswith(lvalue, "X-"))
149                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
150                            "Unknown lvalue '%s' in section '%s'", lvalue, section);
151
152         return 0;
153 }
154
155 /* Parse a variable assignment line */
156 static int parse_line(const char* unit,
157                       const char *filename,
158                       unsigned line,
159                       const char *sections,
160                       ConfigItemLookup lookup,
161                       const void *table,
162                       bool relaxed,
163                       bool allow_include,
164                       char **section,
165                       unsigned *section_line,
166                       bool *section_ignored,
167                       char *l,
168                       void *userdata) {
169
170         char *e;
171
172         assert(filename);
173         assert(line > 0);
174         assert(lookup);
175         assert(l);
176
177         l = strstrip(l);
178
179         if (!*l)
180                 return 0;
181
182         if (strchr(COMMENTS "\n", *l))
183                 return 0;
184
185         if (startswith(l, ".include ")) {
186                 _cleanup_free_ char *fn = NULL;
187
188                 /* .includes are a bad idea, we only support them here
189                  * for historical reasons. They create cyclic include
190                  * problems and make it difficult to detect
191                  * configuration file changes with an easy
192                  * stat(). Better approaches, such as .d/ drop-in
193                  * snippets exist.
194                  *
195                  * Support for them should be eventually removed. */
196
197                 if (!allow_include) {
198                         log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
199                                    ".include not allowed here. Ignoring.");
200                         return 0;
201                 }
202
203                 fn = file_in_same_dir(filename, strstrip(l+9));
204                 if (!fn)
205                         return -ENOMEM;
206
207                 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata);
208         }
209
210         if (*l == '[') {
211                 size_t k;
212                 char *n;
213
214                 k = strlen(l);
215                 assert(k > 0);
216
217                 if (l[k-1] != ']') {
218                         log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
219                                    "Invalid section header '%s'", l);
220                         return -EBADMSG;
221                 }
222
223                 n = strndup(l+1, k-2);
224                 if (!n)
225                         return -ENOMEM;
226
227                 if (sections && !nulstr_contains(sections, n)) {
228
229                         if (!relaxed && !startswith(n, "X-"))
230                                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
231                                            "Unknown section '%s'. Ignoring.", n);
232
233                         free(n);
234                         free(*section);
235                         *section = NULL;
236                         *section_line = 0;
237                         *section_ignored = true;
238                 } else {
239                         free(*section);
240                         *section = n;
241                         *section_line = line;
242                         *section_ignored = false;
243                 }
244
245                 return 0;
246         }
247
248         if (sections && !*section) {
249
250                 if (!relaxed && !*section_ignored)
251                         log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
252                                    "Assignment outside of section. Ignoring.");
253
254                 return 0;
255         }
256
257         e = strchr(l, '=');
258         if (!e) {
259                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
260                 return -EBADMSG;
261         }
262
263         *e = 0;
264         e++;
265
266         return next_assignment(unit,
267                                filename,
268                                line,
269                                lookup,
270                                table,
271                                *section,
272                                *section_line,
273                                strstrip(l),
274                                strstrip(e),
275                                relaxed,
276                                userdata);
277 }
278
279 /* Go through the file and parse each line */
280 int config_parse(const char *unit,
281                  const char *filename,
282                  FILE *f,
283                  const char *sections,
284                  ConfigItemLookup lookup,
285                  const void *table,
286                  bool relaxed,
287                  bool allow_include,
288                  bool warn,
289                  void *userdata) {
290
291         _cleanup_free_ char *section = NULL, *continuation = NULL;
292         _cleanup_fclose_ FILE *ours = NULL;
293         unsigned line = 0, section_line = 0;
294         bool section_ignored = false;
295         int r;
296
297         assert(filename);
298         assert(lookup);
299
300         if (!f) {
301                 f = ours = fopen(filename, "re");
302                 if (!f) {
303                         /* Only log on request, except for ENOENT,
304                          * since we return 0 to the caller. */
305                         if (warn || errno == ENOENT)
306                                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
307                                          "Failed to open configuration file '%s': %m", filename);
308                         return errno == ENOENT ? 0 : -errno;
309                 }
310         }
311
312         fd_warn_permissions(filename, fileno(f));
313
314         while (!feof(f)) {
315                 char l[LINE_MAX], *p, *c = NULL, *e;
316                 bool escaped = false;
317
318                 if (!fgets(l, sizeof(l), f)) {
319                         if (feof(f))
320                                 break;
321
322                         log_error_errno(errno, "Failed to read configuration file '%s': %m", filename);
323                         return -errno;
324                 }
325
326                 truncate_nl(l);
327
328                 if (continuation) {
329                         c = strappend(continuation, l);
330                         if (!c) {
331                                 if (warn)
332                                         log_oom();
333                                 return -ENOMEM;
334                         }
335
336                         free(continuation);
337                         continuation = NULL;
338                         p = c;
339                 } else
340                         p = l;
341
342                 for (e = p; *e; e++) {
343                         if (escaped)
344                                 escaped = false;
345                         else if (*e == '\\')
346                                 escaped = true;
347                 }
348
349                 if (escaped) {
350                         *(e-1) = ' ';
351
352                         if (c)
353                                 continuation = c;
354                         else {
355                                 continuation = strdup(l);
356                                 if (!continuation) {
357                                         if (warn)
358                                                 log_oom();
359                                         return -ENOMEM;
360                                 }
361                         }
362
363                         continue;
364                 }
365
366                 r = parse_line(unit,
367                                filename,
368                                ++line,
369                                sections,
370                                lookup,
371                                table,
372                                relaxed,
373                                allow_include,
374                                &section,
375                                &section_line,
376                                &section_ignored,
377                                p,
378                                userdata);
379                 free(c);
380
381                 if (r < 0) {
382                         if (warn)
383                                 log_warning_errno(r, "Failed to parse file '%s': %m",
384                                                   filename);
385                         return r;
386                 }
387         }
388
389         return 0;
390 }
391
392 /* Parse each config file in the specified directories. */
393 int config_parse_many(const char *conf_file,
394                       const char *conf_file_dirs,
395                       const char *sections,
396                       ConfigItemLookup lookup,
397                       const void *table,
398                       bool relaxed,
399                       void *userdata) {
400         _cleanup_strv_free_ char **files = NULL;
401         char **fn;
402         int r;
403
404         r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
405         if (r < 0)
406                 return r;
407
408         if (conf_file) {
409                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
410                 if (r < 0)
411                         return r;
412         }
413
414         STRV_FOREACH(fn, files) {
415                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
416                 if (r < 0)
417                         return r;
418         }
419
420         return 0;
421 }
422
423 #define DEFINE_PARSER(type, vartype, conv_func)                         \
424         int config_parse_##type(const char *unit,                       \
425                                 const char *filename,                   \
426                                 unsigned line,                          \
427                                 const char *section,                    \
428                                 unsigned section_line,                  \
429                                 const char *lvalue,                     \
430                                 int ltype,                              \
431                                 const char *rvalue,                     \
432                                 void *data,                             \
433                                 void *userdata) {                       \
434                                                                         \
435                 vartype *i = data;                                      \
436                 int r;                                                  \
437                                                                         \
438                 assert(filename);                                       \
439                 assert(lvalue);                                         \
440                 assert(rvalue);                                         \
441                 assert(data);                                           \
442                                                                         \
443                 r = conv_func(rvalue, i);                               \
444                 if (r < 0)                                              \
445                         log_syntax(unit, LOG_ERR, filename, line, -r,   \
446                                    "Failed to parse %s value, ignoring: %s", \
447                                    #vartype, rvalue);                   \
448                                                                         \
449                 return 0;                                               \
450         }
451
452 DEFINE_PARSER(int, int, safe_atoi)
453 DEFINE_PARSER(long, long, safe_atoli)
454 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
455 DEFINE_PARSER(unsigned, unsigned, safe_atou)
456 DEFINE_PARSER(double, double, safe_atod)
457 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
458 DEFINE_PARSER(sec, usec_t, parse_sec)
459
460 int config_parse_iec_size(const char* unit,
461                             const char *filename,
462                             unsigned line,
463                             const char *section,
464                             unsigned section_line,
465                             const char *lvalue,
466                             int ltype,
467                             const char *rvalue,
468                             void *data,
469                             void *userdata) {
470
471         size_t *sz = data;
472         off_t o;
473         int r;
474
475         assert(filename);
476         assert(lvalue);
477         assert(rvalue);
478         assert(data);
479
480         r = parse_size(rvalue, 1024, &o);
481         if (r < 0 || (off_t) (size_t) o != o) {
482                 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
483                 return 0;
484         }
485
486         *sz = (size_t) o;
487         return 0;
488 }
489
490 int config_parse_si_size(const char* unit,
491                             const char *filename,
492                             unsigned line,
493                             const char *section,
494                             unsigned section_line,
495                             const char *lvalue,
496                             int ltype,
497                             const char *rvalue,
498                             void *data,
499                             void *userdata) {
500
501         size_t *sz = data;
502         off_t o;
503         int r;
504
505         assert(filename);
506         assert(lvalue);
507         assert(rvalue);
508         assert(data);
509
510         r = parse_size(rvalue, 1000, &o);
511         if (r < 0 || (off_t) (size_t) o != o) {
512                 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
513                 return 0;
514         }
515
516         *sz = (size_t) o;
517         return 0;
518 }
519
520 int config_parse_iec_off(const char* unit,
521                            const char *filename,
522                            unsigned line,
523                            const char *section,
524                            unsigned section_line,
525                            const char *lvalue,
526                            int ltype,
527                            const char *rvalue,
528                            void *data,
529                            void *userdata) {
530
531         off_t *bytes = data;
532         int r;
533
534         assert(filename);
535         assert(lvalue);
536         assert(rvalue);
537         assert(data);
538
539         assert_cc(sizeof(off_t) == sizeof(uint64_t));
540
541         r = parse_size(rvalue, 1024, bytes);
542         if (r < 0)
543                 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
544
545         return 0;
546 }
547
548 int config_parse_bool(const char* unit,
549                       const char *filename,
550                       unsigned line,
551                       const char *section,
552                       unsigned section_line,
553                       const char *lvalue,
554                       int ltype,
555                       const char *rvalue,
556                       void *data,
557                       void *userdata) {
558
559         int k;
560         bool *b = data;
561
562         assert(filename);
563         assert(lvalue);
564         assert(rvalue);
565         assert(data);
566
567         k = parse_boolean(rvalue);
568         if (k < 0) {
569                 log_syntax(unit, LOG_ERR, filename, line, -k,
570                            "Failed to parse boolean value, ignoring: %s", rvalue);
571                 return 0;
572         }
573
574         *b = !!k;
575         return 0;
576 }
577
578 int config_parse_string(
579                 const char *unit,
580                 const char *filename,
581                 unsigned line,
582                 const char *section,
583                 unsigned section_line,
584                 const char *lvalue,
585                 int ltype,
586                 const char *rvalue,
587                 void *data,
588                 void *userdata) {
589
590         char **s = data, *n;
591
592         assert(filename);
593         assert(lvalue);
594         assert(rvalue);
595         assert(data);
596
597         if (!utf8_is_valid(rvalue)) {
598                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
599                 return 0;
600         }
601
602         if (isempty(rvalue))
603                 n = NULL;
604         else {
605                 n = strdup(rvalue);
606                 if (!n)
607                         return log_oom();
608         }
609
610         free(*s);
611         *s = n;
612
613         return 0;
614 }
615
616 int config_parse_path(
617                 const char *unit,
618                 const char *filename,
619                 unsigned line,
620                 const char *section,
621                 unsigned section_line,
622                 const char *lvalue,
623                 int ltype,
624                 const char *rvalue,
625                 void *data,
626                 void *userdata) {
627
628         char **s = data, *n;
629
630         assert(filename);
631         assert(lvalue);
632         assert(rvalue);
633         assert(data);
634
635         if (!utf8_is_valid(rvalue)) {
636                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
637                 return 0;
638         }
639
640         if (!path_is_absolute(rvalue)) {
641                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
642                 return 0;
643         }
644
645         n = strdup(rvalue);
646         if (!n)
647                 return log_oom();
648
649         path_kill_slashes(n);
650
651         free(*s);
652         *s = n;
653
654         return 0;
655 }
656
657 int config_parse_strv(const char *unit,
658                       const char *filename,
659                       unsigned line,
660                       const char *section,
661                       unsigned section_line,
662                       const char *lvalue,
663                       int ltype,
664                       const char *rvalue,
665                       void *data,
666                       void *userdata) {
667
668         char ***sv = data;
669         const char *word, *state;
670         size_t l;
671         int r;
672
673         assert(filename);
674         assert(lvalue);
675         assert(rvalue);
676         assert(data);
677
678         if (isempty(rvalue)) {
679                 char **empty;
680
681                 /* Empty assignment resets the list. As a special rule
682                  * we actually fill in a real empty array here rather
683                  * than NULL, since some code wants to know if
684                  * something was set at all... */
685                 empty = strv_new(NULL, NULL);
686                 if (!empty)
687                         return log_oom();
688
689                 strv_free(*sv);
690                 *sv = empty;
691                 return 0;
692         }
693
694         FOREACH_WORD_QUOTED(word, l, rvalue, state) {
695                 char *n;
696
697                 n = strndup(word, l);
698                 if (!n)
699                         return log_oom();
700
701                 if (!utf8_is_valid(n)) {
702                         log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
703                         free(n);
704                         continue;
705                 }
706
707                 r = strv_consume(sv, n);
708                 if (r < 0)
709                         return log_oom();
710         }
711         if (!isempty(state))
712                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
713                            "Trailing garbage, ignoring.");
714
715         return 0;
716 }
717
718 int config_parse_mode(
719                 const char *unit,
720                 const char *filename,
721                 unsigned line,
722                 const char *section,
723                       unsigned section_line,
724                 const char *lvalue,
725                 int ltype,
726                 const char *rvalue,
727                 void *data,
728                 void *userdata) {
729
730         mode_t *m = data;
731
732         assert(filename);
733         assert(lvalue);
734         assert(rvalue);
735         assert(data);
736
737         if (parse_mode(rvalue, m) < 0) {
738                 log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
739                 return 0;
740         }
741
742         return 0;
743 }
744
745 int config_parse_log_facility(
746                 const char *unit,
747                 const char *filename,
748                 unsigned line,
749                 const char *section,
750                 unsigned section_line,
751                 const char *lvalue,
752                 int ltype,
753                 const char *rvalue,
754                 void *data,
755                 void *userdata) {
756
757
758         int *o = data, x;
759
760         assert(filename);
761         assert(lvalue);
762         assert(rvalue);
763         assert(data);
764
765         x = log_facility_unshifted_from_string(rvalue);
766         if (x < 0) {
767                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log facility, ignoring: %s", rvalue);
768                 return 0;
769         }
770
771         *o = (x << 3) | LOG_PRI(*o);
772
773         return 0;
774 }
775
776 int config_parse_log_level(
777                 const char *unit,
778                 const char *filename,
779                 unsigned line,
780                 const char *section,
781                 unsigned section_line,
782                 const char *lvalue,
783                 int ltype,
784                 const char *rvalue,
785                 void *data,
786                 void *userdata) {
787
788
789         int *o = data, x;
790
791         assert(filename);
792         assert(lvalue);
793         assert(rvalue);
794         assert(data);
795
796         x = log_level_from_string(rvalue);
797         if (x < 0) {
798                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log level, ignoring: %s", rvalue);
799                 return 0;
800         }
801
802         *o = (*o & LOG_FACMASK) | x;
803         return 0;
804 }