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