]>
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 | 34 | #define COMMENTS "#;\n" |
1ea86b18 | 35 | #define NEWLINES "\n\r" |
ed5bcfbe LP |
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 | ||
f1857be0 LP |
67 | log_info("[%s:%u] Unknown lvalue '%s' in section '%s'. Ignoring.", filename, line, lvalue, strna(section)); |
68 | return 0; | |
ed5bcfbe LP |
69 | } |
70 | ||
ed5bcfbe | 71 | /* Parse a variable assignment line */ |
42f4e3c4 | 72 | static int parse_line(const char *filename, unsigned line, char **section, const char* const * sections, const ConfigItem *t, char *l, void *userdata) { |
b2aa81ef | 73 | char *e; |
ed5bcfbe | 74 | |
b2aa81ef | 75 | l = strstrip(l); |
ed5bcfbe | 76 | |
b2aa81ef | 77 | if (!*l) |
ed5bcfbe | 78 | return 0; |
1ea86b18 | 79 | |
b2aa81ef | 80 | if (strchr(COMMENTS, *l)) |
1ea86b18 | 81 | return 0; |
ed5bcfbe | 82 | |
b2aa81ef LP |
83 | if (startswith(l, ".include ")) { |
84 | char *fn; | |
ed5bcfbe LP |
85 | int r; |
86 | ||
b2aa81ef LP |
87 | if (!(fn = file_in_same_dir(filename, strstrip(l+9)))) |
88 | return -ENOMEM; | |
ed5bcfbe | 89 | |
87f0e418 | 90 | r = config_parse(fn, NULL, sections, t, userdata); |
b2aa81ef LP |
91 | free(fn); |
92 | ||
ed5bcfbe LP |
93 | return r; |
94 | } | |
95 | ||
b2aa81ef | 96 | if (*l == '[') { |
ed5bcfbe LP |
97 | size_t k; |
98 | char *n; | |
99 | ||
b2aa81ef | 100 | k = strlen(l); |
ed5bcfbe LP |
101 | assert(k > 0); |
102 | ||
b2aa81ef | 103 | if (l[k-1] != ']') { |
16354eff | 104 | log_error("[%s:%u] Invalid section header.", filename, line); |
ed5bcfbe LP |
105 | return -EBADMSG; |
106 | } | |
107 | ||
b2aa81ef | 108 | if (!(n = strndup(l+1, k-2))) |
ed5bcfbe LP |
109 | return -ENOMEM; |
110 | ||
b2aa81ef LP |
111 | if (sections && !strv_contains((char**) sections, n)) { |
112 | free(n); | |
113 | return -EBADMSG; | |
42f4e3c4 LP |
114 | } |
115 | ||
ed5bcfbe LP |
116 | free(*section); |
117 | *section = n; | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
b2aa81ef | 122 | if (!(e = strchr(l, '='))) { |
16354eff | 123 | log_error("[%s:%u] Missing '='.", filename, line); |
ed5bcfbe LP |
124 | return -EBADMSG; |
125 | } | |
126 | ||
127 | *e = 0; | |
128 | e++; | |
129 | ||
b2aa81ef | 130 | return next_assignment(filename, line, *section, t, strstrip(l), strstrip(e), userdata); |
ed5bcfbe LP |
131 | } |
132 | ||
133 | /* Go through the file and parse each line */ | |
87f0e418 | 134 | int config_parse(const char *filename, FILE *f, const char* const * sections, const ConfigItem *t, void *userdata) { |
ed5bcfbe LP |
135 | unsigned line = 0; |
136 | char *section = NULL; | |
ed5bcfbe LP |
137 | int r; |
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 | } | |
ed5bcfbe LP |
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; | |
16354eff | 158 | log_error("Failed to read configuration file '%s': %s", filename, strerror(-r)); |
ed5bcfbe LP |
159 | goto finish; |
160 | } | |
161 | ||
42f4e3c4 | 162 | if ((r = parse_line(filename, ++line, §ion, sections, t, l, userdata)) < 0) |
ed5bcfbe LP |
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) { | |
16354eff | 195 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
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) { | |
16354eff | 220 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
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) { | |
16354eff | 246 | log_error("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue); |
ed5bcfbe LP |
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) { | |
16354eff | 272 | log_error("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue); |
ed5bcfbe LP |
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 | } | |
57d42a5f | 308 | |
034c6ed7 LP |
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 | } | |
57d42a5f LP |
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); | |
034c6ed7 | 362 | FOREACH_WORD_QUOTED(w, l, rvalue, state) |
57d42a5f LP |
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]; | |
034c6ed7 | 370 | FOREACH_WORD_QUOTED(w, l, rvalue, state) |
57d42a5f LP |
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]); | |
034c6ed7 | 383 | free(n); |
57d42a5f LP |
384 | |
385 | return -ENOMEM; | |
386 | } |