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