chiark / gitweb /
dbus: return DBUS_ERROR_UNKNOWN_OBJECT when an object is unknown
[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 /* Run the user supplied parser for an assignment */
35 static int next_assignment(
36                 const char *filename,
37                 unsigned line,
38                 const char *section,
39                 const ConfigItem *t,
40                 bool relaxed,
41                 const char *lvalue,
42                 const char *rvalue,
43                 void *userdata) {
44
45         assert(filename);
46         assert(t);
47         assert(lvalue);
48         assert(rvalue);
49
50         for (; t->parse || t->lvalue; t++) {
51
52                 if (t->lvalue && !streq(lvalue, t->lvalue))
53                         continue;
54
55                 if (t->section && !section)
56                         continue;
57
58                 if (t->section && !streq(section, t->section))
59                         continue;
60
61                 if (!t->parse)
62                         return 0;
63
64                 return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
65         }
66
67         /* Warn about unknown non-extension fields. */
68         if (!relaxed && !startswith(lvalue, "X-"))
69                 log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section));
70
71         return 0;
72 }
73
74 /* Parse a variable assignment line */
75 static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, bool relaxed, char *l, void *userdata) {
76         char *e;
77
78         l = strstrip(l);
79
80         if (!*l)
81                 return 0;
82
83         if (strchr(COMMENTS, *l))
84                 return 0;
85
86         if (startswith(l, ".include ")) {
87                 char *fn;
88                 int r;
89
90                 if (!(fn = file_in_same_dir(filename, strstrip(l+9))))
91                         return -ENOMEM;
92
93                 r = config_parse(fn, NULL, sections, t, relaxed, userdata);
94                 free(fn);
95
96                 return r;
97         }
98
99         if (*l == '[') {
100                 size_t k;
101                 char *n;
102
103                 k = strlen(l);
104                 assert(k > 0);
105
106                 if (l[k-1] != ']') {
107                         log_error("[%s:%u] Invalid section header.", filename, line);
108                         return -EBADMSG;
109                 }
110
111                 if (!(n = strndup(l+1, k-2)))
112                         return -ENOMEM;
113
114                 if (!relaxed && sections && !strv_contains((char**) sections, n))
115                         log_info("[%s:%u] Unknown section '%s'. Ignoring.", filename, line, n);
116
117                 free(*section);
118                 *section = n;
119
120                 return 0;
121         }
122
123         if (sections && (!*section || !strv_contains((char**) sections, *section)))
124                 return 0;
125
126         if (!(e = strchr(l, '='))) {
127                 log_error("[%s:%u] Missing '='.", filename, line);
128                 return -EBADMSG;
129         }
130
131         *e = 0;
132         e++;
133
134         return next_assignment(filename, line, *section, t, relaxed, strstrip(l), strstrip(e), userdata);
135 }
136
137 /* Go through the file and parse each line */
138 int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, bool relaxed, void *userdata) {
139         unsigned line = 0;
140         char *section = NULL;
141         int r;
142         bool ours = false;
143         char *continuation = NULL;
144
145         assert(filename);
146         assert(t);
147
148         if (!f) {
149                 if (!(f = fopen(filename, "re"))) {
150                         r = -errno;
151                         log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
152                         goto finish;
153                 }
154
155                 ours = true;
156         }
157
158         while (!feof(f)) {
159                 char l[LINE_MAX], *p, *c = NULL, *e;
160                 bool escaped = false;
161
162                 if (!fgets(l, sizeof(l), f)) {
163                         if (feof(f))
164                                 break;
165
166                         r = -errno;
167                         log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
168                         goto finish;
169                 }
170
171                 truncate_nl(l);
172
173                 if (continuation) {
174                         if (!(c = strappend(continuation, l))) {
175                                 r = -ENOMEM;
176                                 goto finish;
177                         }
178
179                         free(continuation);
180                         continuation = NULL;
181                         p = c;
182                 } else
183                         p = l;
184
185                 for (e = p; *e; e++) {
186                         if (escaped)
187                                 escaped = false;
188                         else if (*e == '\\')
189                                 escaped = true;
190                 }
191
192                 if (escaped) {
193                         *(e-1) = ' ';
194
195                         if (c)
196                                 continuation = c;
197                         else if (!(continuation = strdup(l))) {
198                                 r = -ENOMEM;
199                                 goto finish;
200                         }
201
202                         continue;
203                 }
204
205                 r = parse_line(filename, ++line, &section, sections, t, relaxed, p, userdata);
206                 free(c);
207
208                 if (r < 0)
209                         goto finish;
210         }
211
212         r = 0;
213
214 finish:
215         free(section);
216         free(continuation);
217
218         if (f && ours)
219                 fclose(f);
220
221         return r;
222 }
223
224 int config_parse_int(
225                 const char *filename,
226                 unsigned line,
227                 const char *section,
228                 const char *lvalue,
229                 const char *rvalue,
230                 void *data,
231                 void *userdata) {
232
233         int *i = data;
234         int r;
235
236         assert(filename);
237         assert(lvalue);
238         assert(rvalue);
239         assert(data);
240
241         if ((r = safe_atoi(rvalue, i)) < 0) {
242                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
243                 return r;
244         }
245
246         return 0;
247 }
248
249 int config_parse_uint64(
250                 const char *filename,
251                 unsigned line,
252                 const char *section,
253                 const char *lvalue,
254                 const char *rvalue,
255                 void *data,
256                 void *userdata) {
257
258         uint64_t *u = data;
259         int r;
260
261         assert(filename);
262         assert(lvalue);
263         assert(rvalue);
264         assert(data);
265
266         if ((r = safe_atou64(rvalue, u)) < 0) {
267                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
268                 return r;
269         }
270
271         return 0;
272 }
273
274 int config_parse_unsigned(
275                 const char *filename,
276                 unsigned line,
277                 const char *section,
278                 const char *lvalue,
279                 const char *rvalue,
280                 void *data,
281                 void *userdata) {
282
283         unsigned *u = data;
284         int r;
285
286         assert(filename);
287         assert(lvalue);
288         assert(rvalue);
289         assert(data);
290
291         if ((r = safe_atou(rvalue, u)) < 0) {
292                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
293                 return r;
294         }
295
296         return 0;
297 }
298
299 int config_parse_size(
300                 const char *filename,
301                 unsigned line,
302                 const char *section,
303                 const char *lvalue,
304                 const char *rvalue,
305                 void *data,
306                 void *userdata) {
307
308         size_t *sz = data;
309         unsigned u;
310         int r;
311
312         assert(filename);
313         assert(lvalue);
314         assert(rvalue);
315         assert(data);
316
317         if ((r = safe_atou(rvalue, &u)) < 0) {
318                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
319                 return r;
320         }
321
322         *sz = (size_t) u;
323         return 0;
324 }
325
326 int config_parse_bool(
327                 const char *filename,
328                 unsigned line,
329                 const char *section,
330                 const char *lvalue,
331                 const char *rvalue,
332                 void *data,
333                 void *userdata) {
334
335         int k;
336         bool *b = data;
337
338         assert(filename);
339         assert(lvalue);
340         assert(rvalue);
341         assert(data);
342
343         if ((k = parse_boolean(rvalue)) < 0) {
344                 log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
345                 return k;
346         }
347
348         *b = !!k;
349         return 0;
350 }
351
352 int config_parse_string(
353                 const char *filename,
354                 unsigned line,
355                 const char *section,
356                 const char *lvalue,
357                 const char *rvalue,
358                 void *data,
359                 void *userdata) {
360
361         char **s = data;
362         char *n;
363
364         assert(filename);
365         assert(lvalue);
366         assert(rvalue);
367         assert(data);
368
369         if (*rvalue) {
370                 if (!(n = strdup(rvalue)))
371                         return -ENOMEM;
372         } else
373                 n = NULL;
374
375         free(*s);
376         *s = n;
377
378         return 0;
379 }
380
381 int config_parse_path(
382                 const char *filename,
383                 unsigned line,
384                 const char *section,
385                 const char *lvalue,
386                 const char *rvalue,
387                 void *data,
388                 void *userdata) {
389
390         char **s = data;
391         char *n;
392
393         assert(filename);
394         assert(lvalue);
395         assert(rvalue);
396         assert(data);
397
398         if (!path_is_absolute(rvalue)) {
399                 log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
400                 return -EINVAL;
401         }
402
403         if (!(n = strdup(rvalue)))
404                 return -ENOMEM;
405
406         path_kill_slashes(n);
407
408         free(*s);
409         *s = n;
410
411         return 0;
412 }
413
414 int config_parse_strv(
415                 const char *filename,
416                 unsigned line,
417                 const char *section,
418                 const char *lvalue,
419                 const char *rvalue,
420                 void *data,
421                 void *userdata) {
422
423         char*** sv = data;
424         char **n;
425         char *w;
426         unsigned k;
427         size_t l;
428         char *state;
429
430         assert(filename);
431         assert(lvalue);
432         assert(rvalue);
433         assert(data);
434
435         k = strv_length(*sv);
436         FOREACH_WORD_QUOTED(w, l, rvalue, state)
437                 k++;
438
439         if (!(n = new(char*, k+1)))
440                 return -ENOMEM;
441
442         if (*sv)
443                 for (k = 0; (*sv)[k]; k++)
444                         n[k] = (*sv)[k];
445         else
446                 k = 0;
447
448         FOREACH_WORD_QUOTED(w, l, rvalue, state)
449                 if (!(n[k++] = cunescape_length(w, l)))
450                         goto fail;
451
452         n[k] = NULL;
453         free(*sv);
454         *sv = n;
455
456         return 0;
457
458 fail:
459         for (; k > 0; k--)
460                 free(n[k-1]);
461         free(n);
462
463         return -ENOMEM;
464 }
465
466 int config_parse_path_strv(
467                 const char *filename,
468                 unsigned line,
469                 const char *section,
470                 const char *lvalue,
471                 const char *rvalue,
472                 void *data,
473                 void *userdata) {
474
475         char*** sv = data;
476         char **n;
477         char *w;
478         unsigned k;
479         size_t l;
480         char *state;
481         int r;
482
483         assert(filename);
484         assert(lvalue);
485         assert(rvalue);
486         assert(data);
487
488         k = strv_length(*sv);
489         FOREACH_WORD_QUOTED(w, l, rvalue, state)
490                 k++;
491
492         if (!(n = new(char*, k+1)))
493                 return -ENOMEM;
494
495         k = 0;
496         if (*sv)
497                 for (; (*sv)[k]; k++)
498                         n[k] = (*sv)[k];
499
500         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
501                 if (!(n[k] = cunescape_length(w, l))) {
502                         r = -ENOMEM;
503                         goto fail;
504                 }
505
506                 if (!path_is_absolute(n[k])) {
507                         log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
508                         r = -EINVAL;
509                         goto fail;
510                 }
511
512                 path_kill_slashes(n[k]);
513
514                 k++;
515         }
516
517         n[k] = NULL;
518         free(*sv);
519         *sv = n;
520
521         return 0;
522
523 fail:
524         free(n[k]);
525         for (; k > 0; k--)
526                 free(n[k-1]);
527         free(n);
528
529         return r;
530 }