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