chiark / gitweb /
Prep v226: Apply missing fixes and changes to src/shared
[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 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 int config_parse_tristate(
583                 const char* unit,
584                 const char *filename,
585                 unsigned line,
586                 const char *section,
587                 unsigned section_line,
588                 const char *lvalue,
589                 int ltype,
590                 const char *rvalue,
591                 void *data,
592                 void *userdata) {
593
594         int k, *t = data;
595
596         assert(filename);
597         assert(lvalue);
598         assert(rvalue);
599         assert(data);
600
601         /* A tristate is pretty much a boolean, except that it can
602          * also take the special value -1, indicating "uninitialized",
603          * much like NULL is for a pointer type. */
604
605         k = parse_boolean(rvalue);
606         if (k < 0) {
607                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
608                 return 0;
609         }
610
611         *t = !!k;
612         return 0;
613 }
614
615 int config_parse_string(
616                 const char *unit,
617                 const char *filename,
618                 unsigned line,
619                 const char *section,
620                 unsigned section_line,
621                 const char *lvalue,
622                 int ltype,
623                 const char *rvalue,
624                 void *data,
625                 void *userdata) {
626
627         char **s = data, *n;
628
629         assert(filename);
630         assert(lvalue);
631         assert(rvalue);
632         assert(data);
633
634         if (!utf8_is_valid(rvalue)) {
635                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
636                 return 0;
637         }
638
639         if (isempty(rvalue))
640                 n = NULL;
641         else {
642                 n = strdup(rvalue);
643                 if (!n)
644                         return log_oom();
645         }
646
647         free(*s);
648         *s = n;
649
650         return 0;
651 }
652
653 int config_parse_path(
654                 const char *unit,
655                 const char *filename,
656                 unsigned line,
657                 const char *section,
658                 unsigned section_line,
659                 const char *lvalue,
660                 int ltype,
661                 const char *rvalue,
662                 void *data,
663                 void *userdata) {
664
665         char **s = data, *n;
666
667         assert(filename);
668         assert(lvalue);
669         assert(rvalue);
670         assert(data);
671
672         if (!utf8_is_valid(rvalue)) {
673                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
674                 return 0;
675         }
676
677         if (!path_is_absolute(rvalue)) {
678                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
679                 return 0;
680         }
681
682         n = strdup(rvalue);
683         if (!n)
684                 return log_oom();
685
686         path_kill_slashes(n);
687
688         free(*s);
689         *s = n;
690
691         return 0;
692 }
693
694 int config_parse_strv(const char *unit,
695                       const char *filename,
696                       unsigned line,
697                       const char *section,
698                       unsigned section_line,
699                       const char *lvalue,
700                       int ltype,
701                       const char *rvalue,
702                       void *data,
703                       void *userdata) {
704
705         char ***sv = data;
706         const char *word, *state;
707         size_t l;
708         int r;
709
710         assert(filename);
711         assert(lvalue);
712         assert(rvalue);
713         assert(data);
714
715         if (isempty(rvalue)) {
716                 char **empty;
717
718                 /* Empty assignment resets the list. As a special rule
719                  * we actually fill in a real empty array here rather
720                  * than NULL, since some code wants to know if
721                  * something was set at all... */
722                 empty = strv_new(NULL, NULL);
723                 if (!empty)
724                         return log_oom();
725
726                 strv_free(*sv);
727                 *sv = empty;
728                 return 0;
729         }
730
731         FOREACH_WORD_QUOTED(word, l, rvalue, state) {
732                 char *n;
733
734                 n = strndup(word, l);
735                 if (!n)
736                         return log_oom();
737
738                 if (!utf8_is_valid(n)) {
739                         log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
740                         free(n);
741                         continue;
742                 }
743
744                 r = strv_consume(sv, n);
745                 if (r < 0)
746                         return log_oom();
747         }
748         if (!isempty(state))
749                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
750                            "Trailing garbage, ignoring.");
751
752         return 0;
753 }
754
755 int config_parse_mode(
756                 const char *unit,
757                 const char *filename,
758                 unsigned line,
759                 const char *section,
760                       unsigned section_line,
761                 const char *lvalue,
762                 int ltype,
763                 const char *rvalue,
764                 void *data,
765                 void *userdata) {
766
767         mode_t *m = data;
768
769         assert(filename);
770         assert(lvalue);
771         assert(rvalue);
772         assert(data);
773
774         if (parse_mode(rvalue, m) < 0) {
775                 log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
776                 return 0;
777         }
778
779         return 0;
780 }
781
782 int config_parse_log_facility(
783                 const char *unit,
784                 const char *filename,
785                 unsigned line,
786                 const char *section,
787                 unsigned section_line,
788                 const char *lvalue,
789                 int ltype,
790                 const char *rvalue,
791                 void *data,
792                 void *userdata) {
793
794
795         int *o = data, x;
796
797         assert(filename);
798         assert(lvalue);
799         assert(rvalue);
800         assert(data);
801
802         x = log_facility_unshifted_from_string(rvalue);
803         if (x < 0) {
804                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log facility, ignoring: %s", rvalue);
805                 return 0;
806         }
807
808         *o = (x << 3) | LOG_PRI(*o);
809
810         return 0;
811 }
812
813 int config_parse_log_level(
814                 const char *unit,
815                 const char *filename,
816                 unsigned line,
817                 const char *section,
818                 unsigned section_line,
819                 const char *lvalue,
820                 int ltype,
821                 const char *rvalue,
822                 void *data,
823                 void *userdata) {
824
825
826         int *o = data, x;
827
828         assert(filename);
829         assert(lvalue);
830         assert(rvalue);
831         assert(data);
832
833         x = log_level_from_string(rvalue);
834         if (x < 0) {
835                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log level, ignoring: %s", rvalue);
836                 return 0;
837         }
838
839         *o = (*o & LOG_FACMASK) | x;
840         return 0;
841 }
842
843 int config_parse_signal(
844                 const char *unit,
845                 const char *filename,
846                 unsigned line,
847                 const char *section,
848                 unsigned section_line,
849                 const char *lvalue,
850                 int ltype,
851                 const char *rvalue,
852                 void *data,
853                 void *userdata) {
854
855         int *sig = data, r;
856
857         assert(filename);
858         assert(lvalue);
859         assert(rvalue);
860         assert(sig);
861
862         r = signal_from_string_try_harder(rvalue);
863         if (r <= 0) {
864                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse signal name, ignoring: %s", rvalue);
865                 return 0;
866         }
867
868         *sig = r;
869         return 0;
870 }
871
872 int config_parse_personality(
873                 const char *unit,
874                 const char *filename,
875                 unsigned line,
876                 const char *section,
877                 unsigned section_line,
878                 const char *lvalue,
879                 int ltype,
880                 const char *rvalue,
881                 void *data,
882                 void *userdata) {
883
884         unsigned long *personality = data, p;
885
886         assert(filename);
887         assert(lvalue);
888         assert(rvalue);
889         assert(personality);
890
891         p = personality_from_string(rvalue);
892         if (p == PERSONALITY_INVALID) {
893                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse personality, ignoring: %s", rvalue);
894                 return 0;
895         }
896
897         *personality = p;
898         return 0;
899 }