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