chiark / gitweb /
Prep v225: Remove some orphaned files
[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 "conf-parser.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 "sd-messages.h"
36
37 int config_item_table_lookup(
38                 const void *table,
39                 const char *section,
40                 const char *lvalue,
41                 ConfigParserCallback *func,
42                 int *ltype,
43                 void **data,
44                 void *userdata) {
45
46         const ConfigTableItem *t;
47
48         assert(table);
49         assert(lvalue);
50         assert(func);
51         assert(ltype);
52         assert(data);
53
54         for (t = table; t->lvalue; t++) {
55
56                 if (!streq(lvalue, t->lvalue))
57                         continue;
58
59                 if (!streq_ptr(section, t->section))
60                         continue;
61
62                 *func = t->parse;
63                 *ltype = t->ltype;
64                 *data = t->data;
65                 return 1;
66         }
67
68         return 0;
69 }
70
71 int config_item_perf_lookup(
72                 const void *table,
73                 const char *section,
74                 const char *lvalue,
75                 ConfigParserCallback *func,
76                 int *ltype,
77                 void **data,
78                 void *userdata) {
79
80         ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table;
81         const ConfigPerfItem *p;
82
83         assert(table);
84         assert(lvalue);
85         assert(func);
86         assert(ltype);
87         assert(data);
88
89         if (!section)
90                 p = lookup(lvalue, strlen(lvalue));
91         else {
92                 char *key;
93
94                 key = strjoin(section, ".", lvalue, NULL);
95                 if (!key)
96                         return -ENOMEM;
97
98                 p = lookup(key, strlen(key));
99                 free(key);
100         }
101
102         if (!p)
103                 return 0;
104
105         *func = p->parse;
106         *ltype = p->ltype;
107         *data = (uint8_t*) userdata + p->offset;
108         return 1;
109 }
110
111 /* Run the user supplied parser for an assignment */
112 static int next_assignment(const char *unit,
113                            const char *filename,
114                            unsigned line,
115                            ConfigItemLookup lookup,
116                            const void *table,
117                            const char *section,
118                            unsigned section_line,
119                            const char *lvalue,
120                            const char *rvalue,
121                            bool relaxed,
122                            void *userdata) {
123
124         ConfigParserCallback func = NULL;
125         int ltype = 0;
126         void *data = NULL;
127         int r;
128
129         assert(filename);
130         assert(line > 0);
131         assert(lookup);
132         assert(lvalue);
133         assert(rvalue);
134
135         r = lookup(table, section, lvalue, &func, &ltype, &data, userdata);
136         if (r < 0)
137                 return r;
138
139         if (r > 0) {
140                 if (func)
141                         return func(unit, filename, line, section, section_line,
142                                     lvalue, ltype, rvalue, data, userdata);
143
144                 return 0;
145         }
146
147         /* Warn about unknown non-extension fields. */
148         if (!relaxed && !startswith(lvalue, "X-"))
149                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
150                            "Unknown lvalue '%s' in section '%s'", lvalue, section);
151
152         return 0;
153 }
154
155 /* Parse a variable assignment line */
156 static int parse_line(const char* unit,
157                       const char *filename,
158                       unsigned line,
159                       const char *sections,
160                       ConfigItemLookup lookup,
161                       const void *table,
162                       bool relaxed,
163                       bool allow_include,
164                       char **section,
165                       unsigned *section_line,
166                       bool *section_ignored,
167                       char *l,
168                       void *userdata) {
169
170         char *e;
171
172         assert(filename);
173         assert(line > 0);
174         assert(lookup);
175         assert(l);
176
177         l = strstrip(l);
178
179         if (!*l)
180                 return 0;
181
182         if (strchr(COMMENTS "\n", *l))
183                 return 0;
184
185         if (startswith(l, ".include ")) {
186                 _cleanup_free_ char *fn = NULL;
187
188                 /* .includes are a bad idea, we only support them here
189                  * for historical reasons. They create cyclic include
190                  * problems and make it difficult to detect
191                  * configuration file changes with an easy
192                  * stat(). Better approaches, such as .d/ drop-in
193                  * snippets exist.
194                  *
195                  * Support for them should be eventually removed. */
196
197                 if (!allow_include) {
198                         log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
199                                    ".include not allowed here. Ignoring.");
200                         return 0;
201                 }
202
203                 fn = file_in_same_dir(filename, strstrip(l+9));
204                 if (!fn)
205                         return -ENOMEM;
206
207                 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata);
208         }
209
210         if (*l == '[') {
211                 size_t k;
212                 char *n;
213
214                 k = strlen(l);
215                 assert(k > 0);
216
217                 if (l[k-1] != ']') {
218                         log_syntax(unit, LOG_ERR, filename, line, EBADMSG,
219                                    "Invalid section header '%s'", l);
220                         return -EBADMSG;
221                 }
222
223                 n = strndup(l+1, k-2);
224                 if (!n)
225                         return -ENOMEM;
226
227                 if (sections && !nulstr_contains(sections, n)) {
228
229                         if (!relaxed && !startswith(n, "X-"))
230                                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
231                                            "Unknown section '%s'. Ignoring.", n);
232
233                         free(n);
234                         free(*section);
235                         *section = NULL;
236                         *section_line = 0;
237                         *section_ignored = true;
238                 } else {
239                         free(*section);
240                         *section = n;
241                         *section_line = line;
242                         *section_ignored = false;
243                 }
244
245                 return 0;
246         }
247
248         if (sections && !*section) {
249
250                 if (!relaxed && !*section_ignored)
251                         log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
252                                    "Assignment outside of section. Ignoring.");
253
254                 return 0;
255         }
256
257         e = strchr(l, '=');
258         if (!e) {
259                 log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Missing '='.");
260                 return -EBADMSG;
261         }
262
263         *e = 0;
264         e++;
265
266         return next_assignment(unit,
267                                filename,
268                                line,
269                                lookup,
270                                table,
271                                *section,
272                                *section_line,
273                                strstrip(l),
274                                strstrip(e),
275                                relaxed,
276                                userdata);
277 }
278
279 /* Go through the file and parse each line */
280 int config_parse(const char *unit,
281                  const char *filename,
282                  FILE *f,
283                  const char *sections,
284                  ConfigItemLookup lookup,
285                  const void *table,
286                  bool relaxed,
287                  bool allow_include,
288                  bool warn,
289                  void *userdata) {
290
291         _cleanup_free_ char *section = NULL, *continuation = NULL;
292         _cleanup_fclose_ FILE *ours = NULL;
293         unsigned line = 0, section_line = 0;
294         bool section_ignored = false;
295         int r;
296
297         assert(filename);
298         assert(lookup);
299
300         if (!f) {
301                 f = ours = fopen(filename, "re");
302                 if (!f) {
303                         /* Only log on request, except for ENOENT,
304                          * since we return 0 to the caller. */
305                         if (warn || errno == ENOENT)
306                                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
307                                          "Failed to open configuration file '%s': %m", filename);
308                         return errno == ENOENT ? 0 : -errno;
309                 }
310         }
311
312         fd_warn_permissions(filename, fileno(f));
313
314         while (!feof(f)) {
315                 char l[LINE_MAX], *p, *c = NULL, *e;
316                 bool escaped = false;
317
318                 if (!fgets(l, sizeof(l), f)) {
319                         if (feof(f))
320                                 break;
321
322                         log_error_errno(errno, "Failed to read configuration file '%s': %m", filename);
323                         return -errno;
324                 }
325
326                 truncate_nl(l);
327
328                 if (continuation) {
329                         c = strappend(continuation, l);
330                         if (!c) {
331                                 if (warn)
332                                         log_oom();
333                                 return -ENOMEM;
334                         }
335
336                         free(continuation);
337                         continuation = NULL;
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(uint64, uint64_t, safe_atou64)
455 DEFINE_PARSER(unsigned, unsigned, safe_atou)
456 DEFINE_PARSER(double, double, safe_atod)
457 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
458 DEFINE_PARSER(sec, usec_t, parse_sec)
459
460 int config_parse_iec_size(const char* unit,
461                             const char *filename,
462                             unsigned line,
463                             const char *section,
464                             unsigned section_line,
465                             const char *lvalue,
466                             int ltype,
467                             const char *rvalue,
468                             void *data,
469                             void *userdata) {
470
471         size_t *sz = data;
472         off_t o;
473         int r;
474
475         assert(filename);
476         assert(lvalue);
477         assert(rvalue);
478         assert(data);
479
480         r = parse_size(rvalue, 1024, &o);
481         if (r < 0 || (off_t) (size_t) o != o) {
482                 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
483                 return 0;
484         }
485
486         *sz = (size_t) o;
487         return 0;
488 }
489
490 /// UNNEEDED by elogind
491 #if 0
492 int config_parse_si_size(const char* unit,
493                             const char *filename,
494                             unsigned line,
495                             const char *section,
496                             unsigned section_line,
497                             const char *lvalue,
498                             int ltype,
499                             const char *rvalue,
500                             void *data,
501                             void *userdata) {
502
503         size_t *sz = data;
504         off_t o;
505         int r;
506
507         assert(filename);
508         assert(lvalue);
509         assert(rvalue);
510         assert(data);
511
512         r = parse_size(rvalue, 1000, &o);
513         if (r < 0 || (off_t) (size_t) o != o) {
514                 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
515                 return 0;
516         }
517
518         *sz = (size_t) o;
519         return 0;
520 }
521
522 int config_parse_iec_off(const char* unit,
523                            const char *filename,
524                            unsigned line,
525                            const char *section,
526                            unsigned section_line,
527                            const char *lvalue,
528                            int ltype,
529                            const char *rvalue,
530                            void *data,
531                            void *userdata) {
532
533         off_t *bytes = data;
534         int r;
535
536         assert(filename);
537         assert(lvalue);
538         assert(rvalue);
539         assert(data);
540
541         assert_cc(sizeof(off_t) == sizeof(uint64_t));
542
543         r = parse_size(rvalue, 1024, bytes);
544         if (r < 0)
545                 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
546
547         return 0;
548 }
549 #endif // 0
550
551 int config_parse_bool(const char* unit,
552                       const char *filename,
553                       unsigned line,
554                       const char *section,
555                       unsigned section_line,
556                       const char *lvalue,
557                       int ltype,
558                       const char *rvalue,
559                       void *data,
560                       void *userdata) {
561
562         int k;
563         bool *b = data;
564
565         assert(filename);
566         assert(lvalue);
567         assert(rvalue);
568         assert(data);
569
570         k = parse_boolean(rvalue);
571         if (k < 0) {
572                 log_syntax(unit, LOG_ERR, filename, line, -k,
573                            "Failed to parse boolean value, ignoring: %s", rvalue);
574                 return 0;
575         }
576
577         *b = !!k;
578         return 0;
579 }
580
581 int config_parse_string(
582                 const char *unit,
583                 const char *filename,
584                 unsigned line,
585                 const char *section,
586                 unsigned section_line,
587                 const char *lvalue,
588                 int ltype,
589                 const char *rvalue,
590                 void *data,
591                 void *userdata) {
592
593         char **s = data, *n;
594
595         assert(filename);
596         assert(lvalue);
597         assert(rvalue);
598         assert(data);
599
600         if (!utf8_is_valid(rvalue)) {
601                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
602                 return 0;
603         }
604
605         if (isempty(rvalue))
606                 n = NULL;
607         else {
608                 n = strdup(rvalue);
609                 if (!n)
610                         return log_oom();
611         }
612
613         free(*s);
614         *s = n;
615
616         return 0;
617 }
618
619 int config_parse_path(
620                 const char *unit,
621                 const char *filename,
622                 unsigned line,
623                 const char *section,
624                 unsigned section_line,
625                 const char *lvalue,
626                 int ltype,
627                 const char *rvalue,
628                 void *data,
629                 void *userdata) {
630
631         char **s = data, *n;
632
633         assert(filename);
634         assert(lvalue);
635         assert(rvalue);
636         assert(data);
637
638         if (!utf8_is_valid(rvalue)) {
639                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
640                 return 0;
641         }
642
643         if (!path_is_absolute(rvalue)) {
644                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
645                 return 0;
646         }
647
648         n = strdup(rvalue);
649         if (!n)
650                 return log_oom();
651
652         path_kill_slashes(n);
653
654         free(*s);
655         *s = n;
656
657         return 0;
658 }
659
660 int config_parse_strv(const char *unit,
661                       const char *filename,
662                       unsigned line,
663                       const char *section,
664                       unsigned section_line,
665                       const char *lvalue,
666                       int ltype,
667                       const char *rvalue,
668                       void *data,
669                       void *userdata) {
670
671         char ***sv = data;
672         const char *word, *state;
673         size_t l;
674         int r;
675
676         assert(filename);
677         assert(lvalue);
678         assert(rvalue);
679         assert(data);
680
681         if (isempty(rvalue)) {
682                 char **empty;
683
684                 /* Empty assignment resets the list. As a special rule
685                  * we actually fill in a real empty array here rather
686                  * than NULL, since some code wants to know if
687                  * something was set at all... */
688                 empty = strv_new(NULL, NULL);
689                 if (!empty)
690                         return log_oom();
691
692                 strv_free(*sv);
693                 *sv = empty;
694                 return 0;
695         }
696
697         FOREACH_WORD_QUOTED(word, l, rvalue, state) {
698                 char *n;
699
700                 n = strndup(word, l);
701                 if (!n)
702                         return log_oom();
703
704                 if (!utf8_is_valid(n)) {
705                         log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
706                         free(n);
707                         continue;
708                 }
709
710                 r = strv_consume(sv, n);
711                 if (r < 0)
712                         return log_oom();
713         }
714         if (!isempty(state))
715                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
716                            "Trailing garbage, ignoring.");
717
718         return 0;
719 }
720
721 int config_parse_mode(
722                 const char *unit,
723                 const char *filename,
724                 unsigned line,
725                 const char *section,
726                       unsigned section_line,
727                 const char *lvalue,
728                 int ltype,
729                 const char *rvalue,
730                 void *data,
731                 void *userdata) {
732
733         mode_t *m = data;
734
735         assert(filename);
736         assert(lvalue);
737         assert(rvalue);
738         assert(data);
739
740         if (parse_mode(rvalue, m) < 0) {
741                 log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse mode value, ignoring: %s", rvalue);
742                 return 0;
743         }
744
745         return 0;
746 }
747
748 int config_parse_log_facility(
749                 const char *unit,
750                 const char *filename,
751                 unsigned line,
752                 const char *section,
753                 unsigned section_line,
754                 const char *lvalue,
755                 int ltype,
756                 const char *rvalue,
757                 void *data,
758                 void *userdata) {
759
760
761         int *o = data, x;
762
763         assert(filename);
764         assert(lvalue);
765         assert(rvalue);
766         assert(data);
767
768         x = log_facility_unshifted_from_string(rvalue);
769         if (x < 0) {
770                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log facility, ignoring: %s", rvalue);
771                 return 0;
772         }
773
774         *o = (x << 3) | LOG_PRI(*o);
775
776         return 0;
777 }
778
779 int config_parse_log_level(
780                 const char *unit,
781                 const char *filename,
782                 unsigned line,
783                 const char *section,
784                 unsigned section_line,
785                 const char *lvalue,
786                 int ltype,
787                 const char *rvalue,
788                 void *data,
789                 void *userdata) {
790
791
792         int *o = data, x;
793
794         assert(filename);
795         assert(lvalue);
796         assert(rvalue);
797         assert(data);
798
799         x = log_level_from_string(rvalue);
800         if (x < 0) {
801                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse log level, ignoring: %s", rvalue);
802                 return 0;
803         }
804
805         *o = (*o & LOG_FACMASK) | x;
806         return 0;
807 }