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