]> git.ipfire.org Git - people/ms/systemd.git/blob - conf-parser.c
conf-parser: don't crash when environemnt list is empty
[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 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
375 FOREACH_WORD_QUOTED(w, l, rvalue, state)
376 if (!(n[k++] = strndup(w, l)))
377 goto fail;
378
379 n[k] = NULL;
380 free(*sv);
381 *sv = n;
382
383 return 0;
384
385 fail:
386 for (; k > 0; k--)
387 free(n[k-1]);
388 free(n);
389
390 return -ENOMEM;
391 }