chiark / gitweb /
unit: automatically connect to syslog when it becomes available
[elogind.git] / 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 NEWLINES "\n\r"
36 #define LINE_MAX 4096
37
38 /* Run the user supplied parser for an assignment */
39 static int next_assignment(
40                 const char *filename,
41                 unsigned line,
42                 const char *section,
43                 const ConfigItem *t,
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                 return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
65         }
66
67         log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section));
68         return 0;
69 }
70
71 /* Parse a variable assignment line */
72 static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) {
73         char *e;
74
75         l = strstrip(l);
76
77         if (!*l)
78                 return 0;
79
80         if (strchr(COMMENTS, *l))
81                 return 0;
82
83         if (startswith(l, ".include ")) {
84                 char *fn;
85                 int r;
86
87                 if (!(fn = file_in_same_dir(filename, strstrip(l+9))))
88                         return -ENOMEM;
89
90                 r = config_parse(fn, NULL, sections, t, userdata);
91                 free(fn);
92
93                 return r;
94         }
95
96         if (*l == '[') {
97                 size_t k;
98                 char *n;
99
100                 k = strlen(l);
101                 assert(k > 0);
102
103                 if (l[k-1] != ']') {
104                         log_error("[%s:%u] Invalid section header.", filename, line);
105                         return -EBADMSG;
106                 }
107
108                 if (!(n = strndup(l+1, k-2)))
109                         return -ENOMEM;
110
111                 if (sections && !strv_contains((char**) sections, n)) {
112                         free(n);
113                         return -EBADMSG;
114                 }
115
116                 free(*section);
117                 *section = n;
118
119                 return 0;
120         }
121
122         if (!(e = strchr(l, '='))) {
123                 log_error("[%s:%u] Missing '='.", filename, line);
124                 return -EBADMSG;
125         }
126
127         *e = 0;
128         e++;
129
130         return next_assignment(filename, line, *section, t, strstrip(l), strstrip(e), userdata);
131 }
132
133 /* Go through the file and parse each line */
134 int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) {
135         unsigned line = 0;
136         char *section = NULL;
137         int r;
138         bool ours = false;
139
140         assert(filename);
141         assert(t);
142
143         if (!f) {
144                 if (!(f = fopen(filename, "re"))) {
145                         r = -errno;
146                         log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
147                         goto finish;
148                 }
149
150                 ours = true;
151         }
152
153         while (!feof(f)) {
154                 char l[LINE_MAX];
155
156                 if (!fgets(l, sizeof(l), f)) {
157                         if (feof(f))
158                                 break;
159
160                         r = -errno;
161                         log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
162                         goto finish;
163                 }
164
165                 if ((r = parse_line(filename, ++line, &section, sections, t, l, userdata)) < 0)
166                         goto finish;
167         }
168
169         r = 0;
170
171 finish:
172         free(section);
173
174         if (f && ours)
175                 fclose(f);
176
177         return r;
178 }
179
180 int config_parse_int(
181                 const char *filename,
182                 unsigned line,
183                 const char *section,
184                 const char *lvalue,
185                 const char *rvalue,
186                 void *data,
187                 void *userdata) {
188
189         int *i = data;
190         int r;
191
192         assert(filename);
193         assert(lvalue);
194         assert(rvalue);
195         assert(data);
196
197         if ((r = safe_atoi(rvalue, i)) < 0) {
198                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
199                 return r;
200         }
201
202         return 0;
203 }
204
205 int config_parse_unsigned(
206                 const char *filename,
207                 unsigned line,
208                 const char *section,
209                 const char *lvalue,
210                 const char *rvalue,
211                 void *data,
212                 void *userdata) {
213
214         unsigned *u = data;
215         int r;
216
217         assert(filename);
218         assert(lvalue);
219         assert(rvalue);
220         assert(data);
221
222         if ((r = safe_atou(rvalue, u)) < 0) {
223                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
224                 return r;
225         }
226
227         return 0;
228 }
229
230 int config_parse_size(
231                 const char *filename,
232                 unsigned line,
233                 const char *section,
234                 const char *lvalue,
235                 const char *rvalue,
236                 void *data,
237                 void *userdata) {
238
239         size_t *sz = data;
240         unsigned u;
241         int r;
242
243         assert(filename);
244         assert(lvalue);
245         assert(rvalue);
246         assert(data);
247
248         if ((r = safe_atou(rvalue, &u)) < 0) {
249                 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
250                 return r;
251         }
252
253         *sz = (size_t) u;
254         return 0;
255 }
256
257 int config_parse_bool(
258                 const char *filename,
259                 unsigned line,
260                 const char *section,
261                 const char *lvalue,
262                 const char *rvalue,
263                 void *data,
264                 void *userdata) {
265
266         int k;
267         bool *b = data;
268
269         assert(filename);
270         assert(lvalue);
271         assert(rvalue);
272         assert(data);
273
274         if ((k = parse_boolean(rvalue)) < 0) {
275                 log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
276                 return k;
277         }
278
279         *b = !!k;
280         return 0;
281 }
282
283 int config_parse_string(
284                 const char *filename,
285                 unsigned line,
286                 const char *section,
287                 const char *lvalue,
288                 const char *rvalue,
289                 void *data,
290                 void *userdata) {
291
292         char **s = data;
293         char *n;
294
295         assert(filename);
296         assert(lvalue);
297         assert(rvalue);
298         assert(data);
299
300         if (*rvalue) {
301                 if (!(n = strdup(rvalue)))
302                         return -ENOMEM;
303         } else
304                 n = NULL;
305
306         free(*s);
307         *s = n;
308
309         return 0;
310 }
311
312 int config_parse_path(
313                 const char *filename,
314                 unsigned line,
315                 const char *section,
316                 const char *lvalue,
317                 const char *rvalue,
318                 void *data,
319                 void *userdata) {
320
321         char **s = data;
322         char *n;
323
324         assert(filename);
325         assert(lvalue);
326         assert(rvalue);
327         assert(data);
328
329         if (*rvalue != '/') {
330                 log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
331                 return -EINVAL;
332         }
333
334         if (!(n = strdup(rvalue)))
335                 return -ENOMEM;
336
337         free(*s);
338         *s = n;
339
340         return 0;
341 }
342
343 int config_parse_strv(
344                 const char *filename,
345                 unsigned line,
346                 const char *section,
347                 const char *lvalue,
348                 const char *rvalue,
349                 void *data,
350                 void *userdata) {
351
352         char*** sv = data;
353         char **n;
354         char *w;
355         unsigned k;
356         size_t l;
357         char *state;
358
359         assert(filename);
360         assert(lvalue);
361         assert(rvalue);
362         assert(data);
363
364         k = strv_length(*sv);
365         FOREACH_WORD_QUOTED(w, l, rvalue, state)
366                 k++;
367
368         if (!(n = new(char*, k+1)))
369                 return -ENOMEM;
370
371         if (*sv)
372                 for (k = 0; (*sv)[k]; k++)
373                         n[k] = (*sv)[k];
374         else
375                 k = 0;
376
377         FOREACH_WORD_QUOTED(w, l, rvalue, state)
378                 if (!(n[k++] = strndup(w, l)))
379                         goto fail;
380
381         n[k] = NULL;
382         free(*sv);
383         *sv = n;
384
385         return 0;
386
387 fail:
388         for (; k > 0; k--)
389                 free(n[k-1]);
390         free(n);
391
392         return -ENOMEM;
393 }