chiark / gitweb /
Merge pull request #5 from elogind/dev_v227
[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, 0, "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, 0, ".include not allowed here. Ignoring.");
199                         return 0;
200                 }
201
202                 fn = file_in_same_dir(filename, strstrip(l+9));
203                 if (!fn)
204                         return -ENOMEM;
205
206                 return config_parse(unit, fn, NULL, sections, lookup, table, relaxed, false, false, userdata);
207         }
208
209         if (*l == '[') {
210                 size_t k;
211                 char *n;
212
213                 k = strlen(l);
214                 assert(k > 0);
215
216                 if (l[k-1] != ']') {
217                         log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid section header '%s'", l);
218                         return -EBADMSG;
219                 }
220
221                 n = strndup(l+1, k-2);
222                 if (!n)
223                         return -ENOMEM;
224
225                 if (sections && !nulstr_contains(sections, n)) {
226
227                         if (!relaxed && !startswith(n, "X-"))
228                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n);
229
230                         free(n);
231                         *section = mfree(*section);
232                         *section_line = 0;
233                         *section_ignored = true;
234                 } else {
235                         free(*section);
236                         *section = n;
237                         *section_line = line;
238                         *section_ignored = false;
239                 }
240
241                 return 0;
242         }
243
244         if (sections && !*section) {
245
246                 if (!relaxed && !*section_ignored)
247                         log_syntax(unit, LOG_WARNING, filename, line, 0, "Assignment outside of section. Ignoring.");
248
249                 return 0;
250         }
251
252         e = strchr(l, '=');
253         if (!e) {
254                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Missing '='.");
255                 return -EINVAL;
256         }
257
258         *e = 0;
259         e++;
260
261         return next_assignment(unit,
262                                filename,
263                                line,
264                                lookup,
265                                table,
266                                *section,
267                                *section_line,
268                                strstrip(l),
269                                strstrip(e),
270                                relaxed,
271                                userdata);
272 }
273
274 /* Go through the file and parse each line */
275 int config_parse(const char *unit,
276                  const char *filename,
277                  FILE *f,
278                  const char *sections,
279                  ConfigItemLookup lookup,
280                  const void *table,
281                  bool relaxed,
282                  bool allow_include,
283                  bool warn,
284                  void *userdata) {
285
286         _cleanup_free_ char *section = NULL, *continuation = NULL;
287         _cleanup_fclose_ FILE *ours = NULL;
288         unsigned line = 0, section_line = 0;
289         bool section_ignored = false;
290         int r;
291
292         assert(filename);
293         assert(lookup);
294
295         if (!f) {
296                 f = ours = fopen(filename, "re");
297                 if (!f) {
298                         /* Only log on request, except for ENOENT,
299                          * since we return 0 to the caller. */
300                         if (warn || errno == ENOENT)
301                                 log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR,
302                                          "Failed to open configuration file '%s': %m", filename);
303                         return errno == ENOENT ? 0 : -errno;
304                 }
305         }
306
307         fd_warn_permissions(filename, fileno(f));
308
309         while (!feof(f)) {
310                 char l[LINE_MAX], *p, *c = NULL, *e;
311                 bool escaped = false;
312
313                 if (!fgets(l, sizeof(l), f)) {
314                         if (feof(f))
315                                 break;
316
317                         log_error_errno(errno, "Failed to read configuration file '%s': %m", filename);
318                         return -errno;
319                 }
320
321                 truncate_nl(l);
322
323                 if (continuation) {
324                         c = strappend(continuation, l);
325                         if (!c) {
326                                 if (warn)
327                                         log_oom();
328                                 return -ENOMEM;
329                         }
330
331                         continuation = mfree(continuation);
332                         p = c;
333                 } else
334                         p = l;
335
336                 for (e = p; *e; e++) {
337                         if (escaped)
338                                 escaped = false;
339                         else if (*e == '\\')
340                                 escaped = true;
341                 }
342
343                 if (escaped) {
344                         *(e-1) = ' ';
345
346                         if (c)
347                                 continuation = c;
348                         else {
349                                 continuation = strdup(l);
350                                 if (!continuation) {
351                                         if (warn)
352                                                 log_oom();
353                                         return -ENOMEM;
354                                 }
355                         }
356
357                         continue;
358                 }
359
360                 r = parse_line(unit,
361                                filename,
362                                ++line,
363                                sections,
364                                lookup,
365                                table,
366                                relaxed,
367                                allow_include,
368                                &section,
369                                &section_line,
370                                &section_ignored,
371                                p,
372                                userdata);
373                 free(c);
374
375                 if (r < 0) {
376                         if (warn)
377                                 log_warning_errno(r, "Failed to parse file '%s': %m",
378                                                   filename);
379                         return r;
380                 }
381         }
382
383         return 0;
384 }
385
386 /* Parse each config file in the specified directories. */
387 int config_parse_many(const char *conf_file,
388                       const char *conf_file_dirs,
389                       const char *sections,
390                       ConfigItemLookup lookup,
391                       const void *table,
392                       bool relaxed,
393                       void *userdata) {
394         _cleanup_strv_free_ char **files = NULL;
395         char **fn;
396         int r;
397
398         r = conf_files_list_nulstr(&files, ".conf", NULL, conf_file_dirs);
399         if (r < 0)
400                 return r;
401
402         if (conf_file) {
403                 r = config_parse(NULL, conf_file, NULL, sections, lookup, table, relaxed, false, true, userdata);
404                 if (r < 0)
405                         return r;
406         }
407
408         STRV_FOREACH(fn, files) {
409                 r = config_parse(NULL, *fn, NULL, sections, lookup, table, relaxed, false, true, userdata);
410                 if (r < 0)
411                         return r;
412         }
413
414         return 0;
415 }
416
417 #define DEFINE_PARSER(type, vartype, conv_func)                         \
418         int config_parse_##type(                                        \
419                         const char *unit,                               \
420                                 const char *filename,                   \
421                                 unsigned line,                          \
422                                 const char *section,                    \
423                                 unsigned section_line,                  \
424                                 const char *lvalue,                     \
425                                 int ltype,                              \
426                                 const char *rvalue,                     \
427                                 void *data,                             \
428                                 void *userdata) {                       \
429                                                                         \
430                 vartype *i = data;                                      \
431                 int r;                                                  \
432                                                                         \
433                 assert(filename);                                       \
434                 assert(lvalue);                                         \
435                 assert(rvalue);                                         \
436                 assert(data);                                           \
437                                                                         \
438                 r = conv_func(rvalue, i);                               \
439                 if (r < 0)                                              \
440                         log_syntax(unit, LOG_ERR, filename, line, r,    \
441                                    "Failed to parse %s value, ignoring: %s", \
442                                    #type, rvalue);                      \
443                                                                         \
444                 return 0;                                               \
445         }                                                               \
446         struct __useless_struct_to_allow_trailing_semicolon__
447
448 DEFINE_PARSER(int, int, safe_atoi);
449 DEFINE_PARSER(long, long, safe_atoli);
450 DEFINE_PARSER(uint32, uint32_t, safe_atou32);
451 DEFINE_PARSER(uint64, uint64_t, safe_atou64);
452 DEFINE_PARSER(unsigned, unsigned, safe_atou);
453 DEFINE_PARSER(double, double, safe_atod);
454 // UNNEEDED DEFINE_PARSER(nsec, nsec_t, parse_nsec);
455 DEFINE_PARSER(sec, usec_t, parse_sec);
456 DEFINE_PARSER(mode, mode_t, parse_mode);
457
458 int config_parse_iec_size(const char* unit,
459                             const char *filename,
460                             unsigned line,
461                             const char *section,
462                             unsigned section_line,
463                             const char *lvalue,
464                             int ltype,
465                             const char *rvalue,
466                             void *data,
467                             void *userdata) {
468
469         size_t *sz = data;
470         uint64_t v;
471         int r;
472
473         assert(filename);
474         assert(lvalue);
475         assert(rvalue);
476         assert(data);
477
478         r = parse_size(rvalue, 1024, &v);
479         if (r < 0 || (uint64_t) (size_t) v != v) {
480                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
481                 return 0;
482         }
483
484         *sz = (size_t) v;
485         return 0;
486 }
487
488 /// UNNEEDED by elogind
489 #if 0
490 int config_parse_si_size(const char* unit,
491                             const char *filename,
492                             unsigned line,
493                             const char *section,
494                             unsigned section_line,
495                             const char *lvalue,
496                             int ltype,
497                             const char *rvalue,
498                             void *data,
499                             void *userdata) {
500
501         size_t *sz = data;
502         uint64_t v;
503         int r;
504
505         assert(filename);
506         assert(lvalue);
507         assert(rvalue);
508         assert(data);
509
510         r = parse_size(rvalue, 1000, &v);
511         if (r < 0 || (uint64_t) (size_t) v != v) {
512                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
513                 return 0;
514         }
515
516         *sz = (size_t) v;
517         return 0;
518 }
519
520 int config_parse_iec_uint64(const char* unit,
521                            const char *filename,
522                            unsigned line,
523                            const char *section,
524                            unsigned section_line,
525                            const char *lvalue,
526                            int ltype,
527                            const char *rvalue,
528                            void *data,
529                            void *userdata) {
530
531         uint64_t *bytes = data;
532         int r;
533
534         assert(filename);
535         assert(lvalue);
536         assert(rvalue);
537         assert(data);
538
539         r = parse_size(rvalue, 1024, bytes);
540         if (r < 0)
541                 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
542
543         return 0;
544 }
545 #endif // 0
546
547 int config_parse_bool(const char* unit,
548                       const char *filename,
549                       unsigned line,
550                       const char *section,
551                       unsigned section_line,
552                       const char *lvalue,
553                       int ltype,
554                       const char *rvalue,
555                       void *data,
556                       void *userdata) {
557
558         int k;
559         bool *b = data;
560
561         assert(filename);
562         assert(lvalue);
563         assert(rvalue);
564         assert(data);
565
566         k = parse_boolean(rvalue);
567         if (k < 0) {
568                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
569                 return 0;
570         }
571
572         *b = !!k;
573         return 0;
574 }
575
576 /// UNNEEDED by elogind
577 #if 0
578 int config_parse_tristate(
579                 const char* unit,
580                 const char *filename,
581                 unsigned line,
582                 const char *section,
583                 unsigned section_line,
584                 const char *lvalue,
585                 int ltype,
586                 const char *rvalue,
587                 void *data,
588                 void *userdata) {
589
590         int k, *t = data;
591
592         assert(filename);
593         assert(lvalue);
594         assert(rvalue);
595         assert(data);
596
597         /* A tristate is pretty much a boolean, except that it can
598          * also take the special value -1, indicating "uninitialized",
599          * much like NULL is for a pointer type. */
600
601         k = parse_boolean(rvalue);
602         if (k < 0) {
603                 log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse boolean value, ignoring: %s", rvalue);
604                 return 0;
605         }
606
607         *t = !!k;
608         return 0;
609 }
610 #endif // 0
611
612 int config_parse_string(
613                 const char *unit,
614                 const char *filename,
615                 unsigned line,
616                 const char *section,
617                 unsigned section_line,
618                 const char *lvalue,
619                 int ltype,
620                 const char *rvalue,
621                 void *data,
622                 void *userdata) {
623
624         char **s = data, *n;
625
626         assert(filename);
627         assert(lvalue);
628         assert(rvalue);
629         assert(data);
630
631         if (!utf8_is_valid(rvalue)) {
632                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
633                 return 0;
634         }
635
636         if (isempty(rvalue))
637                 n = NULL;
638         else {
639                 n = strdup(rvalue);
640                 if (!n)
641                         return log_oom();
642         }
643
644         free(*s);
645         *s = n;
646
647         return 0;
648 }
649
650 int config_parse_path(
651                 const char *unit,
652                 const char *filename,
653                 unsigned line,
654                 const char *section,
655                 unsigned section_line,
656                 const char *lvalue,
657                 int ltype,
658                 const char *rvalue,
659                 void *data,
660                 void *userdata) {
661
662         char **s = data, *n;
663
664         assert(filename);
665         assert(lvalue);
666         assert(rvalue);
667         assert(data);
668
669         if (!utf8_is_valid(rvalue)) {
670                 log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
671                 return 0;
672         }
673
674         if (!path_is_absolute(rvalue)) {
675                 log_syntax(unit, LOG_ERR, filename, line, 0, "Not an absolute path, ignoring: %s", rvalue);
676                 return 0;
677         }
678
679         n = strdup(rvalue);
680         if (!n)
681                 return log_oom();
682
683         path_kill_slashes(n);
684
685         free(*s);
686         *s = n;
687
688         return 0;
689 }
690
691 int config_parse_strv(const char *unit,
692                       const char *filename,
693                       unsigned line,
694                       const char *section,
695                       unsigned section_line,
696                       const char *lvalue,
697                       int ltype,
698                       const char *rvalue,
699                       void *data,
700                       void *userdata) {
701
702         char ***sv = data;
703         const char *word, *state;
704         size_t l;
705         int r;
706
707         assert(filename);
708         assert(lvalue);
709         assert(rvalue);
710         assert(data);
711
712         if (isempty(rvalue)) {
713                 char **empty;
714
715                 /* Empty assignment resets the list. As a special rule
716                  * we actually fill in a real empty array here rather
717                  * than NULL, since some code wants to know if
718                  * something was set at all... */
719                 empty = strv_new(NULL, NULL);
720                 if (!empty)
721                         return log_oom();
722
723                 strv_free(*sv);
724                 *sv = empty;
725                 return 0;
726         }
727
728         FOREACH_WORD_QUOTED(word, l, rvalue, state) {
729                 char *n;
730
731                 n = strndup(word, l);
732                 if (!n)
733                         return log_oom();
734
735                 if (!utf8_is_valid(n)) {
736                         log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, rvalue);
737                         free(n);
738                         continue;
739                 }
740
741                 r = strv_consume(sv, n);
742                 if (r < 0)
743                         return log_oom();
744         }
745         if (!isempty(state))
746                 log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring.");
747
748         return 0;
749 }
750
751 /// UNNEEDED by elogind
752 #if 0
753 int config_parse_log_facility(
754                 const char *unit,
755                 const char *filename,
756                 unsigned line,
757                 const char *section,
758                 unsigned section_line,
759                 const char *lvalue,
760                 int ltype,
761                 const char *rvalue,
762                 void *data,
763                 void *userdata) {
764
765
766         int *o = data, x;
767
768         assert(filename);
769         assert(lvalue);
770         assert(rvalue);
771         assert(data);
772
773         x = log_facility_unshifted_from_string(rvalue);
774         if (x < 0) {
775                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log facility, ignoring: %s", rvalue);
776                 return 0;
777         }
778
779         *o = (x << 3) | LOG_PRI(*o);
780
781         return 0;
782 }
783 #endif // 0
784
785 int config_parse_log_level(
786                 const char *unit,
787                 const char *filename,
788                 unsigned line,
789                 const char *section,
790                 unsigned section_line,
791                 const char *lvalue,
792                 int ltype,
793                 const char *rvalue,
794                 void *data,
795                 void *userdata) {
796
797
798         int *o = data, x;
799
800         assert(filename);
801         assert(lvalue);
802         assert(rvalue);
803         assert(data);
804
805         x = log_level_from_string(rvalue);
806         if (x < 0) {
807                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse log level, ignoring: %s", rvalue);
808                 return 0;
809         }
810
811         *o = (*o & LOG_FACMASK) | x;
812         return 0;
813 }
814
815 int config_parse_signal(
816                 const char *unit,
817                 const char *filename,
818                 unsigned line,
819                 const char *section,
820                 unsigned section_line,
821                 const char *lvalue,
822                 int ltype,
823                 const char *rvalue,
824                 void *data,
825                 void *userdata) {
826
827         int *sig = data, r;
828
829         assert(filename);
830         assert(lvalue);
831         assert(rvalue);
832         assert(sig);
833
834         r = signal_from_string_try_harder(rvalue);
835         if (r <= 0) {
836                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse signal name, ignoring: %s", rvalue);
837                 return 0;
838         }
839
840         *sig = r;
841         return 0;
842 }
843
844 /// UNNEEDED by elogind
845 #if 0
846 int config_parse_personality(
847                 const char *unit,
848                 const char *filename,
849                 unsigned line,
850                 const char *section,
851                 unsigned section_line,
852                 const char *lvalue,
853                 int ltype,
854                 const char *rvalue,
855                 void *data,
856                 void *userdata) {
857
858         unsigned long *personality = data, p;
859
860         assert(filename);
861         assert(lvalue);
862         assert(rvalue);
863         assert(personality);
864
865         p = personality_from_string(rvalue);
866         if (p == PERSONALITY_INVALID) {
867                 log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse personality, ignoring: %s", rvalue);
868                 return 0;
869         }
870
871         *personality = p;
872         return 0;
873 }
874 #endif // 0