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