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