chiark / gitweb /
Ignore BOM in config files
[elogind.git] / src / shared / conf-parser.c
1 /***
2   This file is part of systemd.
3
4   Copyright 2010 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as published by
8   the Free Software Foundation; either version 2.1 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <errno.h>
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27
28 #include "alloc-util.h"
29 #include "conf-files.h"
30 #include "conf-parser.h"
31 #include "extract-word.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 "time-util.h"
44 #include "utf8.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, allow_bom = true;
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         for (;;) {
318                 char buf[LINE_MAX], *l, *p, *c = NULL, *e;
319                 bool escaped = false;
320
321                 if (!fgets(buf, sizeof buf, 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                 l = buf;
330                 if (allow_bom && startswith(l, UTF8_BYTE_ORDER_MARK))
331                         l += strlen(UTF8_BYTE_ORDER_MARK);
332                 allow_bom = false;
333
334                 truncate_nl(l);
335
336                 if (continuation) {
337                         c = strappend(continuation, l);
338                         if (!c) {
339                                 if (warn)
340                                         log_oom();
341                                 return -ENOMEM;
342                         }
343
344                         continuation = mfree(continuation);
345                         p = c;
346                 } else
347                         p = l;
348
349                 for (e = p; *e; e++) {
350                         if (escaped)
351                                 escaped = false;
352                         else if (*e == '\\')
353                                 escaped = true;
354                 }
355
356                 if (escaped) {
357                         *(e-1) = ' ';
358
359                         if (c)
360                                 continuation = c;
361                         else {
362                                 continuation = strdup(l);
363                                 if (!continuation) {
364                                         if (warn)
365                                                 log_oom();
366                                         return -ENOMEM;
367                                 }
368                         }
369
370                         continue;
371                 }
372
373                 r = parse_line(unit,
374                                filename,
375                                ++line,
376                                sections,
377                                lookup,
378                                table,
379                                relaxed,
380                                allow_include,
381                                &section,
382                                &section_line,
383                                &section_ignored,
384                                p,
385                                userdata);
386                 free(c);
387
388                 if (r < 0) {
389                         if (warn)
390                                 log_warning_errno(r, "Failed to parse file '%s': %m",
391                                                   filename);
392                         return r;
393                 }
394         }
395
396         return 0;
397 }
398
399 /* Parse each config file in the specified directories. */
400 int config_parse_many(const char *conf_file,
401                       const char *conf_file_dirs,
402                       const char *sections,
403                       ConfigItemLookup lookup,
404                       const void *table,
405                       bool relaxed,
406                       void *userdata) {
407         _cleanup_strv_free_ char **files = NULL;
408         char **fn;
409         int r;
410
411         r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
412         if (r < 0)
413                 return r;
414
415         if (conf_file) {
416                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
417                 if (r < 0)
418                         return r;
419         }
420
421         STRV_FOREACH(fn, files) {
422                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
423                 if (r < 0)
424                         return r;
425         }
426
427         return 0;
428 }
429
430 #define DEFINE_PARSER(type, vartype, conv_func)                         \
431         int config_parse_##type(                                        \
432                         const char *unit,                               \
433                                 const char *filename,                   \
434                                 unsigned line,                          \
435                                 const char *section,                    \
436                                 unsigned section_line,                  \
437                                 const char *lvalue,                     \
438                                 int ltype,                              \
439                                 const char *rvalue,                     \
440                                 void *data,                             \
441                                 void *userdata) {                       \
442                                                                         \
443                 vartype *i = data;                                      \
444                 int r;                                                  \
445                                                                         \
446                 assert(filename);                                       \
447                 assert(lvalue);                                         \
448                 assert(rvalue);                                         \
449                 assert(data);                                           \
450                                                                         \
451                 r = conv_func(rvalue, i);                               \
452                 if (r < 0)                                              \
453                         log_syntax(unit, LOG_ERR, filename, line, r,    \
454                                    "Failed to parse %s value, ignoring: %s", \
455                                    #type, rvalue);                      \
456                                                                         \
457                 return 0;                                               \
458         }                                                               \
459         struct __useless_struct_to_allow_trailing_semicolon__
460
461 DEFINE_PARSER(int, int, safe_atoi);
462 DEFINE_PARSER(long, long, safe_atoli);
463 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
464 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
465 DEFINE_PARSER(unsigned, unsigned, safe_atou);
466 DEFINE_PARSER(double, double, safe_atod);
467 #if 0 /// UNNEEDED by elogind
468 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
469 #endif // 0
470 DEFINE_PARSER(sec, usec_t, parse_sec);
471 DEFINE_PARSER(mode, mode_t, parse_mode);
472
473 int config_parse_iec_size(const char* unit,
474                             const char *filename,
475                             unsigned line,
476                             const char *section,
477                             unsigned section_line,
478                             const char *lvalue,
479                             int ltype,
480                             const char *rvalue,
481                             void *data,
482                             void *userdata) {
483
484         size_t *sz = data;
485         uint64_t v;
486         int r;
487
488         assert(filename);
489         assert(lvalue);
490         assert(rvalue);
491         assert(data);
492
493         r = parse_size(rvalue, 1024, &v);
494         if (r < 0 || (uint64_t) (size_t) v != v) {
495                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
496                 return 0;
497         }
498
499         *sz = (size_t) v;
500         return 0;
501 }
502
503 #if 0 /// UNNEEDED by elogind
504 int config_parse_si_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         uint64_t v;
517         int r;
518
519         assert(filename);
520         assert(lvalue);
521         assert(rvalue);
522         assert(data);
523
524         r = parse_size(rvalue, 1000, &v);
525         if (r < 0 || (uint64_t) (size_t) v != v) {
526                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
527                 return 0;
528         }
529
530         *sz = (size_t) v;
531         return 0;
532 }
533
534 int config_parse_iec_uint64(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         uint64_t *bytes = data;
546         int r;
547
548         assert(filename);
549         assert(lvalue);
550         assert(rvalue);
551         assert(data);
552
553         r = parse_size(rvalue, 1024, bytes);
554         if (r < 0)
555                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
556
557         return 0;
558 }
559 #endif // 0
560
561 int config_parse_bool(const char* unit,
562                       const char *filename,
563                       unsigned line,
564                       const char *section,
565                       unsigned section_line,
566                       const char *lvalue,
567                       int ltype,
568                       const char *rvalue,
569                       void *data,
570                       void *userdata) {
571
572         int k;
573         bool *b = data;
574
575         assert(filename);
576         assert(lvalue);
577         assert(rvalue);
578         assert(data);
579
580         k = parse_boolean(rvalue);
581         if (k < 0) {
582                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
583                 return 0;
584         }
585
586         *b = !!k;
587         return 0;
588 }
589
590 #if 0 /// UNNEEDED by elogind
591 int config_parse_tristate(
592                 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, *t = data;
604
605         assert(filename);
606         assert(lvalue);
607         assert(rvalue);
608         assert(data);
609
610         /* A tristate is pretty much a boolean, except that it can
611          * also take the special value -1, indicating "uninitialized",
612          * much like NULL is for a pointer type. */
613
614         k = parse_boolean(rvalue);
615         if (k < 0) {
616                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
617                 return 0;
618         }
619
620         *t = !!k;
621         return 0;
622 }
623 #endif // 0
624
625 int config_parse_string(
626                 const char *unit,
627                 const char *filename,
628                 unsigned line,
629                 const char *section,
630                 unsigned section_line,
631                 const char *lvalue,
632                 int ltype,
633                 const char *rvalue,
634                 void *data,
635                 void *userdata) {
636
637         char **s = data, *n;
638
639         assert(filename);
640         assert(lvalue);
641         assert(rvalue);
642         assert(data);
643
644         if (!utf8_is_valid(rvalue)) {
645                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
646                 return 0;
647         }
648
649         if (isempty(rvalue))
650                 n = NULL;
651         else {
652                 n = strdup(rvalue);
653                 if (!n)
654                         return log_oom();
655         }
656
657         free(*s);
658         *s = n;
659
660         return 0;
661 }
662
663 int config_parse_path(
664                 const char *unit,
665                 const char *filename,
666                 unsigned line,
667                 const char *section,
668                 unsigned section_line,
669                 const char *lvalue,
670                 int ltype,
671                 const char *rvalue,
672                 void *data,
673                 void *userdata) {
674
675         char **s = data, *n;
676
677         assert(filename);
678         assert(lvalue);
679         assert(rvalue);
680         assert(data);
681
682         if (!utf8_is_valid(rvalue)) {
683                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
684                 return 0;
685         }
686
687         if (!path_is_absolute(rvalue)) {
688                 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", rvalue);
689                 return 0;
690         }
691
692         n = strdup(rvalue);
693         if (!n)
694                 return log_oom();
695
696         path_kill_slashes(n);
697
698         free(*s);
699         *s = n;
700
701         return 0;
702 }
703
704 int config_parse_strv(const char *unit,
705                       const char *filename,
706                       unsigned line,
707                       const char *section,
708                       unsigned section_line,
709                       const char *lvalue,
710                       int ltype,
711                       const char *rvalue,
712                       void *data,
713                       void *userdata) {
714
715         char ***sv = data;
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         for (;;) {
739                 char *word = NULL;
740                 int r;
741                 r = extract_first_word(&rvalue, &word, WHITESPACE, EXTRACT_QUOTES);
742                 if (r == 0)
743                         break;
744                 if (r == -ENOMEM)
745                         return log_oom();
746                 if (r < 0) {
747                         log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
748                         break;
749                 }
750
751                 if (!utf8_is_valid(word)) {
752                         log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
753                         free(word);
754                         continue;
755                 }
756                 r = strv_consume(sv, word);
757                 if (r < 0)
758                         return log_oom();
759         }
760
761         return 0;
762 }
763
764 #if 0 /// UNNEEDED by elogind
765 int config_parse_log_facility(
766                 const char *unit,
767                 const char *filename,
768                 unsigned line,
769                 const char *section,
770                 unsigned section_line,
771                 const char *lvalue,
772                 int ltype,
773                 const char *rvalue,
774                 void *data,
775                 void *userdata) {
776
777
778         int *o = data, x;
779
780         assert(filename);
781         assert(lvalue);
782         assert(rvalue);
783         assert(data);
784
785         x = log_facility_unshifted_from_string(rvalue);
786         if (x < 0) {
787                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
788                 return 0;
789         }
790
791         *o = (x << 3) | LOG_PRI(*o);
792
793         return 0;
794 }
795 #endif // 0
796
797 int config_parse_log_level(
798                 const char *unit,
799                 const char *filename,
800                 unsigned line,
801                 const char *section,
802                 unsigned section_line,
803                 const char *lvalue,
804                 int ltype,
805                 const char *rvalue,
806                 void *data,
807                 void *userdata) {
808
809
810         int *o = data, x;
811
812         assert(filename);
813         assert(lvalue);
814         assert(rvalue);
815         assert(data);
816
817         x = log_level_from_string(rvalue);
818         if (x < 0) {
819                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
820                 return 0;
821         }
822
823         *o = (*o & LOG_FACMASK) | x;
824         return 0;
825 }
826
827 int config_parse_signal(
828                 const char *unit,
829                 const char *filename,
830                 unsigned line,
831                 const char *section,
832                 unsigned section_line,
833                 const char *lvalue,
834                 int ltype,
835                 const char *rvalue,
836                 void *data,
837                 void *userdata) {
838
839         int *sig = data, r;
840
841         assert(filename);
842         assert(lvalue);
843         assert(rvalue);
844         assert(sig);
845
846         r = signal_from_string_try_harder(rvalue);
847         if (r <= 0) {
848                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
849                 return 0;
850         }
851
852         *sig = r;
853         return 0;
854 }
855
856 #if 0 /// UNNEEDED by elogind
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