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