chiark / gitweb /
rework socket handling
[elogind.git] / conf-parser.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include <stdlib.h>
8
9 #include "conf-parser.h"
10 #include "util.h"
11 #include "macro.h"
12 #include "strv.h"
13 #include "log.h"
14
15 #define WHITESPACE " \t\n"
16 #define COMMENTS "#;\n"
17 #define LINE_MAX 4096
18
19 /* Run the user supplied parser for an assignment */
20 static int next_assignment(
21                 const char *filename,
22                 unsigned line,
23                 const char *section,
24                 const ConfigItem *t,
25                 const char *lvalue,
26                 const char *rvalue,
27                 void *userdata) {
28
29         assert(filename);
30         assert(t);
31         assert(lvalue);
32         assert(rvalue);
33
34         for (; t->parse; t++) {
35
36                 if (t->lvalue && !streq(lvalue, t->lvalue))
37                         continue;
38
39                 if (t->section && !section)
40                         continue;
41
42                 if (t->section && !streq(section, t->section))
43                         continue;
44
45                 return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
46         }
47
48         log_error("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, strna(section));
49         return -EBADMSG;
50 }
51
52 /* Returns non-zero when c is contained in s */
53 static int in_string(char c, const char *s) {
54         assert(s);
55
56         for (; *s; s++)
57                 if (*s == c)
58                         return 1;
59
60         return 0;
61 }
62
63 /* Remove all whitepsapce from the beginning and the end of *s. *s may
64  * be modified. */
65 static char *strip(char *s) {
66         char *b = s+strspn(s, WHITESPACE);
67         char *e, *l = NULL;
68
69         for (e = b; *e; e++)
70                 if (!in_string(*e, WHITESPACE))
71                         l = e;
72
73         if (l)
74                 *(l+1) = 0;
75
76         return b;
77 }
78
79 /* Parse a variable assignment line */
80 static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) {
81         char *e, *c, *b;
82
83         b = l+strspn(l, WHITESPACE);
84
85         if ((c = strpbrk(b, COMMENTS)))
86                 *c = 0;
87
88         if (!*b)
89                 return 0;
90
91         if (startswith(b, ".include ")) {
92                 char *path = NULL, *fn;
93                 int r;
94
95                 fn = strip(b+9);
96                 if (!is_path_absolute(fn)) {
97                         const char *k;
98
99                         if ((k = strrchr(filename, '/'))) {
100                                 char *dir;
101
102                                 if (!(dir = strndup(filename, k-filename)))
103                                         return -ENOMEM;
104
105                                 if (asprintf(&path, "%s/%s", dir, fn) < 0)
106                                         return -errno;
107
108                                 fn = path;
109                                 free(dir);
110                         }
111                 }
112
113                 r = config_parse(fn, sections, t, userdata);
114                 free(path);
115                 return r;
116         }
117
118         if (*b == '[') {
119                 size_t k;
120                 char *n;
121
122                 k = strlen(b);
123                 assert(k > 0);
124
125                 if (b[k-1] != ']') {
126                         log_error("[%s:%u] Invalid section header.", filename, line);
127                         return -EBADMSG;
128                 }
129
130                 if (!(n = strndup(b+1, k-2)))
131                         return -ENOMEM;
132
133                 if (sections) {
134                         const char * const * i;
135                         bool good = false;
136                         STRV_FOREACH(i, sections)
137                                 if (streq(*i, n)) {
138                                         good = true;
139                                         break;
140                                 }
141
142                         if (!good) {
143                                 free(n);
144                                 return -EBADMSG;
145                         }
146                 }
147
148                 free(*section);
149                 *section = n;
150
151                 return 0;
152         }
153
154         if (!(e = strchr(b, '='))) {
155                 log_error("[%s:%u] Missing '='.", filename, line);
156                 return -EBADMSG;
157         }
158
159         *e = 0;
160         e++;
161
162         return next_assignment(filename, line, *section, t, strip(b), strip(e), userdata);
163 }
164
165 /* Go through the file and parse each line */
166 int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata) {
167         unsigned line = 0;
168         char *section = NULL;
169         FILE *f;
170         int r;
171
172         assert(filename);
173         assert(t);
174
175         if (!(f = fopen(filename, "re"))) {
176                 r = -errno;
177                 log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
178                 goto finish;
179         }
180
181         while (!feof(f)) {
182                 char l[LINE_MAX];
183
184                 if (!fgets(l, sizeof(l), f)) {
185                         if (feof(f))
186                                 break;
187
188                         r = -errno;
189                         log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
190                         goto finish;
191                 }
192
193                 if ((r = parse_line(filename, ++line, &section, sections, t, l, userdata)) < 0)
194                         goto finish;
195         }
196
197         r = 0;
198
199 finish:
200         free(section);
201
202         if (f)
203                 fclose(f);
204
205         return r;
206 }
207
208 int config_parse_int(
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         int *i = data;
218         int r;
219
220         assert(filename);
221         assert(lvalue);
222         assert(rvalue);
223         assert(data);
224
225         if ((r = safe_atoi(rvalue, i)) < 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_unsigned(
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         unsigned *u = data;
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         return 0;
256 }
257
258 int config_parse_size(
259                 const char *filename,
260                 unsigned line,
261                 const char *section,
262                 const char *lvalue,
263                 const char *rvalue,
264                 void *data,
265                 void *userdata) {
266
267         size_t *sz = data;
268         unsigned u;
269         int r;
270
271         assert(filename);
272         assert(lvalue);
273         assert(rvalue);
274         assert(data);
275
276         if ((r = safe_atou(rvalue, &u)) < 0) {
277                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
278                 return r;
279         }
280
281         *sz = (size_t) u;
282         return 0;
283 }
284
285 int config_parse_bool(
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         int k;
295         bool *b = data;
296
297         assert(filename);
298         assert(lvalue);
299         assert(rvalue);
300         assert(data);
301
302         if ((k = parse_boolean(rvalue)) < 0) {
303                 log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
304                 return k;
305         }
306
307         *b = !!k;
308         return 0;
309 }
310
311 int config_parse_string(
312                 const char *filename,
313                 unsigned line,
314                 const char *section,
315                 const char *lvalue,
316                 const char *rvalue,
317                 void *data,
318                 void *userdata) {
319
320         char **s = data;
321         char *n;
322
323         assert(filename);
324         assert(lvalue);
325         assert(rvalue);
326         assert(data);
327
328         if (*rvalue) {
329                 if (!(n = strdup(rvalue)))
330                         return -ENOMEM;
331         } else
332                 n = NULL;
333
334         free(*s);
335         *s = n;
336
337         return 0;
338 }
339
340
341 int config_parse_strv(
342                 const char *filename,
343                 unsigned line,
344                 const char *section,
345                 const char *lvalue,
346                 const char *rvalue,
347                 void *data,
348                 void *userdata) {
349
350         char*** sv = data;
351         char **n;
352         char *w;
353         unsigned k;
354         size_t l;
355         char *state;
356
357         assert(filename);
358         assert(lvalue);
359         assert(rvalue);
360         assert(data);
361
362         k = strv_length(*sv);
363         FOREACH_WORD(w, &l, rvalue, state)
364                 k++;
365
366         if (!(n = new(char*, k+1)))
367                 return -ENOMEM;
368
369         for (k = 0; (*sv)[k]; k++)
370                 n[k] = (*sv)[k];
371         FOREACH_WORD(w, &l, rvalue, state)
372                 if (!(n[k++] = strndup(w, l)))
373                         goto fail;
374
375         n[k] = NULL;
376         free(*sv);
377         *sv = n;
378
379         return 0;
380
381 fail:
382         for (; k > 0; k--)
383                 free(n[k-1]);
384
385         return -ENOMEM;
386 }