chiark / gitweb /
manager: properly parse finish timestamp
[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, 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                 const char *rvalue,
230                 void *data,
231                 void *userdata) {
232
233         int *i = data;
234         int r;
235
236         assert(filename);
237         assert(lvalue);
238         assert(rvalue);
239         assert(data);
240
241         if ((r = safe_atoi(rvalue, i)) < 0) {
242                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
243                 return r;
244         }
245
246         return 0;
247 }
248
249 int config_parse_unsigned(
250                 const char *filename,
251                 unsigned line,
252                 const char *section,
253                 const char *lvalue,
254                 const char *rvalue,
255                 void *data,
256                 void *userdata) {
257
258         unsigned *u = data;
259         int r;
260
261         assert(filename);
262         assert(lvalue);
263         assert(rvalue);
264         assert(data);
265
266         if ((r = safe_atou(rvalue, u)) < 0) {
267                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
268                 return r;
269         }
270
271         return 0;
272 }
273
274 int config_parse_size(
275                 const char *filename,
276                 unsigned line,
277                 const char *section,
278                 const char *lvalue,
279                 const char *rvalue,
280                 void *data,
281                 void *userdata) {
282
283         size_t *sz = data;
284         unsigned u;
285         int r;
286
287         assert(filename);
288         assert(lvalue);
289         assert(rvalue);
290         assert(data);
291
292         if ((r = safe_atou(rvalue, &u)) < 0) {
293                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
294                 return r;
295         }
296
297         *sz = (size_t) u;
298         return 0;
299 }
300
301 int config_parse_bool(
302                 const char *filename,
303                 unsigned line,
304                 const char *section,
305                 const char *lvalue,
306                 const char *rvalue,
307                 void *data,
308                 void *userdata) {
309
310         int k;
311         bool *b = data;
312
313         assert(filename);
314         assert(lvalue);
315         assert(rvalue);
316         assert(data);
317
318         if ((k = parse_boolean(rvalue)) < 0) {
319                 log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
320                 return k;
321         }
322
323         *b = !!k;
324         return 0;
325 }
326
327 int config_parse_string(
328                 const char *filename,
329                 unsigned line,
330                 const char *section,
331                 const char *lvalue,
332                 const char *rvalue,
333                 void *data,
334                 void *userdata) {
335
336         char **s = data;
337         char *n;
338
339         assert(filename);
340         assert(lvalue);
341         assert(rvalue);
342         assert(data);
343
344         if (*rvalue) {
345                 if (!(n = strdup(rvalue)))
346                         return -ENOMEM;
347         } else
348                 n = NULL;
349
350         free(*s);
351         *s = n;
352
353         return 0;
354 }
355
356 int config_parse_path(
357                 const char *filename,
358                 unsigned line,
359                 const char *section,
360                 const char *lvalue,
361                 const char *rvalue,
362                 void *data,
363                 void *userdata) {
364
365         char **s = data;
366         char *n;
367
368         assert(filename);
369         assert(lvalue);
370         assert(rvalue);
371         assert(data);
372
373         if (!path_is_absolute(rvalue)) {
374                 log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
375                 return -EINVAL;
376         }
377
378         if (!(n = strdup(rvalue)))
379                 return -ENOMEM;
380
381         path_kill_slashes(n);
382
383         free(*s);
384         *s = n;
385
386         return 0;
387 }
388
389 int config_parse_strv(
390                 const char *filename,
391                 unsigned line,
392                 const char *section,
393                 const char *lvalue,
394                 const char *rvalue,
395                 void *data,
396                 void *userdata) {
397
398         char*** sv = data;
399         char **n;
400         char *w;
401         unsigned k;
402         size_t l;
403         char *state;
404
405         assert(filename);
406         assert(lvalue);
407         assert(rvalue);
408         assert(data);
409
410         k = strv_length(*sv);
411         FOREACH_WORD_QUOTED(w, l, rvalue, state)
412                 k++;
413
414         if (!(n = new(char*, k+1)))
415                 return -ENOMEM;
416
417         if (*sv)
418                 for (k = 0; (*sv)[k]; k++)
419                         n[k] = (*sv)[k];
420         else
421                 k = 0;
422
423         FOREACH_WORD_QUOTED(w, l, rvalue, state)
424                 if (!(n[k++] = cunescape_length(w, l)))
425                         goto fail;
426
427         n[k] = NULL;
428         free(*sv);
429         *sv = n;
430
431         return 0;
432
433 fail:
434         for (; k > 0; k--)
435                 free(n[k-1]);
436         free(n);
437
438         return -ENOMEM;
439 }
440
441 int config_parse_path_strv(
442                 const char *filename,
443                 unsigned line,
444                 const char *section,
445                 const char *lvalue,
446                 const char *rvalue,
447                 void *data,
448                 void *userdata) {
449
450         char*** sv = data;
451         char **n;
452         char *w;
453         unsigned k;
454         size_t l;
455         char *state;
456         int r;
457
458         assert(filename);
459         assert(lvalue);
460         assert(rvalue);
461         assert(data);
462
463         k = strv_length(*sv);
464         FOREACH_WORD_QUOTED(w, l, rvalue, state)
465                 k++;
466
467         if (!(n = new(char*, k+1)))
468                 return -ENOMEM;
469
470         k = 0;
471         if (*sv)
472                 for (; (*sv)[k]; k++)
473                         n[k] = (*sv)[k];
474
475         FOREACH_WORD_QUOTED(w, l, rvalue, state) {
476                 if (!(n[k] = cunescape_length(w, l))) {
477                         r = -ENOMEM;
478                         goto fail;
479                 }
480
481                 if (!path_is_absolute(n[k])) {
482                         log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
483                         r = -EINVAL;
484                         goto fail;
485                 }
486
487                 path_kill_slashes(n[k]);
488
489                 k++;
490         }
491
492         n[k] = NULL;
493         free(*sv);
494         *sv = n;
495
496         return 0;
497
498 fail:
499         free(n[k]);
500         for (; k > 0; k--)
501                 free(n[k-1]);
502         free(n);
503
504         return r;
505 }