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