chiark / gitweb /
conf-parser: silently ignore X- extension fields
[elogind.git] / conf-parser.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
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 #define COMMENTS "#;\n"
35 #define LINE_MAX 4096
36
37 /* Run the user supplied parser for an assignment */
38 static int next_assignment(
39                 const char *filename,
40                 unsigned line,
41                 const char *section,
42                 const ConfigItem *t,
43                 const char *lvalue,
44                 const char *rvalue,
45                 void *userdata) {
46
47         assert(filename);
48         assert(t);
49         assert(lvalue);
50         assert(rvalue);
51
52         for (; t->parse; t++) {
53
54                 if (t->lvalue && !streq(lvalue, t->lvalue))
55                         continue;
56
57                 if (t->section && !section)
58                         continue;
59
60                 if (t->section && !streq(section, t->section))
61                         continue;
62
63                 return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
64         }
65
66         /* Warn about unknown non-extension fields. */
67         if (!startswith(lvalue, "X-"))
68                 log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section));
69
70         return 0;
71 }
72
73 /* Parse a variable assignment line */
74 static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) {
75         char *e;
76
77         l = strstrip(l);
78
79         if (!*l)
80                 return 0;
81
82         if (strchr(COMMENTS, *l))
83                 return 0;
84
85         if (startswith(l, ".include ")) {
86                 char *fn;
87                 int r;
88
89                 if (!(fn = file_in_same_dir(filename, strstrip(l+9))))
90                         return -ENOMEM;
91
92                 r = config_parse(fn, NULL, sections, t, userdata);
93                 free(fn);
94
95                 return r;
96         }
97
98         if (*l == '[') {
99                 size_t k;
100                 char *n;
101
102                 k = strlen(l);
103                 assert(k > 0);
104
105                 if (l[k-1] != ']') {
106                         log_error("[%s:%u] Invalid section header.", filename, line);
107                         return -EBADMSG;
108                 }
109
110                 if (!(n = strndup(l+1, k-2)))
111                         return -ENOMEM;
112
113                 if (sections && !strv_contains((char**) sections, n)) {
114                         free(n);
115                         return -EBADMSG;
116                 }
117
118                 free(*section);
119                 *section = n;
120
121                 return 0;
122         }
123
124         if (!(e = strchr(l, '='))) {
125                 log_error("[%s:%u] Missing '='.", filename, line);
126                 return -EBADMSG;
127         }
128
129         *e = 0;
130         e++;
131
132         return next_assignment(filename, line, *section, t, strstrip(l), strstrip(e), userdata);
133 }
134
135 /* Go through the file and parse each line */
136 int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) {
137         unsigned line = 0;
138         char *section = NULL;
139         int r;
140         bool ours = false;
141
142         assert(filename);
143         assert(t);
144
145         if (!f) {
146                 if (!(f = fopen(filename, "re"))) {
147                         r = -errno;
148                         log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
149                         goto finish;
150                 }
151
152                 ours = true;
153         }
154
155         while (!feof(f)) {
156                 char l[LINE_MAX];
157
158                 if (!fgets(l, sizeof(l), f)) {
159                         if (feof(f))
160                                 break;
161
162                         r = -errno;
163                         log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
164                         goto finish;
165                 }
166
167                 if ((r = parse_line(filename, ++line, &section, sections, t, l, userdata)) < 0)
168                         goto finish;
169         }
170
171         r = 0;
172
173 finish:
174         free(section);
175
176         if (f && ours)
177                 fclose(f);
178
179         return r;
180 }
181
182 int config_parse_int(
183                 const char *filename,
184                 unsigned line,
185                 const char *section,
186                 const char *lvalue,
187                 const char *rvalue,
188                 void *data,
189                 void *userdata) {
190
191         int *i = data;
192         int r;
193
194         assert(filename);
195         assert(lvalue);
196         assert(rvalue);
197         assert(data);
198
199         if ((r = safe_atoi(rvalue, i)) < 0) {
200                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
201                 return r;
202         }
203
204         return 0;
205 }
206
207 int config_parse_unsigned(
208                 const char *filename,
209                 unsigned line,
210                 const char *section,
211                 const char *lvalue,
212                 const char *rvalue,
213                 void *data,
214                 void *userdata) {
215
216         unsigned *u = data;
217         int r;
218
219         assert(filename);
220         assert(lvalue);
221         assert(rvalue);
222         assert(data);
223
224         if ((r = safe_atou(rvalue, u)) < 0) {
225                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
226                 return r;
227         }
228
229         return 0;
230 }
231
232 int config_parse_size(
233                 const char *filename,
234                 unsigned line,
235                 const char *section,
236                 const char *lvalue,
237                 const char *rvalue,
238                 void *data,
239                 void *userdata) {
240
241         size_t *sz = data;
242         unsigned u;
243         int r;
244
245         assert(filename);
246         assert(lvalue);
247         assert(rvalue);
248         assert(data);
249
250         if ((r = safe_atou(rvalue, &u)) < 0) {
251                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
252                 return r;
253         }
254
255         *sz = (size_t) u;
256         return 0;
257 }
258
259 int config_parse_bool(
260                 const char *filename,
261                 unsigned line,
262                 const char *section,
263                 const char *lvalue,
264                 const char *rvalue,
265                 void *data,
266                 void *userdata) {
267
268         int k;
269         bool *b = data;
270
271         assert(filename);
272         assert(lvalue);
273         assert(rvalue);
274         assert(data);
275
276         if ((k = parse_boolean(rvalue)) < 0) {
277                 log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
278                 return k;
279         }
280
281         *b = !!k;
282         return 0;
283 }
284
285 int config_parse_string(
286                 const char *filename,
287                 unsigned line,
288                 const char *section,
289                 const char *lvalue,
290                 const char *rvalue,
291                 void *data,
292                 void *userdata) {
293
294         char **s = data;
295         char *n;
296
297         assert(filename);
298         assert(lvalue);
299         assert(rvalue);
300         assert(data);
301
302         if (*rvalue) {
303                 if (!(n = strdup(rvalue)))
304                         return -ENOMEM;
305         } else
306                 n = NULL;
307
308         free(*s);
309         *s = n;
310
311         return 0;
312 }
313
314 int config_parse_path(
315                 const char *filename,
316                 unsigned line,
317                 const char *section,
318                 const char *lvalue,
319                 const char *rvalue,
320                 void *data,
321                 void *userdata) {
322
323         char **s = data;
324         char *n;
325
326         assert(filename);
327         assert(lvalue);
328         assert(rvalue);
329         assert(data);
330
331         if (*rvalue != '/') {
332                 log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
333                 return -EINVAL;
334         }
335
336         if (!(n = strdup(rvalue)))
337                 return -ENOMEM;
338
339         free(*s);
340         *s = n;
341
342         return 0;
343 }
344
345 int config_parse_strv(
346                 const char *filename,
347                 unsigned line,
348                 const char *section,
349                 const char *lvalue,
350                 const char *rvalue,
351                 void *data,
352                 void *userdata) {
353
354         char*** sv = data;
355         char **n;
356         char *w;
357         unsigned k;
358         size_t l;
359         char *state;
360
361         assert(filename);
362         assert(lvalue);
363         assert(rvalue);
364         assert(data);
365
366         k = strv_length(*sv);
367         FOREACH_WORD_QUOTED(w, l, rvalue, state)
368                 k++;
369
370         if (!(n = new(char*, k+1)))
371                 return -ENOMEM;
372
373         if (*sv)
374                 for (k = 0; (*sv)[k]; k++)
375                         n[k] = (*sv)[k];
376         else
377                 k = 0;
378
379         FOREACH_WORD_QUOTED(w, l, rvalue, state)
380                 if (!(n[k++] = strndup(w, l)))
381                         goto fail;
382
383         n[k] = NULL;
384         free(*sv);
385         *sv = n;
386
387         return 0;
388
389 fail:
390         for (; k > 0; k--)
391                 free(n[k-1]);
392         free(n);
393
394         return -ENOMEM;
395 }