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