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