chiark / gitweb /
core: rename Random* to RandomizedDelay*
[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 #if 0 /// UNNEEDED by elogind
463 DEFINE_PARSER(nsec, nsec_t, parse_nsec);
464 #endif // 0
465 DEFINE_PARSER(sec, usec_t, parse_sec);
466 DEFINE_PARSER(mode, mode_t, parse_mode);
467
468 int config_parse_iec_size(const char* unit,
469                             const char *filename,
470                             unsigned line,
471                             const char *section,
472                             unsigned section_line,
473                             const char *lvalue,
474                             int ltype,
475                             const char *rvalue,
476                             void *data,
477                             void *userdata) {
478
479         size_t *sz = data;
480         uint64_t v;
481         int r;
482
483         assert(filename);
484         assert(lvalue);
485         assert(rvalue);
486         assert(data);
487
488         r = parse_size(rvalue, 1024, &v);
489         if (r < 0 || (uint64_t) (size_t) v != v) {
490                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
491                 return 0;
492         }
493
494         *sz = (size_t) v;
495         return 0;
496 }
497
498 #if 0 /// UNNEEDED by elogind
499 int config_parse_si_size(const char* unit,
500                             const char *filename,
501                             unsigned line,
502                             const char *section,
503                             unsigned section_line,
504                             const char *lvalue,
505                             int ltype,
506                             const char *rvalue,
507                             void *data,
508                             void *userdata) {
509
510         size_t *sz = data;
511         uint64_t v;
512         int r;
513
514         assert(filename);
515         assert(lvalue);
516         assert(rvalue);
517         assert(data);
518
519         r = parse_size(rvalue, 1000, &v);
520         if (r < 0 || (uint64_t) (size_t) v != v) {
521                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
522                 return 0;
523         }
524
525         *sz = (size_t) v;
526         return 0;
527 }
528
529 int config_parse_iec_uint64(const char* unit,
530                            const char *filename,
531                            unsigned line,
532                            const char *section,
533                            unsigned section_line,
534                            const char *lvalue,
535                            int ltype,
536                            const char *rvalue,
537                            void *data,
538                            void *userdata) {
539
540         uint64_t *bytes = data;
541         int r;
542
543         assert(filename);
544         assert(lvalue);
545         assert(rvalue);
546         assert(data);
547
548         r = parse_size(rvalue, 1024, bytes);
549         if (r < 0)
550                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
551
552         return 0;
553 }
554 #endif // 0
555
556 int config_parse_bool(const char* unit,
557                       const char *filename,
558                       unsigned line,
559                       const char *section,
560                       unsigned section_line,
561                       const char *lvalue,
562                       int ltype,
563                       const char *rvalue,
564                       void *data,
565                       void *userdata) {
566
567         int k;
568         bool *b = data;
569
570         assert(filename);
571         assert(lvalue);
572         assert(rvalue);
573         assert(data);
574
575         k = parse_boolean(rvalue);
576         if (k < 0) {
577                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
578                 return 0;
579         }
580
581         *b = !!k;
582         return 0;
583 }
584
585 #if 0 /// UNNEEDED by elogind
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 #if 0 /// UNNEEDED by elogind
760 int config_parse_log_facility(
761                 const char *unit,
762                 const char *filename,
763                 unsigned line,
764                 const char *section,
765                 unsigned section_line,
766                 const char *lvalue,
767                 int ltype,
768                 const char *rvalue,
769                 void *data,
770                 void *userdata) {
771
772
773         int *o = data, x;
774
775         assert(filename);
776         assert(lvalue);
777         assert(rvalue);
778         assert(data);
779
780         x = log_facility_unshifted_from_string(rvalue);
781         if (x < 0) {
782                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
783                 return 0;
784         }
785
786         *o = (x << 3) | LOG_PRI(*o);
787
788         return 0;
789 }
790 #endif // 0
791
792 int config_parse_log_level(
793                 const char *unit,
794                 const char *filename,
795                 unsigned line,
796                 const char *section,
797                 unsigned section_line,
798                 const char *lvalue,
799                 int ltype,
800                 const char *rvalue,
801                 void *data,
802                 void *userdata) {
803
804
805         int *o = data, x;
806
807         assert(filename);
808         assert(lvalue);
809         assert(rvalue);
810         assert(data);
811
812         x = log_level_from_string(rvalue);
813         if (x < 0) {
814                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
815                 return 0;
816         }
817
818         *o = (*o & LOG_FACMASK) | x;
819         return 0;
820 }
821
822 int config_parse_signal(
823                 const char *unit,
824                 const char *filename,
825                 unsigned line,
826                 const char *section,
827                 unsigned section_line,
828                 const char *lvalue,
829                 int ltype,
830                 const char *rvalue,
831                 void *data,
832                 void *userdata) {
833
834         int *sig = data, r;
835
836         assert(filename);
837         assert(lvalue);
838         assert(rvalue);
839         assert(sig);
840
841         r = signal_from_string_try_harder(rvalue);
842         if (r <= 0) {
843                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
844                 return 0;
845         }
846
847         *sig = r;
848         return 0;
849 }
850
851 #if 0 /// UNNEEDED by elogind
852 int config_parse_personality(
853                 const char *unit,
854                 const char *filename,
855                 unsigned line,
856                 const char *section,
857                 unsigned section_line,
858                 const char *lvalue,
859                 int ltype,
860                 const char *rvalue,
861                 void *data,
862                 void *userdata) {
863
864         unsigned long *personality = data, p;
865
866         assert(filename);
867         assert(lvalue);
868         assert(rvalue);
869         assert(personality);
870
871         p = personality_from_string(rvalue);
872         if (p == PERSONALITY_INVALID) {
873                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
874                 return 0;
875         }
876
877         *personality = p;
878         return 0;
879 }
880 #endif // 0