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