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