chiark / gitweb /
a71dcd0d8fb8c87460ea24f81b244334ce740519
[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_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         unsigned u;
469         int r;
470
471         assert(filename);
472         assert(lvalue);
473         assert(rvalue);
474         assert(data);
475
476         if ((r = safe_atou(rvalue, &u)) < 0) {
477                 log_error("[%s:%u] Failed to parse numeric value, ignoring: %s", filename, line, rvalue);
478                 return 0;
479         }
480
481         *sz = (size_t) u;
482         return 0;
483 }
484
485 int config_parse_bool(
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         int k;
496         bool *b = data;
497
498         assert(filename);
499         assert(lvalue);
500         assert(rvalue);
501         assert(data);
502
503         if ((k = parse_boolean(rvalue)) < 0) {
504                 log_error("[%s:%u] Failed to parse boolean value, ignoring: %s", filename, line, rvalue);
505                 return 0;
506         }
507
508         *b = !!k;
509         return 0;
510 }
511
512 int config_parse_string(
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         char **s = data;
523         char *n;
524
525         assert(filename);
526         assert(lvalue);
527         assert(rvalue);
528         assert(data);
529
530         if (*rvalue) {
531                 if (!(n = strdup(rvalue)))
532                         return -ENOMEM;
533         } else
534                 n = NULL;
535
536         free(*s);
537         *s = n;
538
539         return 0;
540 }
541
542 int config_parse_path(
543                 const char *filename,
544                 unsigned line,
545                 const char *section,
546                 const char *lvalue,
547                 int ltype,
548                 const char *rvalue,
549                 void *data,
550                 void *userdata) {
551
552         char **s = data;
553         char *n;
554
555         assert(filename);
556         assert(lvalue);
557         assert(rvalue);
558         assert(data);
559
560         if (!path_is_absolute(rvalue)) {
561                 log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
562                 return 0;
563         }
564
565         if (!(n = strdup(rvalue)))
566                 return -ENOMEM;
567
568         path_kill_slashes(n);
569
570         free(*s);
571         *s = n;
572
573         return 0;
574 }
575
576 int config_parse_strv(
577                 const char *filename,
578                 unsigned line,
579                 const char *section,
580                 const char *lvalue,
581                 int ltype,
582                 const char *rvalue,
583                 void *data,
584                 void *userdata) {
585
586         char*** sv = data;
587         char **n;
588         char *w;
589         unsigned k;
590         size_t l;
591         char *state;
592
593         assert(filename);
594         assert(lvalue);
595         assert(rvalue);
596         assert(data);
597
598         k = strv_length(*sv);
599         FOREACH_WORD_QUOTED(w, l, rvalue, state)
600                 k++;
601
602         if (!(n = new(char*, k+1)))
603                 return -ENOMEM;
604
605         if (*sv)
606                 for (k = 0; (*sv)[k]; k++)
607                         n[k] = (*sv)[k];
608         else
609                 k = 0;
610
611         FOREACH_WORD_QUOTED(w, l, rvalue, state)
612                 if (!(n[k++] = cunescape_length(w, l)))
613                         goto fail;
614
615         n[k] = NULL;
616         free(*sv);
617         *sv = n;
618
619         return 0;
620
621 fail:
622         for (; k > 0; k--)
623                 free(n[k-1]);
624         free(n);
625
626         return -ENOMEM;
627 }
628
629 int config_parse_path_strv(
630                 const char *filename,
631                 unsigned line,
632                 const char *section,
633                 const char *lvalue,
634                 int ltype,
635                 const char *rvalue,
636                 void *data,
637                 void *userdata) {
638
639         char*** sv = data;
640         char **n;
641         char *w;
642         unsigned k;
643         size_t l;
644         char *state;
645         int r;
646
647         assert(filename);
648         assert(lvalue);
649         assert(rvalue);
650         assert(data);
651
652         k = strv_length(*sv);
653         FOREACH_WORD_QUOTED(w, l, rvalue, state)
654                 k++;
655
656         if (!(n = new(char*, k+1)))
657                 return -ENOMEM;
658
659         k = 0;
660         if (*sv)
661                 for (; (*sv)[k]; k++)
662                         n[k] = (*sv)[k];
663
664         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
665                 if (!(n[k] = cunescape_length(w, l))) {
666                         r = -ENOMEM;
667                         goto fail;
668                 }
669
670                 if (!path_is_absolute(n[k])) {
671                         log_error("[%s:%u] Not an absolute path, ignoring: %s", filename, line, rvalue);
672                         free(n[k]);
673                         continue;
674                 }
675
676                 path_kill_slashes(n[k]);
677
678                 k++;
679         }
680
681         n[k] = NULL;
682         free(*sv);
683         *sv = n;
684
685         return 0;
686
687 fail:
688         free(n[k]);
689         for (; k > 0; k--)
690                 free(n[k-1]);
691         free(n);
692
693         return r;
694 }
695
696 int config_parse_usec(
697                 const char *filename,
698                 unsigned line,
699                 const char *section,
700                 const char *lvalue,
701                 int ltype,
702                 const char *rvalue,
703                 void *data,
704                 void *userdata) {
705
706         usec_t *usec = data;
707
708         assert(filename);
709         assert(lvalue);
710         assert(rvalue);
711         assert(data);
712
713         if (parse_usec(rvalue, usec) < 0) {
714                 log_error("[%s:%u] Failed to parse time value, ignoring: %s", filename, line, rvalue);
715                 return 0;
716         }
717
718         return 0;
719 }
720
721 int config_parse_mode(
722                 const char *filename,
723                 unsigned line,
724                 const char *section,
725                 const char *lvalue,
726                 int ltype,
727                 const char *rvalue,
728                 void *data,
729                 void *userdata) {
730
731         mode_t *m = data;
732         long l;
733         char *x = NULL;
734
735         assert(filename);
736         assert(lvalue);
737         assert(rvalue);
738         assert(data);
739
740         errno = 0;
741         l = strtol(rvalue, &x, 8);
742         if (!x || *x || errno) {
743                 log_error("[%s:%u] Failed to parse mode value, ignoring: %s", filename, line, rvalue);
744                 return 0;
745         }
746
747         if (l < 0000 || l > 07777) {
748                 log_error("[%s:%u] mode value out of range, ignoring: %s", filename, line, rvalue);
749                 return 0;
750         }
751
752         *m = (mode_t) l;
753         return 0;
754 }