]> git.ipfire.org Git - people/ms/systemd.git/blob - conf-parser.c
license: add GPLv2+ license blurbs everwhere
[people/ms/systemd.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
139 assert(filename);
140 assert(t);
141
142 if (!f) {
143 if (!(f = fopen(filename, "re"))) {
144 r = -errno;
145 log_error("Failed to open configuration file '%s': %s", filename, strerror(-r));
146 goto finish;
147 }
148 }
149
150 while (!feof(f)) {
151 char l[LINE_MAX];
152
153 if (!fgets(l, sizeof(l), f)) {
154 if (feof(f))
155 break;
156
157 r = -errno;
158 log_error("Failed to read configuration file '%s': %s", filename, strerror(-r));
159 goto finish;
160 }
161
162 if ((r = parse_line(filename, ++line, &section, sections, t, l, userdata)) < 0)
163 goto finish;
164 }
165
166 r = 0;
167
168 finish:
169 free(section);
170
171 if (f)
172 fclose(f);
173
174 return r;
175 }
176
177 int config_parse_int(
178 const char *filename,
179 unsigned line,
180 const char *section,
181 const char *lvalue,
182 const char *rvalue,
183 void *data,
184 void *userdata) {
185
186 int *i = data;
187 int r;
188
189 assert(filename);
190 assert(lvalue);
191 assert(rvalue);
192 assert(data);
193
194 if ((r = safe_atoi(rvalue, i)) < 0) {
195 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
196 return r;
197 }
198
199 return 0;
200 }
201
202 int config_parse_unsigned(
203 const char *filename,
204 unsigned line,
205 const char *section,
206 const char *lvalue,
207 const char *rvalue,
208 void *data,
209 void *userdata) {
210
211 unsigned *u = data;
212 int r;
213
214 assert(filename);
215 assert(lvalue);
216 assert(rvalue);
217 assert(data);
218
219 if ((r = safe_atou(rvalue, u)) < 0) {
220 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
221 return r;
222 }
223
224 return 0;
225 }
226
227 int config_parse_size(
228 const char *filename,
229 unsigned line,
230 const char *section,
231 const char *lvalue,
232 const char *rvalue,
233 void *data,
234 void *userdata) {
235
236 size_t *sz = data;
237 unsigned u;
238 int r;
239
240 assert(filename);
241 assert(lvalue);
242 assert(rvalue);
243 assert(data);
244
245 if ((r = safe_atou(rvalue, &u)) < 0) {
246 log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
247 return r;
248 }
249
250 *sz = (size_t) u;
251 return 0;
252 }
253
254 int config_parse_bool(
255 const char *filename,
256 unsigned line,
257 const char *section,
258 const char *lvalue,
259 const char *rvalue,
260 void *data,
261 void *userdata) {
262
263 int k;
264 bool *b = data;
265
266 assert(filename);
267 assert(lvalue);
268 assert(rvalue);
269 assert(data);
270
271 if ((k = parse_boolean(rvalue)) < 0) {
272 log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
273 return k;
274 }
275
276 *b = !!k;
277 return 0;
278 }
279
280 int config_parse_string(
281 const char *filename,
282 unsigned line,
283 const char *section,
284 const char *lvalue,
285 const char *rvalue,
286 void *data,
287 void *userdata) {
288
289 char **s = data;
290 char *n;
291
292 assert(filename);
293 assert(lvalue);
294 assert(rvalue);
295 assert(data);
296
297 if (*rvalue) {
298 if (!(n = strdup(rvalue)))
299 return -ENOMEM;
300 } else
301 n = NULL;
302
303 free(*s);
304 *s = n;
305
306 return 0;
307 }
308
309 int config_parse_path(
310 const char *filename,
311 unsigned line,
312 const char *section,
313 const char *lvalue,
314 const char *rvalue,
315 void *data,
316 void *userdata) {
317
318 char **s = data;
319 char *n;
320
321 assert(filename);
322 assert(lvalue);
323 assert(rvalue);
324 assert(data);
325
326 if (*rvalue != '/') {
327 log_error("[%s:%u] Not an absolute path: %s", filename, line, rvalue);
328 return -EINVAL;
329 }
330
331 if (!(n = strdup(rvalue)))
332 return -ENOMEM;
333
334 free(*s);
335 *s = n;
336
337 return 0;
338 }
339
340 int config_parse_strv(
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*** sv = data;
350 char **n;
351 char *w;
352 unsigned k;
353 size_t l;
354 char *state;
355
356 assert(filename);
357 assert(lvalue);
358 assert(rvalue);
359 assert(data);
360
361 k = strv_length(*sv);
362 FOREACH_WORD_QUOTED(w, l, rvalue, state)
363 k++;
364
365 if (!(n = new(char*, k+1)))
366 return -ENOMEM;
367
368 for (k = 0; (*sv)[k]; k++)
369 n[k] = (*sv)[k];
370 FOREACH_WORD_QUOTED(w, l, rvalue, state)
371 if (!(n[k++] = strndup(w, l)))
372 goto fail;
373
374 n[k] = NULL;
375 free(*sv);
376 *sv = n;
377
378 return 0;
379
380 fail:
381 for (; k > 0; k--)
382 free(n[k-1]);
383 free(n);
384
385 return -ENOMEM;
386 }