]> git.ipfire.org Git - people/ms/systemd.git/blob - conf-parser.c
various cleanups
[people/ms/systemd.git] / conf-parser.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <errno.h>
6 #include <assert.h>
7 #include <stdlib.h>
8
9 #include "conf-parser.h"
10 #include "util.h"
11 #include "macro.h"
12 #include "strv.h"
13 #include "log.h"
14
15 #define COMMENTS "#;\n"
16 #define LINE_MAX 4096
17
18 /* Run the user supplied parser for an assignment */
19 static int next_assignment(
20 const char *filename,
21 unsigned line,
22 const char *section,
23 const ConfigItem *t,
24 const char *lvalue,
25 const char *rvalue,
26 void *userdata) {
27
28 assert(filename);
29 assert(t);
30 assert(lvalue);
31 assert(rvalue);
32
33 for (; t->parse; t++) {
34
35 if (t->lvalue && !streq(lvalue, t->lvalue))
36 continue;
37
38 if (t->section && !section)
39 continue;
40
41 if (t->section && !streq(section, t->section))
42 continue;
43
44 return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
45 }
46
47 log_error("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, strna(section));
48 return -EBADMSG;
49 }
50
51 /* Returns non-zero when c is contained in s */
52 static int in_string(char c, const char *s) {
53 assert(s);
54
55 for (; *s; s++)
56 if (*s == c)
57 return 1;
58
59 return 0;
60 }
61
62 /* Remove all whitepsapce from the beginning and the end of *s. *s may
63 * be modified. */
64 static char *strip(char *s) {
65 char *b = s+strspn(s, WHITESPACE);
66 char *e, *l = NULL;
67
68 for (e = b; *e; e++)
69 if (!in_string(*e, WHITESPACE))
70 l = e;
71
72 if (l)
73 *(l+1) = 0;
74
75 return b;
76 }
77
78 /* Parse a variable assignment line */
79 static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) {
80 char *e, *c, *b;
81
82 b = l+strspn(l, WHITESPACE);
83
84 if ((c = strpbrk(b, COMMENTS)))
85 *c = 0;
86
87 if (!*b)
88 return 0;
89
90 if (startswith(b, ".include ")) {
91 char *path = NULL, *fn;
92 int r;
93
94 fn = strip(b+9);
95 if (!is_path_absolute(fn)) {
96 const char *k;
97
98 if ((k = strrchr(filename, '/'))) {
99 char *dir;
100
101 if (!(dir = strndup(filename, k-filename)))
102 return -ENOMEM;
103
104 if (asprintf(&path, "%s/%s", dir, fn) < 0)
105 return -errno;
106
107 fn = path;
108 free(dir);
109 }
110 }
111
112 r = config_parse(fn, sections, t, userdata);
113 free(path);
114 return r;
115 }
116
117 if (*b == '[') {
118 size_t k;
119 char *n;
120
121 k = strlen(b);
122 assert(k > 0);
123
124 if (b[k-1] != ']') {
125 log_error("[%s:%u] Invalid section header.", filename, line);
126 return -EBADMSG;
127 }
128
129 if (!(n = strndup(b+1, k-2)))
130 return -ENOMEM;
131
132 if (sections) {
133 const char * const * i;
134 bool good = false;
135 STRV_FOREACH(i, sections)
136 if (streq(*i, n)) {
137 good = true;
138 break;
139 }
140
141 if (!good) {
142 free(n);
143 return -EBADMSG;
144 }
145 }
146
147 free(*section);
148 *section = n;
149
150 return 0;
151 }
152
153 if (!(e = strchr(b, '='))) {
154 log_error("[%s:%u] Missing '='.", filename, line);
155 return -EBADMSG;
156 }
157
158 *e = 0;
159 e++;
160
161 return next_assignment(filename, line, *section, t, strip(b), strip(e), userdata);
162 }
163
164 /* Go through the file and parse each line */
165 int config_parse(const char *filename, const char* const * sections, const ConfigItem *t, void *userdata) {
166 unsigned line = 0;
167 char *section = NULL;
168 FILE *f;
169 int r;
170
171 assert(filename);
172 assert(t);
173
174 if (!(f = fopen(filename, "re"))) {
175 r = -errno;
176 log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
177 goto finish;
178 }
179
180 while (!feof(f)) {
181 char l[LINE_MAX];
182
183 if (!fgets(l, sizeof(l), f)) {
184 if (feof(f))
185 break;
186
187 r = -errno;
188 log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
189 goto finish;
190 }
191
192 if ((r = parse_line(filename, ++line, &section, sections, t, l, userdata)) < 0)
193 goto finish;
194 }
195
196 r = 0;
197
198 finish:
199 free(section);
200
201 if (f)
202 fclose(f);
203
204 return r;
205 }
206
207 int config_parse_int(
208 const char *filename,
209 unsigned line,
210 const char *section,
211 const char *lvalue,
212 const char *rvalue,
213 void *data,
214 void *userdata) {
215
216 int *i = data;
217 int r;
218
219 assert(filename);
220 assert(lvalue);
221 assert(rvalue);
222 assert(data);
223
224 if ((r = safe_atoi(rvalue, i)) < 0) {
225 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
226 return r;
227 }
228
229 return 0;
230 }
231
232 int config_parse_unsigned(
233 const char *filename,
234 unsigned line,
235 const char *section,
236 const char *lvalue,
237 const char *rvalue,
238 void *data,
239 void *userdata) {
240
241 unsigned *u = data;
242 int r;
243
244 assert(filename);
245 assert(lvalue);
246 assert(rvalue);
247 assert(data);
248
249 if ((r = safe_atou(rvalue, u)) < 0) {
250 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
251 return r;
252 }
253
254 return 0;
255 }
256
257 int config_parse_size(
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 size_t *sz = data;
267 unsigned u;
268 int r;
269
270 assert(filename);
271 assert(lvalue);
272 assert(rvalue);
273 assert(data);
274
275 if ((r = safe_atou(rvalue, &u)) < 0) {
276 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
277 return r;
278 }
279
280 *sz = (size_t) u;
281 return 0;
282 }
283
284 int config_parse_bool(
285 const char *filename,
286 unsigned line,
287 const char *section,
288 const char *lvalue,
289 const char *rvalue,
290 void *data,
291 void *userdata) {
292
293 int k;
294 bool *b = data;
295
296 assert(filename);
297 assert(lvalue);
298 assert(rvalue);
299 assert(data);
300
301 if ((k = parse_boolean(rvalue)) < 0) {
302 log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
303 return k;
304 }
305
306 *b = !!k;
307 return 0;
308 }
309
310 int config_parse_string(
311 const char *filename,
312 unsigned line,
313 const char *section,
314 const char *lvalue,
315 const char *rvalue,
316 void *data,
317 void *userdata) {
318
319 char **s = data;
320 char *n;
321
322 assert(filename);
323 assert(lvalue);
324 assert(rvalue);
325 assert(data);
326
327 if (*rvalue) {
328 if (!(n = strdup(rvalue)))
329 return -ENOMEM;
330 } else
331 n = NULL;
332
333 free(*s);
334 *s = n;
335
336 return 0;
337 }
338
339 int config_parse_path(
340 const char *filename,
341 unsigned line,
342 const char *section,
343 const char *lvalue,
344 const char *rvalue,
345 void *data,
346 void *userdata) {
347
348 char **s = data;
349 char *n;
350
351 assert(filename);
352 assert(lvalue);
353 assert(rvalue);
354 assert(data);
355
356 if (*rvalue != '/') {
357 log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
358 return -EINVAL;
359 }
360
361 if (!(n = strdup(rvalue)))
362 return -ENOMEM;
363
364 free(*s);
365 *s = n;
366
367 return 0;
368 }
369
370 int config_parse_strv(
371 const char *filename,
372 unsigned line,
373 const char *section,
374 const char *lvalue,
375 const char *rvalue,
376 void *data,
377 void *userdata) {
378
379 char*** sv = data;
380 char **n;
381 char *w;
382 unsigned k;
383 size_t l;
384 char *state;
385
386 assert(filename);
387 assert(lvalue);
388 assert(rvalue);
389 assert(data);
390
391 k = strv_length(*sv);
392 FOREACH_WORD_QUOTED(w, l, rvalue, state)
393 k++;
394
395 if (!(n = new(char*, k+1)))
396 return -ENOMEM;
397
398 for (k = 0; (*sv)[k]; k++)
399 n[k] = (*sv)[k];
400 FOREACH_WORD_QUOTED(w, l, rvalue, state)
401 if (!(n[k++] = strndup(w, l)))
402 goto fail;
403
404 n[k] = NULL;
405 free(*sv);
406 *sv = n;
407
408 return 0;
409
410 fail:
411 for (; k > 0; k--)
412 free(n[k-1]);
413 free(n);
414
415 return -ENOMEM;
416 }