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