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