chiark / gitweb /
Constify ConfigTableItem tables
[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, 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                  void *userdata) {
330
331         _cleanup_free_ char *section = NULL, *continuation = NULL;
332         _cleanup_fclose_ FILE *ours = NULL;
333         unsigned line = 0, section_line = 0;
334         bool section_ignored = false;
335         int r;
336
337         assert(filename);
338         assert(lookup);
339
340         if (!f) {
341                 f = ours = fopen(filename, "re");
342                 if (!f) {
343                         log_full(errno == ENOENT ? LOG_DEBUG : LOG_ERR, "Failed to open configuration file '%s': %m", filename);
344                         return errno == ENOENT ? 0 : -errno;
345                 }
346         }
347
348         fd_warn_permissions(filename, fileno(f));
349
350         while (!feof(f)) {
351                 char l[LINE_MAX], *p, *c = NULL, *e;
352                 bool escaped = false;
353
354                 if (!fgets(l, sizeof(l), f)) {
355                         if (feof(f))
356                                 break;
357
358                         log_error("Failed to read configuration file '%s': %m", filename);
359                         return -errno;
360                 }
361
362                 truncate_nl(l);
363
364                 if (continuation) {
365                         c = strappend(continuation, l);
366                         if (!c)
367                                 return -ENOMEM;
368
369                         free(continuation);
370                         continuation = NULL;
371                         p = c;
372                 } else
373                         p = l;
374
375                 for (e = p; *e; e++) {
376                         if (escaped)
377                                 escaped = false;
378                         else if (*e == '\\')
379                                 escaped = true;
380                 }
381
382                 if (escaped) {
383                         *(e-1) = ' ';
384
385                         if (c)
386                                 continuation = c;
387                         else {
388                                 continuation = strdup(l);
389                                 if (!continuation)
390                                         return -ENOMEM;
391                         }
392
393                         continue;
394                 }
395
396                 r = parse_line(unit,
397                                filename,
398                                ++line,
399                                sections,
400                                lookup,
401                                table,
402                                relaxed,
403                                allow_include,
404                                &section,
405                                &section_line,
406                                &section_ignored,
407                                p,
408                                userdata);
409                 free(c);
410
411                 if (r < 0)
412                         return r;
413         }
414
415         return 0;
416 }
417
418 #define DEFINE_PARSER(type, vartype, conv_func)                         \
419         int config_parse_##type(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                                    #vartype, rvalue);                   \
443                                                                         \
444                 return 0;                                               \
445         }
446
447 DEFINE_PARSER(int, int, safe_atoi)
448 DEFINE_PARSER(long, long, safe_atoli)
449 DEFINE_PARSER(uint64, uint64_t, safe_atou64)
450 DEFINE_PARSER(unsigned, unsigned, safe_atou)
451 DEFINE_PARSER(double, double, safe_atod)
452 DEFINE_PARSER(nsec, nsec_t, parse_nsec)
453 DEFINE_PARSER(sec, usec_t, parse_sec)
454
455 int config_parse_iec_size(const char* unit,
456                             const char *filename,
457                             unsigned line,
458                             const char *section,
459                             unsigned section_line,
460                             const char *lvalue,
461                             int ltype,
462                             const char *rvalue,
463                             void *data,
464                             void *userdata) {
465
466         size_t *sz = data;
467         off_t o;
468         int r;
469
470         assert(filename);
471         assert(lvalue);
472         assert(rvalue);
473         assert(data);
474
475         r = parse_size(rvalue, 1024, &o);
476         if (r < 0 || (off_t) (size_t) o != o) {
477                 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
478                 return 0;
479         }
480
481         *sz = (size_t) o;
482         return 0;
483 }
484
485 int config_parse_si_size(const char* unit,
486                             const char *filename,
487                             unsigned line,
488                             const char *section,
489                             unsigned section_line,
490                             const char *lvalue,
491                             int ltype,
492                             const char *rvalue,
493                             void *data,
494                             void *userdata) {
495
496         size_t *sz = data;
497         off_t o;
498         int r;
499
500         assert(filename);
501         assert(lvalue);
502         assert(rvalue);
503         assert(data);
504
505         r = parse_size(rvalue, 1000, &o);
506         if (r < 0 || (off_t) (size_t) o != o) {
507                 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
508                 return 0;
509         }
510
511         *sz = (size_t) o;
512         return 0;
513 }
514
515 int config_parse_iec_off(const char* unit,
516                            const char *filename,
517                            unsigned line,
518                            const char *section,
519                            unsigned section_line,
520                            const char *lvalue,
521                            int ltype,
522                            const char *rvalue,
523                            void *data,
524                            void *userdata) {
525
526         off_t *bytes = data;
527         int r;
528
529         assert(filename);
530         assert(lvalue);
531         assert(rvalue);
532         assert(data);
533
534         assert_cc(sizeof(off_t) == sizeof(uint64_t));
535
536         r = parse_size(rvalue, 1024, bytes);
537         if (r < 0)
538                 log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to parse size value, ignoring: %s", rvalue);
539
540         return 0;
541 }
542
543 int config_parse_bool(const char* unit,
544                       const char *filename,
545                       unsigned line,
546                       const char *section,
547                       unsigned section_line,
548                       const char *lvalue,
549                       int ltype,
550                       const char *rvalue,
551                       void *data,
552                       void *userdata) {
553
554         int k;
555         bool *b = data;
556
557         assert(filename);
558         assert(lvalue);
559         assert(rvalue);
560         assert(data);
561
562         k = parse_boolean(rvalue);
563         if (k < 0) {
564                 log_syntax(unit, LOG_ERR, filename, line, -k,
565                            "Failed to parse boolean value, ignoring: %s", rvalue);
566                 return 0;
567         }
568
569         *b = !!k;
570         return 0;
571 }
572
573 int config_parse_string(
574                 const char *unit,
575                 const char *filename,
576                 unsigned line,
577                 const char *section,
578                 unsigned section_line,
579                 const char *lvalue,
580                 int ltype,
581                 const char *rvalue,
582                 void *data,
583                 void *userdata) {
584
585         char **s = data, *n;
586
587         assert(filename);
588         assert(lvalue);
589         assert(rvalue);
590         assert(data);
591
592         if (!utf8_is_valid(rvalue)) {
593                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
594                 return 0;
595         }
596
597         if (isempty(rvalue))
598                 n = NULL;
599         else {
600                 n = strdup(rvalue);
601                 if (!n)
602                         return log_oom();
603         }
604
605         free(*s);
606         *s = n;
607
608         return 0;
609 }
610
611 int config_parse_path(
612                 const char *unit,
613                 const char *filename,
614                 unsigned line,
615                 const char *section,
616                 unsigned section_line,
617                 const char *lvalue,
618                 int ltype,
619                 const char *rvalue,
620                 void *data,
621                 void *userdata) {
622
623         char **s = data, *n;
624
625         assert(filename);
626         assert(lvalue);
627         assert(rvalue);
628         assert(data);
629
630         if (!utf8_is_valid(rvalue)) {
631                 log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
632                 return 0;
633         }
634
635         if (!path_is_absolute(rvalue)) {
636                 log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Not an absolute path, ignoring: %s", rvalue);
637                 return 0;
638         }
639
640         n = strdup(rvalue);
641         if (!n)
642                 return log_oom();
643
644         path_kill_slashes(n);
645
646         free(*s);
647         *s = n;
648
649         return 0;
650 }
651
652 int config_parse_strv(const char *unit,
653                       const char *filename,
654                       unsigned line,
655                       const char *section,
656                       unsigned section_line,
657                       const char *lvalue,
658                       int ltype,
659                       const char *rvalue,
660                       void *data,
661                       void *userdata) {
662
663         char *** sv = data, *w, *state;
664         size_t l;
665         int r;
666
667         assert(filename);
668         assert(lvalue);
669         assert(rvalue);
670         assert(data);
671
672         if (isempty(rvalue)) {
673                 char **empty;
674
675                 /* Empty assignment resets the list. As a special rule
676                  * we actually fill in a real empty array here rather
677                  * than NULL, since some code wants to know if
678                  * something was set at all... */
679                 empty = strv_new(NULL, NULL);
680                 if (!empty)
681                         return log_oom();
682
683                 strv_free(*sv);
684                 *sv = empty;
685                 return 0;
686         }
687
688         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
689                 char *n;
690
691                 n = strndup(w, l);
692                 if (!n)
693                         return log_oom();
694
695                 if (!utf8_is_valid(n)) {
696                         log_invalid_utf8(unit, LOG_ERR, filename, line, EINVAL, rvalue);
697                         continue;
698                 }
699
700                 r = strv_consume(sv, n);
701                 if (r < 0)
702                         return log_oom();
703         }
704
705         return 0;
706 }
707
708 int config_parse_mode(const char *unit,
709                       const char *filename,
710                       unsigned line,
711                       const char *section,
712                       unsigned section_line,
713                       const char *lvalue,
714                       int ltype,
715                       const char *rvalue,
716                       void *data,
717                       void *userdata) {
718
719         mode_t *m = data;
720         long l;
721         char *x = NULL;
722
723         assert(filename);
724         assert(lvalue);
725         assert(rvalue);
726         assert(data);
727
728         errno = 0;
729         l = strtol(rvalue, &x, 8);
730         if (!x || x == rvalue || *x || errno) {
731                 log_syntax(unit, LOG_ERR, filename, line, errno,
732                            "Failed to parse mode value, ignoring: %s", rvalue);
733                 return 0;
734         }
735
736         if (l < 0000 || l > 07777) {
737                 log_syntax(unit, LOG_ERR, filename, line, ERANGE,
738                            "Mode value out of range, ignoring: %s", rvalue);
739                 return 0;
740         }
741
742         *m = (mode_t) l;
743         return 0;
744 }
745
746 int config_parse_log_facility(
747                 const char *unit,
748                 const char *filename,
749                 unsigned line,
750                 const char *section,
751                 unsigned section_line,
752                 const char *lvalue,
753                 int ltype,
754                 const char *rvalue,
755                 void *data,
756                 void *userdata) {
757
758
759         int *o = data, x;
760
761         assert(filename);
762         assert(lvalue);
763         assert(rvalue);
764         assert(data);
765
766         x = log_facility_unshifted_from_string(rvalue);
767         if (x < 0) {
768                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
769                            "Failed to parse log facility, ignoring: %s", rvalue);
770                 return 0;
771         }
772
773         *o = (x << 3) | LOG_PRI(*o);
774
775         return 0;
776 }
777
778 int config_parse_log_level(
779                 const char *unit,
780                 const char *filename,
781                 unsigned line,
782                 const char *section,
783                 unsigned section_line,
784                 const char *lvalue,
785                 int ltype,
786                 const char *rvalue,
787                 void *data,
788                 void *userdata) {
789
790
791         int *o = data, x;
792
793         assert(filename);
794         assert(lvalue);
795         assert(rvalue);
796         assert(data);
797
798         x = log_level_from_string(rvalue);
799         if (x < 0) {
800                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
801                            "Failed to parse log level, ignoring: %s", rvalue);
802                 return 0;
803         }
804
805         *o = (*o & LOG_FACMASK) | x;
806         return 0;
807 }