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