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