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