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