chiark / gitweb /
build-sys: move source files to subdirectory
[elogind.git] / src / 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                         log_error("[%s:%u] Unknown section '%s'.", filename, line, n);
115                         free(n);
116                         return -EBADMSG;
117                 }
118
119                 free(*section);
120                 *section = n;
121
122                 return 0;
123         }
124
125         if (!(e = strchr(l, '='))) {
126                 log_error("[%s:%u] Missing '='.", filename, line);
127                 return -EBADMSG;
128         }
129
130         *e = 0;
131         e++;
132
133         return next_assignment(filename, line, *section, t, strstrip(l), strstrip(e), userdata);
134 }
135
136 /* Go through the file and parse each line */
137 int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) {
138         unsigned line = 0;
139         char *section = NULL;
140         int r;
141         bool ours = false;
142
143         assert(filename);
144         assert(t);
145
146         if (!f) {
147                 if (!(f = fopen(filename, "re"))) {
148                         r = -errno;
149                         log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
150                         goto finish;
151                 }
152
153                 ours = true;
154         }
155
156         while (!feof(f)) {
157                 char l[LINE_MAX];
158
159                 if (!fgets(l, sizeof(l), f)) {
160                         if (feof(f))
161                                 break;
162
163                         r = -errno;
164                         log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
165                         goto finish;
166                 }
167
168                 if ((r = parse_line(filename, ++line, &section, sections, t, l, userdata)) < 0)
169                         goto finish;
170         }
171
172         r = 0;
173
174 finish:
175         free(section);
176
177         if (f && ours)
178                 fclose(f);
179
180         return r;
181 }
182
183 int config_parse_int(
184                 const char *filename,
185                 unsigned line,
186                 const char *section,
187                 const char *lvalue,
188                 const char *rvalue,
189                 void *data,
190                 void *userdata) {
191
192         int *i = data;
193         int r;
194
195         assert(filename);
196         assert(lvalue);
197         assert(rvalue);
198         assert(data);
199
200         if ((r = safe_atoi(rvalue, i)) < 0) {
201                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
202                 return r;
203         }
204
205         return 0;
206 }
207
208 int config_parse_unsigned(
209                 const char *filename,
210                 unsigned line,
211                 const char *section,
212                 const char *lvalue,
213                 const char *rvalue,
214                 void *data,
215                 void *userdata) {
216
217         unsigned *u = data;
218         int r;
219
220         assert(filename);
221         assert(lvalue);
222         assert(rvalue);
223         assert(data);
224
225         if ((r = safe_atou(rvalue, u)) < 0) {
226                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
227                 return r;
228         }
229
230         return 0;
231 }
232
233 int config_parse_size(
234                 const char *filename,
235                 unsigned line,
236                 const char *section,
237                 const char *lvalue,
238                 const char *rvalue,
239                 void *data,
240                 void *userdata) {
241
242         size_t *sz = data;
243         unsigned u;
244         int r;
245
246         assert(filename);
247         assert(lvalue);
248         assert(rvalue);
249         assert(data);
250
251         if ((r = safe_atou(rvalue, &u)) < 0) {
252                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
253                 return r;
254         }
255
256         *sz = (size_t) u;
257         return 0;
258 }
259
260 int config_parse_bool(
261                 const char *filename,
262                 unsigned line,
263                 const char *section,
264                 const char *lvalue,
265                 const char *rvalue,
266                 void *data,
267                 void *userdata) {
268
269         int k;
270         bool *b = data;
271
272         assert(filename);
273         assert(lvalue);
274         assert(rvalue);
275         assert(data);
276
277         if ((k = parse_boolean(rvalue)) < 0) {
278                 log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
279                 return k;
280         }
281
282         *b = !!k;
283         return 0;
284 }
285
286 int config_parse_string(
287                 const char *filename,
288                 unsigned line,
289                 const char *section,
290                 const char *lvalue,
291                 const char *rvalue,
292                 void *data,
293                 void *userdata) {
294
295         char **s = data;
296         char *n;
297
298         assert(filename);
299         assert(lvalue);
300         assert(rvalue);
301         assert(data);
302
303         if (*rvalue) {
304                 if (!(n = strdup(rvalue)))
305                         return -ENOMEM;
306         } else
307                 n = NULL;
308
309         free(*s);
310         *s = n;
311
312         return 0;
313 }
314
315 int config_parse_path(
316                 const char *filename,
317                 unsigned line,
318                 const char *section,
319                 const char *lvalue,
320                 const char *rvalue,
321                 void *data,
322                 void *userdata) {
323
324         char **s = data;
325         char *n;
326
327         assert(filename);
328         assert(lvalue);
329         assert(rvalue);
330         assert(data);
331
332         if (!path_is_absolute(rvalue)) {
333                 log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
334                 return -EINVAL;
335         }
336
337         if (!(n = strdup(rvalue)))
338                 return -ENOMEM;
339
340         free(*s);
341         *s = n;
342
343         return 0;
344 }
345
346 int config_parse_strv(
347                 const char *filename,
348                 unsigned line,
349                 const char *section,
350                 const char *lvalue,
351                 const char *rvalue,
352                 void *data,
353                 void *userdata) {
354
355         char*** sv = data;
356         char **n;
357         char *w;
358         unsigned k;
359         size_t l;
360         char *state;
361
362         assert(filename);
363         assert(lvalue);
364         assert(rvalue);
365         assert(data);
366
367         k = strv_length(*sv);
368         FOREACH_WORD_QUOTED(w, l, rvalue, state)
369                 k++;
370
371         if (!(n = new(char*, k+1)))
372                 return -ENOMEM;
373
374         if (*sv)
375                 for (k = 0; (*sv)[k]; k++)
376                         n[k] = (*sv)[k];
377         else
378                 k = 0;
379
380         FOREACH_WORD_QUOTED(w, l, rvalue, state)
381                 if (!(n[k++] = strndup(w, l)))
382                         goto fail;
383
384         n[k] = NULL;
385         free(*sv);
386         *sv = n;
387
388         return 0;
389
390 fail:
391         for (; k > 0; k--)
392                 free(n[k-1]);
393         free(n);
394
395         return -ENOMEM;
396 }
397
398 int config_parse_path_strv(
399                 const char *filename,
400                 unsigned line,
401                 const char *section,
402                 const char *lvalue,
403                 const char *rvalue,
404                 void *data,
405                 void *userdata) {
406
407         char*** sv = data;
408         char **n;
409         char *w;
410         unsigned k;
411         size_t l;
412         char *state;
413         int r;
414
415         assert(filename);
416         assert(lvalue);
417         assert(rvalue);
418         assert(data);
419
420         k = strv_length(*sv);
421         FOREACH_WORD_QUOTED(w, l, rvalue, state)
422                 k++;
423
424         if (!(n = new(char*, k+1)))
425                 return -ENOMEM;
426
427         k = 0;
428         if (*sv)
429                 for (; (*sv)[k]; k++)
430                         n[k] = (*sv)[k];
431
432         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
433                 if (!(n[k] = strndup(w, l))) {
434                         r = -ENOMEM;
435                         goto fail;
436                 }
437
438                 if (!path_is_absolute(n[k])) {
439                         log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
440                         r = -EINVAL;
441                         goto fail;
442                 }
443
444                 k++;
445         }
446
447         n[k] = NULL;
448         free(*sv);
449         *sv = n;
450
451         return 0;
452
453 fail:
454         free(n[k]);
455         for (; k > 0; k--)
456                 free(n[k-1]);
457         free(n);
458
459         return r;
460 }