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