]>
Commit | Line | Data |
---|---|---|
e59e2f7c PR |
1 | /* |
2 | * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. | |
3 | * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. | |
4 | * | |
5 | * This file is part of LVM2. | |
6 | * | |
7 | * This copyrighted material is made available to anyone wishing to use, | |
8 | * modify, copy, or redistribute it subject to the terms and conditions | |
9 | * of the GNU Lesser General Public License v.2.1. | |
10 | * | |
11 | * You should have received a copy of the GNU Lesser General Public License | |
12 | * along with this program; if not, write to the Free Software Foundation, | |
fcbef05a | 13 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
e59e2f7c PR |
14 | */ |
15 | ||
7f97c7ea | 16 | #include "libdm/misc/dmlib.h" |
e59e2f7c PR |
17 | |
18 | #include <sys/stat.h> | |
19 | #include <sys/mman.h> | |
20 | #include <unistd.h> | |
21 | #include <fcntl.h> | |
22 | #include <ctype.h> | |
589983a2 | 23 | #include <stdarg.h> |
e59e2f7c PR |
24 | |
25 | #define SECTION_B_CHAR '{' | |
26 | #define SECTION_E_CHAR '}' | |
27 | ||
28 | enum { | |
29 | TOK_INT, | |
30 | TOK_FLOAT, | |
31 | TOK_STRING, /* Single quotes */ | |
32 | TOK_STRING_ESCAPED, /* Double quotes */ | |
8bc99667 | 33 | TOK_STRING_BARE, /* No quotes */ |
e59e2f7c PR |
34 | TOK_EQ, |
35 | TOK_SECTION_B, | |
36 | TOK_SECTION_E, | |
37 | TOK_ARRAY_B, | |
38 | TOK_ARRAY_E, | |
39 | TOK_IDENTIFIER, | |
40 | TOK_COMMA, | |
41 | TOK_EOF | |
42 | }; | |
43 | ||
44 | struct parser { | |
45 | const char *fb, *fe; /* file limits */ | |
46 | ||
47 | int t; /* token limits and type */ | |
48 | const char *tb, *te; | |
49 | ||
50 | int line; /* line number we are on */ | |
51 | ||
52 | struct dm_pool *mem; | |
7563e69c | 53 | int no_dup_node_check; /* whether to disable dup node checking */ |
85dbcda1 ZK |
54 | const char *key; /* last obtained key */ |
55 | unsigned ignored_creation_time; | |
e59e2f7c PR |
56 | }; |
57 | ||
e29cd366 | 58 | struct config_output { |
e59e2f7c PR |
59 | struct dm_pool *mem; |
60 | dm_putline_fn putline; | |
e29cd366 PR |
61 | const struct dm_config_node_out_spec *spec; |
62 | void *baton; | |
e59e2f7c PR |
63 | }; |
64 | ||
65 | static void _get_token(struct parser *p, int tok_prev); | |
66 | static void _eat_space(struct parser *p); | |
67 | static struct dm_config_node *_file(struct parser *p); | |
687029cb | 68 | static struct dm_config_node *_section(struct parser *p, struct dm_config_node *parent); |
e59e2f7c PR |
69 | static struct dm_config_value *_value(struct parser *p); |
70 | static struct dm_config_value *_type(struct parser *p); | |
71 | static int _match_aux(struct parser *p, int t); | |
72 | static struct dm_config_value *_create_value(struct dm_pool *mem); | |
73 | static struct dm_config_node *_create_node(struct dm_pool *mem); | |
74 | static char *_dup_tok(struct parser *p); | |
687029cb | 75 | static char *_dup_token(struct dm_pool *mem, const char *b, const char *e); |
e59e2f7c | 76 | |
e59e2f7c PR |
77 | #define MAX_INDENT 32 |
78 | ||
79 | #define match(t) do {\ | |
80 | if (!_match_aux(p, (t))) {\ | |
81 | log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \ | |
82 | p->tb - p->fb + 1, p->line); \ | |
83 | return 0;\ | |
84 | } \ | |
55c9286d | 85 | } while(0) |
e59e2f7c PR |
86 | |
87 | static int _tok_match(const char *str, const char *b, const char *e) | |
88 | { | |
89 | while (*str && (b != e)) { | |
90 | if (*str++ != *b++) | |
91 | return 0; | |
92 | } | |
93 | ||
94 | return !(*str || (b != e)); | |
95 | } | |
96 | ||
e38b557a | 97 | struct dm_config_tree *dm_config_create(void) |
e59e2f7c | 98 | { |
845b1df6 | 99 | struct dm_config_tree *cft; |
e59e2f7c PR |
100 | struct dm_pool *mem = dm_pool_create("config", 10 * 1024); |
101 | ||
102 | if (!mem) { | |
103 | log_error("Failed to allocate config pool."); | |
104 | return 0; | |
105 | } | |
106 | ||
845b1df6 | 107 | if (!(cft = dm_pool_zalloc(mem, sizeof(*cft)))) { |
e59e2f7c PR |
108 | log_error("Failed to allocate config tree."); |
109 | dm_pool_destroy(mem); | |
110 | return 0; | |
111 | } | |
845b1df6 | 112 | cft->mem = mem; |
35ab841e | 113 | |
845b1df6 | 114 | return cft; |
e59e2f7c PR |
115 | } |
116 | ||
117 | void dm_config_set_custom(struct dm_config_tree *cft, void *custom) | |
118 | { | |
845b1df6 | 119 | cft->custom = custom; |
e59e2f7c PR |
120 | } |
121 | ||
122 | void *dm_config_get_custom(struct dm_config_tree *cft) | |
123 | { | |
845b1df6 | 124 | return cft->custom; |
e59e2f7c PR |
125 | } |
126 | ||
127 | void dm_config_destroy(struct dm_config_tree *cft) | |
128 | { | |
845b1df6 | 129 | dm_pool_destroy(cft->mem); |
e59e2f7c PR |
130 | } |
131 | ||
c82c2beb AK |
132 | /* |
133 | * If there's a cascaded dm_config_tree, remove and return it, otherwise | |
134 | * return NULL. | |
135 | */ | |
136 | struct dm_config_tree *dm_config_remove_cascaded_tree(struct dm_config_tree *cft) | |
137 | { | |
5a6ae6f5 | 138 | struct dm_config_tree *second_cft; |
c82c2beb | 139 | |
5a6ae6f5 ZK |
140 | if (!cft) |
141 | return NULL; | |
142 | ||
143 | second_cft = cft->cascade; | |
c82c2beb AK |
144 | cft->cascade = NULL; |
145 | ||
146 | return second_cft; | |
147 | } | |
148 | ||
149 | /* | |
150 | * When searching, first_cft is checked before second_cft. | |
151 | */ | |
152 | struct dm_config_tree *dm_config_insert_cascaded_tree(struct dm_config_tree *first_cft, struct dm_config_tree *second_cft) | |
153 | { | |
154 | first_cft->cascade = second_cft; | |
155 | ||
156 | return first_cft; | |
157 | } | |
158 | ||
956c1928 PR |
159 | static struct dm_config_node *_config_reverse(struct dm_config_node *head) |
160 | { | |
956c1928 PR |
161 | struct dm_config_node *left = head, *middle = NULL, *right = NULL; |
162 | ||
39cffa4e | 163 | while (left) { |
956c1928 PR |
164 | right = middle; |
165 | middle = left; | |
166 | left = left->sib; | |
167 | middle->sib = right; | |
168 | middle->child = _config_reverse(middle->child); | |
39cffa4e | 169 | } |
956c1928 PR |
170 | |
171 | return middle; | |
39cffa4e | 172 | } |
956c1928 | 173 | |
7563e69c | 174 | static int _do_dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end, int no_dup_node_check) |
e59e2f7c PR |
175 | { |
176 | /* TODO? if (start == end) return 1; */ | |
177 | ||
93115ef9 ZK |
178 | struct parser p = { |
179 | .mem = cft->mem, | |
180 | .tb = start, | |
181 | .te = start, | |
182 | .fb = start, | |
183 | .fe = end, | |
184 | .line = 1, | |
185 | .no_dup_node_check = no_dup_node_check | |
186 | }; | |
187 | ||
188 | _get_token(&p, TOK_SECTION_E); | |
189 | if (!(cft->root = _file(&p))) | |
e59e2f7c PR |
190 | return_0; |
191 | ||
956c1928 PR |
192 | cft->root = _config_reverse(cft->root); |
193 | ||
e59e2f7c PR |
194 | return 1; |
195 | } | |
196 | ||
7563e69c PR |
197 | int dm_config_parse(struct dm_config_tree *cft, const char *start, const char *end) |
198 | { | |
199 | return _do_dm_config_parse(cft, start, end, 0); | |
200 | } | |
201 | ||
202 | int dm_config_parse_without_dup_node_check(struct dm_config_tree *cft, const char *start, const char *end) | |
203 | { | |
204 | return _do_dm_config_parse(cft, start, end, 1); | |
205 | } | |
206 | ||
e59e2f7c PR |
207 | struct dm_config_tree *dm_config_from_string(const char *config_settings) |
208 | { | |
209 | struct dm_config_tree *cft; | |
210 | ||
845b1df6 | 211 | if (!(cft = dm_config_create())) |
e59e2f7c PR |
212 | return_NULL; |
213 | ||
214 | if (!dm_config_parse(cft, config_settings, config_settings + strlen(config_settings))) { | |
215 | dm_config_destroy(cft); | |
216 | return_NULL; | |
217 | } | |
218 | ||
219 | return cft; | |
220 | } | |
221 | ||
e29cd366 | 222 | static int _line_start(struct config_output *out) |
e59e2f7c | 223 | { |
e29cd366 | 224 | if (!dm_pool_begin_object(out->mem, 128)) { |
e59e2f7c PR |
225 | log_error("dm_pool_begin_object failed for config line"); |
226 | return 0; | |
227 | } | |
228 | ||
229 | return 1; | |
230 | } | |
231 | ||
347e1afd | 232 | __attribute__ ((format(printf, 2, 3))) |
e29cd366 | 233 | static int _line_append(struct config_output *out, const char *fmt, ...) |
e59e2f7c PR |
234 | { |
235 | char buf[4096]; | |
67c5006e | 236 | char *dyn_buf = NULL; |
e59e2f7c PR |
237 | va_list ap; |
238 | int n; | |
239 | ||
967a0889 PR |
240 | /* |
241 | * We should be fine with the 4096 char buffer 99% of the time, | |
242 | * but if we need to go beyond that, allocate the buffer dynamically. | |
243 | */ | |
244 | ||
e59e2f7c | 245 | va_start(ap, fmt); |
7239a45b | 246 | n = vsnprintf(buf, sizeof(buf), fmt, ap); |
e59e2f7c PR |
247 | va_end(ap); |
248 | ||
967a0889 | 249 | if (n < 0) { |
e59e2f7c PR |
250 | log_error("vsnprintf failed for config line"); |
251 | return 0; | |
252 | } | |
253 | ||
967a0889 PR |
254 | if (n > (int) sizeof buf - 1) { |
255 | /* | |
256 | * Fixed size buffer with sizeof buf is not enough, | |
257 | * so try dynamically allocated buffer now... | |
258 | */ | |
259 | va_start(ap, fmt); | |
67c5006e | 260 | n = dm_vasprintf(&dyn_buf, fmt, ap); |
967a0889 PR |
261 | va_end(ap); |
262 | ||
263 | if (n < 0) { | |
264 | log_error("dm_vasprintf failed for config line"); | |
265 | return 0; | |
266 | } | |
67c5006e | 267 | } |
967a0889 | 268 | |
67c5006e | 269 | if (!dm_pool_grow_object(out->mem, dyn_buf ? : buf, 0)) { |
e59e2f7c | 270 | log_error("dm_pool_grow_object failed for config line"); |
67c5006e | 271 | dm_free(dyn_buf); |
e59e2f7c PR |
272 | return 0; |
273 | } | |
274 | ||
67c5006e PR |
275 | dm_free(dyn_buf); |
276 | ||
e59e2f7c PR |
277 | return 1; |
278 | } | |
279 | ||
e29cd366 | 280 | #define line_append(args...) do {if (!_line_append(out, args)) {return_0;}} while (0) |
e59e2f7c | 281 | |
e29cd366 | 282 | static int _line_end(const struct dm_config_node *cn, struct config_output *out) |
e59e2f7c PR |
283 | { |
284 | const char *line; | |
285 | ||
e29cd366 | 286 | if (!dm_pool_grow_object(out->mem, "\0", 1)) { |
e59e2f7c PR |
287 | log_error("dm_pool_grow_object failed for config line"); |
288 | return 0; | |
289 | } | |
290 | ||
e29cd366 | 291 | line = dm_pool_end_object(out->mem); |
11e52025 | 292 | |
e29cd366 | 293 | if (!out->putline && !out->spec) |
11e52025 PR |
294 | return 0; |
295 | ||
e29cd366 PR |
296 | if (out->putline) |
297 | out->putline(line, out->baton); | |
298 | ||
299 | if (out->spec && out->spec->line_fn) | |
300 | out->spec->line_fn(cn, line, out->baton); | |
e59e2f7c PR |
301 | |
302 | return 1; | |
303 | } | |
304 | ||
e29cd366 | 305 | static int _write_value(struct config_output *out, const struct dm_config_value *v) |
e59e2f7c PR |
306 | { |
307 | char *buf; | |
9465963f | 308 | const char *s; |
e59e2f7c PR |
309 | |
310 | switch (v->type) { | |
311 | case DM_CFG_STRING: | |
b6d3fd62 | 312 | buf = alloca(dm_escaped_len(v->v.str)); |
9465963f PR |
313 | s = (v->format_flags & DM_CONFIG_VALUE_FMT_STRING_NO_QUOTES) ? "" : "\""; |
314 | line_append("%s%s%s", s, dm_escape_double_quotes(buf, v->v.str), s); | |
e59e2f7c PR |
315 | break; |
316 | ||
317 | case DM_CFG_FLOAT: | |
fe8f5dbe | 318 | line_append("%f", v->v.f); |
e59e2f7c PR |
319 | break; |
320 | ||
321 | case DM_CFG_INT: | |
9465963f PR |
322 | if (v->format_flags & DM_CONFIG_VALUE_FMT_INT_OCTAL) |
323 | line_append("0%" PRIo64, v->v.i); | |
324 | else | |
810ab095 | 325 | line_append(FMTd64, v->v.i); |
e59e2f7c PR |
326 | break; |
327 | ||
328 | case DM_CFG_EMPTY_ARRAY: | |
9465963f PR |
329 | s = (v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES) ? " " : ""; |
330 | line_append("[%s]", s); | |
e59e2f7c PR |
331 | break; |
332 | ||
333 | default: | |
334 | log_error("_write_value: Unknown value type: %d", v->type); | |
335 | ||
336 | } | |
337 | ||
338 | return 1; | |
339 | } | |
340 | ||
341 | static int _write_config(const struct dm_config_node *n, int only_one, | |
e29cd366 | 342 | struct config_output *out, int level) |
e59e2f7c | 343 | { |
9465963f PR |
344 | const char *extra_space; |
345 | int format_array; | |
e59e2f7c PR |
346 | char space[MAX_INDENT + 1]; |
347 | int l = (level < MAX_INDENT) ? level : MAX_INDENT; | |
348 | int i; | |
c3539495 | 349 | char *escaped_key = NULL; |
e59e2f7c PR |
350 | |
351 | if (!n) | |
352 | return 1; | |
353 | ||
354 | for (i = 0; i < l; i++) | |
355 | space[i] = '\t'; | |
356 | space[i] = '\0'; | |
357 | ||
358 | do { | |
9465963f PR |
359 | extra_space = (n->v && (n->v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_EXTRA_SPACES)) ? " " : ""; |
360 | format_array = (n->v && (n->v->format_flags & DM_CONFIG_VALUE_FMT_COMMON_ARRAY)); | |
361 | ||
e29cd366 PR |
362 | if (out->spec && out->spec->prefix_fn) |
363 | out->spec->prefix_fn(n, space, out->baton); | |
364 | ||
365 | if (!_line_start(out)) | |
e59e2f7c | 366 | return_0; |
c3539495 PR |
367 | if (strchr(n->key, '#') || strchr(n->key, '"') || strchr(n->key, '!')) { |
368 | escaped_key = alloca(dm_escaped_len(n->key) + 2); | |
369 | *escaped_key = '"'; | |
370 | dm_escape_double_quotes(escaped_key + 1, n->key); | |
371 | strcat(escaped_key, "\""); | |
372 | } | |
373 | line_append("%s%s", space, escaped_key ? escaped_key : n->key); | |
374 | escaped_key = NULL; | |
e59e2f7c PR |
375 | if (!n->v) { |
376 | /* it's a sub section */ | |
377 | line_append(" {"); | |
e29cd366 | 378 | if (!_line_end(n, out)) |
e59e2f7c | 379 | return_0; |
9720898c PR |
380 | if (!_write_config(n->child, 0, out, level + 1)) |
381 | return_0; | |
e29cd366 | 382 | if (!_line_start(out)) |
e59e2f7c PR |
383 | return_0; |
384 | line_append("%s}", space); | |
385 | } else { | |
386 | /* it's a value */ | |
387 | const struct dm_config_value *v = n->v; | |
9465963f | 388 | line_append("%s=%s", extra_space, extra_space); |
e59e2f7c | 389 | if (v->next) { |
9465963f | 390 | line_append("[%s", extra_space); |
e59e2f7c | 391 | while (v && v->type != DM_CFG_EMPTY_ARRAY) { |
e29cd366 | 392 | if (!_write_value(out, v)) |
e59e2f7c PR |
393 | return_0; |
394 | v = v->next; | |
395 | if (v && v->type != DM_CFG_EMPTY_ARRAY) | |
9465963f | 396 | line_append(",%s", extra_space); |
e59e2f7c | 397 | } |
9465963f PR |
398 | line_append("%s]", extra_space); |
399 | } else { | |
400 | if (format_array && (v->type != DM_CFG_EMPTY_ARRAY)) | |
401 | line_append("[%s", extra_space); | |
e29cd366 | 402 | if (!_write_value(out, v)) |
e59e2f7c | 403 | return_0; |
9465963f PR |
404 | if (format_array && (v->type != DM_CFG_EMPTY_ARRAY)) |
405 | line_append("%s]", extra_space); | |
406 | } | |
e59e2f7c | 407 | } |
e29cd366 | 408 | if (!_line_end(n, out)) |
e59e2f7c | 409 | return_0; |
e29cd366 PR |
410 | |
411 | if (out->spec && out->spec->suffix_fn) | |
412 | out->spec->suffix_fn(n, space, out->baton); | |
413 | ||
e59e2f7c PR |
414 | n = n->sib; |
415 | } while (n && !only_one); | |
416 | /* FIXME: add error checking */ | |
417 | return 1; | |
418 | } | |
419 | ||
5e36b86c | 420 | static int _write_node(const struct dm_config_node *cn, int only_one, |
e29cd366 PR |
421 | dm_putline_fn putline, |
422 | const struct dm_config_node_out_spec *out_spec, | |
423 | void *baton) | |
e59e2f7c | 424 | { |
5e19410d ZK |
425 | struct config_output out = { |
426 | .mem = dm_pool_create("config_output", 1024), | |
427 | .putline = putline, | |
428 | .spec = out_spec, | |
429 | .baton = baton | |
430 | }; | |
431 | ||
432 | if (!out.mem) | |
e59e2f7c | 433 | return_0; |
5e19410d | 434 | |
e29cd366 PR |
435 | if (!_write_config(cn, only_one, &out, 0)) { |
436 | dm_pool_destroy(out.mem); | |
e59e2f7c PR |
437 | return_0; |
438 | } | |
e29cd366 | 439 | dm_pool_destroy(out.mem); |
e59e2f7c PR |
440 | return 1; |
441 | } | |
442 | ||
5e36b86c PR |
443 | int dm_config_write_one_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton) |
444 | { | |
e29cd366 | 445 | return _write_node(cn, 1, putline, NULL, baton); |
5e36b86c PR |
446 | } |
447 | ||
448 | int dm_config_write_node(const struct dm_config_node *cn, dm_putline_fn putline, void *baton) | |
449 | { | |
e29cd366 PR |
450 | return _write_node(cn, 0, putline, NULL, baton); |
451 | } | |
452 | ||
453 | int dm_config_write_one_node_out(const struct dm_config_node *cn, | |
454 | const struct dm_config_node_out_spec *out_spec, | |
455 | void *baton) | |
456 | { | |
457 | return _write_node(cn, 1, NULL, out_spec, baton); | |
458 | } | |
459 | ||
460 | int dm_config_write_node_out(const struct dm_config_node *cn, | |
461 | const struct dm_config_node_out_spec *out_spec, | |
462 | void *baton) | |
463 | { | |
464 | return _write_node(cn, 0, NULL, out_spec, baton); | |
5e36b86c PR |
465 | } |
466 | ||
e59e2f7c PR |
467 | /* |
468 | * parser | |
469 | */ | |
4f439707 ZK |
470 | static char *_dup_string_tok(struct parser *p) |
471 | { | |
472 | char *str; | |
473 | ||
474 | p->tb++, p->te--; /* strip "'s */ | |
475 | ||
476 | if (p->te < p->tb) { | |
477 | log_error("Parse error at byte %" PRIptrdiff_t " (line %d): " | |
478 | "expected a string token.", | |
479 | p->tb - p->fb + 1, p->line); | |
480 | return NULL; | |
481 | } | |
482 | ||
483 | if (!(str = _dup_tok(p))) | |
484 | return_NULL; | |
485 | ||
486 | p->te++; | |
487 | ||
488 | return str; | |
489 | } | |
490 | ||
e59e2f7c PR |
491 | static struct dm_config_node *_file(struct parser *p) |
492 | { | |
687029cb PR |
493 | struct dm_config_node root = { 0 }; |
494 | root.key = "<root>"; | |
495 | ||
496 | while (p->t != TOK_EOF) | |
497 | if (!_section(p, &root)) | |
f45106b7 | 498 | return_NULL; |
687029cb PR |
499 | return root.child; |
500 | } | |
501 | ||
502 | static struct dm_config_node *_make_node(struct dm_pool *mem, | |
503 | const char *key_b, const char *key_e, | |
504 | struct dm_config_node *parent) | |
505 | { | |
506 | struct dm_config_node *n; | |
507 | ||
508 | if (!(n = _create_node(mem))) | |
509 | return_NULL; | |
e59e2f7c | 510 | |
687029cb PR |
511 | n->key = _dup_token(mem, key_b, key_e); |
512 | if (parent) { | |
513 | n->parent = parent; | |
514 | n->sib = parent->child; | |
515 | parent->child = n; | |
e59e2f7c | 516 | } |
687029cb PR |
517 | return n; |
518 | } | |
519 | ||
520 | /* when mem is not NULL, we create the path if it doesn't exist yet */ | |
521 | static struct dm_config_node *_find_or_make_node(struct dm_pool *mem, | |
522 | struct dm_config_node *parent, | |
7563e69c PR |
523 | const char *path, |
524 | int no_dup_node_check) | |
687029cb | 525 | { |
026344e8 | 526 | const int sep = '/'; |
687029cb PR |
527 | const char *e; |
528 | struct dm_config_node *cn = parent ? parent->child : NULL; | |
529 | struct dm_config_node *cn_found = NULL; | |
530 | ||
531 | while (cn || mem) { | |
532 | /* trim any leading slashes */ | |
026344e8 | 533 | while (*path && (*path == sep)) |
687029cb PR |
534 | path++; |
535 | ||
536 | /* find the end of this segment */ | |
026344e8 | 537 | for (e = path; *e && (*e != sep); e++) ; |
687029cb PR |
538 | |
539 | /* hunt for the node */ | |
540 | cn_found = NULL; | |
541 | ||
7563e69c PR |
542 | if (!no_dup_node_check) { |
543 | while (cn) { | |
544 | if (_tok_match(cn->key, path, e)) { | |
545 | /* Inefficient */ | |
546 | if (!cn_found) | |
547 | cn_found = cn; | |
548 | else | |
549 | log_warn("WARNING: Ignoring duplicate" | |
550 | " config node: %s (" | |
551 | "seeking %s)", cn->key, path); | |
552 | } | |
687029cb | 553 | |
7563e69c PR |
554 | cn = cn->sib; |
555 | } | |
687029cb PR |
556 | } |
557 | ||
558 | if (!cn_found && mem) { | |
559 | if (!(cn_found = _make_node(mem, path, e, parent))) | |
560 | return_NULL; | |
561 | } | |
562 | ||
563 | if (cn_found && *e) { | |
564 | parent = cn_found; | |
565 | cn = cn_found->child; | |
566 | } else | |
567 | return cn_found; | |
568 | path = e; | |
569 | } | |
570 | ||
571 | return NULL; | |
e59e2f7c PR |
572 | } |
573 | ||
687029cb | 574 | static struct dm_config_node *_section(struct parser *p, struct dm_config_node *parent) |
e59e2f7c PR |
575 | { |
576 | /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */ | |
b6d3fd62 | 577 | |
4c184e9d | 578 | struct dm_config_node *root; |
0050480c | 579 | struct dm_config_value *value; |
b6d3fd62 AK |
580 | char *str; |
581 | ||
c3539495 | 582 | if (p->t == TOK_STRING_ESCAPED) { |
b6d3fd62 AK |
583 | if (!(str = _dup_string_tok(p))) |
584 | return_NULL; | |
585 | dm_unescape_double_quotes(str); | |
b6d3fd62 | 586 | |
c3539495 | 587 | match(TOK_STRING_ESCAPED); |
b6d3fd62 AK |
588 | } else if (p->t == TOK_STRING) { |
589 | if (!(str = _dup_string_tok(p))) | |
590 | return_NULL; | |
b6d3fd62 AK |
591 | |
592 | match(TOK_STRING); | |
593 | } else { | |
687029cb | 594 | if (!(str = _dup_tok(p))) |
b6d3fd62 AK |
595 | return_NULL; |
596 | ||
c3539495 | 597 | match(TOK_IDENTIFIER); |
b6d3fd62 AK |
598 | } |
599 | ||
687029cb | 600 | if (!strlen(str)) { |
b6d3fd62 AK |
601 | log_error("Parse error at byte %" PRIptrdiff_t " (line %d): empty section identifier", |
602 | p->tb - p->fb + 1, p->line); | |
603 | return NULL; | |
604 | } | |
e59e2f7c | 605 | |
7563e69c | 606 | if (!(root = _find_or_make_node(p->mem, parent, str, p->no_dup_node_check))) |
b1c40177 | 607 | return_NULL; |
687029cb | 608 | |
e59e2f7c PR |
609 | if (p->t == TOK_SECTION_B) { |
610 | match(TOK_SECTION_B); | |
611 | while (p->t != TOK_SECTION_E) { | |
4c184e9d | 612 | if (!(_section(p, root))) |
f45106b7 | 613 | return_NULL; |
e59e2f7c PR |
614 | } |
615 | match(TOK_SECTION_E); | |
616 | } else { | |
617 | match(TOK_EQ); | |
85dbcda1 | 618 | p->key = root->key; |
0050480c | 619 | if (!(value = _value(p))) |
f45106b7 | 620 | return_NULL; |
0050480c PR |
621 | if (root->v) |
622 | log_warn("WARNING: Ignoring duplicate" | |
623 | " config value: %s", str); | |
624 | root->v = value; | |
e59e2f7c PR |
625 | } |
626 | ||
627 | return root; | |
628 | } | |
629 | ||
630 | static struct dm_config_value *_value(struct parser *p) | |
631 | { | |
632 | /* '[' TYPE* ']' | TYPE */ | |
633 | struct dm_config_value *h = NULL, *l, *ll = NULL; | |
634 | if (p->t == TOK_ARRAY_B) { | |
635 | match(TOK_ARRAY_B); | |
636 | while (p->t != TOK_ARRAY_E) { | |
637 | if (!(l = _type(p))) | |
f45106b7 | 638 | return_NULL; |
e59e2f7c PR |
639 | |
640 | if (!h) | |
641 | h = l; | |
642 | else | |
643 | ll->next = l; | |
644 | ll = l; | |
645 | ||
646 | if (p->t == TOK_COMMA) | |
647 | match(TOK_COMMA); | |
648 | } | |
649 | match(TOK_ARRAY_E); | |
650 | /* | |
651 | * Special case for an empty array. | |
652 | */ | |
653 | if (!h) { | |
d2c25f02 ZK |
654 | if (!(h = _create_value(p->mem))) { |
655 | log_error("Failed to allocate value"); | |
656 | return NULL; | |
657 | } | |
e59e2f7c PR |
658 | |
659 | h->type = DM_CFG_EMPTY_ARRAY; | |
660 | } | |
661 | ||
662 | } else | |
60444f8b ZK |
663 | if (!(h = _type(p))) |
664 | return_NULL; | |
e59e2f7c PR |
665 | |
666 | return h; | |
667 | } | |
668 | ||
669 | static struct dm_config_value *_type(struct parser *p) | |
670 | { | |
671 | /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */ | |
672 | struct dm_config_value *v = _create_value(p->mem); | |
673 | char *str; | |
674 | ||
d2c25f02 ZK |
675 | if (!v) { |
676 | log_error("Failed to allocate type value"); | |
e59e2f7c | 677 | return NULL; |
d2c25f02 | 678 | } |
e59e2f7c PR |
679 | |
680 | switch (p->t) { | |
681 | case TOK_INT: | |
682 | v->type = DM_CFG_INT; | |
ba9820b1 | 683 | errno = 0; |
e59e2f7c | 684 | v->v.i = strtoll(p->tb, NULL, 0); /* FIXME: check error */ |
ba9820b1 | 685 | if (errno) { |
85dbcda1 ZK |
686 | if (errno == ERANGE && p->key && |
687 | strcmp("creation_time", p->key) == 0) { | |
688 | /* Due to a bug in some older 32bit builds (<2.02.169), | |
689 | * lvm was able to produce invalid creation_time string */ | |
690 | v->v.i = 1527120000; /* Pick 2018-05-24 day instead */ | |
691 | if (!p->ignored_creation_time++) | |
692 | log_warn("WARNING: Invalid creation_time found in metadata (repaired with next metadata update)."); | |
693 | } else { | |
694 | log_error("Failed to read int token."); | |
695 | return NULL; | |
696 | } | |
ba9820b1 | 697 | } |
e59e2f7c PR |
698 | match(TOK_INT); |
699 | break; | |
700 | ||
701 | case TOK_FLOAT: | |
702 | v->type = DM_CFG_FLOAT; | |
ba9820b1 | 703 | errno = 0; |
fe8f5dbe | 704 | v->v.f = strtod(p->tb, NULL); /* FIXME: check error */ |
ba9820b1 ZK |
705 | if (errno) { |
706 | log_error("Failed to read float token."); | |
707 | return NULL; | |
708 | } | |
e59e2f7c PR |
709 | match(TOK_FLOAT); |
710 | break; | |
711 | ||
712 | case TOK_STRING: | |
713 | v->type = DM_CFG_STRING; | |
714 | ||
4f439707 | 715 | if (!(v->v.str = _dup_string_tok(p))) |
f45106b7 | 716 | return_NULL; |
4f439707 | 717 | |
e59e2f7c PR |
718 | match(TOK_STRING); |
719 | break; | |
720 | ||
8bc99667 PR |
721 | case TOK_STRING_BARE: |
722 | v->type = DM_CFG_STRING; | |
723 | ||
724 | if (!(v->v.str = _dup_tok(p))) | |
725 | return_NULL; | |
726 | ||
727 | match(TOK_STRING_BARE); | |
728 | break; | |
729 | ||
e59e2f7c PR |
730 | case TOK_STRING_ESCAPED: |
731 | v->type = DM_CFG_STRING; | |
732 | ||
4f439707 | 733 | if (!(str = _dup_string_tok(p))) |
f45106b7 | 734 | return_NULL; |
e59e2f7c PR |
735 | dm_unescape_double_quotes(str); |
736 | v->v.str = str; | |
e59e2f7c PR |
737 | match(TOK_STRING_ESCAPED); |
738 | break; | |
739 | ||
740 | default: | |
741 | log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value", | |
742 | p->tb - p->fb + 1, p->line); | |
f45106b7 | 743 | return NULL; |
e59e2f7c PR |
744 | } |
745 | return v; | |
746 | } | |
747 | ||
748 | static int _match_aux(struct parser *p, int t) | |
749 | { | |
750 | if (p->t != t) | |
751 | return 0; | |
752 | ||
753 | _get_token(p, t); | |
754 | return 1; | |
755 | } | |
756 | ||
757 | /* | |
39b7d1ba | 758 | * tokenizer |
e59e2f7c PR |
759 | */ |
760 | static void _get_token(struct parser *p, int tok_prev) | |
761 | { | |
762 | int values_allowed = 0; | |
763 | ||
764 | const char *te; | |
765 | ||
766 | p->tb = p->te; | |
767 | _eat_space(p); | |
768 | if (p->tb == p->fe || !*p->tb) { | |
769 | p->t = TOK_EOF; | |
770 | return; | |
771 | } | |
772 | ||
773 | /* Should next token be interpreted as value instead of identifier? */ | |
774 | if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B || | |
775 | tok_prev == TOK_COMMA) | |
776 | values_allowed = 1; | |
777 | ||
778 | p->t = TOK_INT; /* fudge so the fall through for | |
779 | floats works */ | |
780 | ||
781 | te = p->te; | |
782 | switch (*te) { | |
783 | case SECTION_B_CHAR: | |
784 | p->t = TOK_SECTION_B; | |
785 | te++; | |
786 | break; | |
787 | ||
788 | case SECTION_E_CHAR: | |
789 | p->t = TOK_SECTION_E; | |
790 | te++; | |
791 | break; | |
792 | ||
793 | case '[': | |
794 | p->t = TOK_ARRAY_B; | |
795 | te++; | |
796 | break; | |
797 | ||
798 | case ']': | |
799 | p->t = TOK_ARRAY_E; | |
800 | te++; | |
801 | break; | |
802 | ||
803 | case ',': | |
804 | p->t = TOK_COMMA; | |
805 | te++; | |
806 | break; | |
807 | ||
808 | case '=': | |
809 | p->t = TOK_EQ; | |
810 | te++; | |
811 | break; | |
812 | ||
813 | case '"': | |
814 | p->t = TOK_STRING_ESCAPED; | |
815 | te++; | |
816 | while ((te != p->fe) && (*te) && (*te != '"')) { | |
817 | if ((*te == '\\') && (te + 1 != p->fe) && | |
818 | *(te + 1)) | |
819 | te++; | |
820 | te++; | |
821 | } | |
822 | ||
823 | if ((te != p->fe) && (*te)) | |
824 | te++; | |
825 | break; | |
826 | ||
827 | case '\'': | |
828 | p->t = TOK_STRING; | |
829 | te++; | |
830 | while ((te != p->fe) && (*te) && (*te != '\'')) | |
831 | te++; | |
832 | ||
833 | if ((te != p->fe) && (*te)) | |
834 | te++; | |
835 | break; | |
836 | ||
837 | case '.': | |
838 | p->t = TOK_FLOAT; | |
839 | /* Fall through */ | |
840 | case '0': | |
841 | case '1': | |
842 | case '2': | |
843 | case '3': | |
844 | case '4': | |
845 | case '5': | |
846 | case '6': | |
847 | case '7': | |
848 | case '8': | |
849 | case '9': | |
850 | case '+': | |
851 | case '-': | |
852 | if (values_allowed) { | |
853 | while (++te != p->fe) { | |
854 | if (!isdigit((int) *te)) { | |
855 | if (*te == '.') { | |
856 | if (p->t != TOK_FLOAT) { | |
857 | p->t = TOK_FLOAT; | |
858 | continue; | |
859 | } | |
860 | } | |
861 | break; | |
862 | } | |
863 | } | |
864 | break; | |
865 | } | |
866 | /* fall through */ | |
867 | ||
868 | default: | |
869 | p->t = TOK_IDENTIFIER; | |
870 | while ((te != p->fe) && (*te) && !isspace(*te) && | |
871 | (*te != '#') && (*te != '=') && | |
872 | (*te != SECTION_B_CHAR) && | |
873 | (*te != SECTION_E_CHAR)) | |
874 | te++; | |
8bc99667 PR |
875 | if (values_allowed) |
876 | p->t = TOK_STRING_BARE; | |
e59e2f7c PR |
877 | break; |
878 | } | |
879 | ||
880 | p->te = te; | |
881 | } | |
882 | ||
883 | static void _eat_space(struct parser *p) | |
884 | { | |
885 | while (p->tb != p->fe) { | |
886 | if (*p->te == '#') | |
887 | while ((p->te != p->fe) && (*p->te != '\n') && (*p->te)) | |
888 | ++p->te; | |
889 | ||
890 | else if (!isspace(*p->te)) | |
891 | break; | |
892 | ||
893 | while ((p->te != p->fe) && isspace(*p->te)) { | |
894 | if (*p->te == '\n') | |
895 | ++p->line; | |
896 | ++p->te; | |
897 | } | |
898 | ||
899 | p->tb = p->te; | |
900 | } | |
901 | } | |
902 | ||
903 | /* | |
904 | * memory management | |
905 | */ | |
906 | static struct dm_config_value *_create_value(struct dm_pool *mem) | |
907 | { | |
908 | return dm_pool_zalloc(mem, sizeof(struct dm_config_value)); | |
909 | } | |
910 | ||
911 | static struct dm_config_node *_create_node(struct dm_pool *mem) | |
912 | { | |
913 | return dm_pool_zalloc(mem, sizeof(struct dm_config_node)); | |
914 | } | |
915 | ||
687029cb | 916 | static char *_dup_token(struct dm_pool *mem, const char *b, const char *e) |
e59e2f7c | 917 | { |
687029cb PR |
918 | size_t len = e - b; |
919 | char *str = dm_pool_alloc(mem, len + 1); | |
e59e2f7c PR |
920 | if (!str) { |
921 | log_error("Failed to duplicate token."); | |
922 | return 0; | |
923 | } | |
687029cb | 924 | memcpy(str, b, len); |
e59e2f7c PR |
925 | str[len] = '\0'; |
926 | return str; | |
927 | } | |
928 | ||
687029cb PR |
929 | static char *_dup_tok(struct parser *p) |
930 | { | |
931 | return _dup_token(p->mem, p->tb, p->te); | |
932 | } | |
933 | ||
e59e2f7c | 934 | /* |
fe8f5dbe AK |
935 | * Utility functions |
936 | */ | |
937 | ||
938 | /* | |
939 | * node_lookup_fn is either: | |
940 | * _find_config_node to perform a lookup starting from a given config_node | |
941 | * in a config_tree; | |
942 | * or | |
943 | * _find_first_config_node to find the first config_node in a set of | |
944 | * cascaded trees. | |
e59e2f7c | 945 | */ |
fe8f5dbe AK |
946 | typedef const struct dm_config_node *node_lookup_fn(const void *start, const char *path); |
947 | ||
687029cb PR |
948 | static const struct dm_config_node *_find_config_node(const void *start, const char *path) { |
949 | struct dm_config_node dummy = { .child = (void *) start }; | |
7563e69c | 950 | return _find_or_make_node(NULL, &dummy, path, 0); |
e59e2f7c PR |
951 | } |
952 | ||
441edcb5 | 953 | static const struct dm_config_node *_find_first_config_node(const void *start, const char *path) |
e59e2f7c PR |
954 | { |
955 | const struct dm_config_tree *cft = start; | |
441edcb5 | 956 | const struct dm_config_node *cn = NULL; |
e59e2f7c PR |
957 | |
958 | while (cft) { | |
959 | if ((cn = _find_config_node(cft->root, path))) | |
960 | return cn; | |
961 | cft = cft->cascade; | |
962 | } | |
963 | ||
964 | return NULL; | |
965 | } | |
966 | ||
fe8f5dbe | 967 | static const char *_find_config_str(const void *start, node_lookup_fn find_fn, |
7ad1c43b | 968 | const char *path, const char *fail, int allow_empty) |
e59e2f7c | 969 | { |
fe8f5dbe | 970 | const struct dm_config_node *n = find_fn(start, path); |
e59e2f7c | 971 | |
ebf55527 AK |
972 | /* Empty strings are ignored if allow_empty is set */ |
973 | if (n && n->v) { | |
974 | if ((n->v->type == DM_CFG_STRING) && | |
975 | (allow_empty || (*n->v->v.str))) { | |
9b6a62f9 | 976 | /* log_very_verbose("Setting %s to %s", path, n->v->v.str); */ |
ebf55527 AK |
977 | return n->v->v.str; |
978 | } | |
979 | if ((n->v->type != DM_CFG_STRING) || (!allow_empty && fail)) | |
980 | log_warn("WARNING: Ignoring unsupported value for %s.", path); | |
981 | } | |
e59e2f7c PR |
982 | |
983 | if (fail) | |
984 | log_very_verbose("%s not found in config: defaulting to %s", | |
985 | path, fail); | |
986 | return fail; | |
987 | } | |
988 | ||
989 | const char *dm_config_find_str(const struct dm_config_node *cn, | |
990 | const char *path, const char *fail) | |
991 | { | |
7ad1c43b | 992 | return _find_config_str(cn, _find_config_node, path, fail, 0); |
e59e2f7c PR |
993 | } |
994 | ||
99a150fc ZK |
995 | const char *dm_config_find_str_allow_empty(const struct dm_config_node *cn, |
996 | const char *path, const char *fail) | |
997 | { | |
998 | return _find_config_str(cn, _find_config_node, path, fail, 1); | |
999 | } | |
1000 | ||
fe8f5dbe | 1001 | static int64_t _find_config_int64(const void *start, node_lookup_fn find, |
e59e2f7c PR |
1002 | const char *path, int64_t fail) |
1003 | { | |
1004 | const struct dm_config_node *n = find(start, path); | |
1005 | ||
1006 | if (n && n->v && n->v->type == DM_CFG_INT) { | |
9b6a62f9 | 1007 | /* log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i); */ |
e59e2f7c PR |
1008 | return n->v->v.i; |
1009 | } | |
1010 | ||
1011 | log_very_verbose("%s not found in config: defaulting to %" PRId64, | |
1012 | path, fail); | |
1013 | return fail; | |
1014 | } | |
1015 | ||
fe8f5dbe | 1016 | static float _find_config_float(const void *start, node_lookup_fn find, |
e59e2f7c PR |
1017 | const char *path, float fail) |
1018 | { | |
1019 | const struct dm_config_node *n = find(start, path); | |
1020 | ||
1021 | if (n && n->v && n->v->type == DM_CFG_FLOAT) { | |
9b6a62f9 | 1022 | /* log_very_verbose("Setting %s to %f", path, n->v->v.f); */ |
fe8f5dbe | 1023 | return n->v->v.f; |
e59e2f7c PR |
1024 | } |
1025 | ||
1026 | log_very_verbose("%s not found in config: defaulting to %f", | |
1027 | path, fail); | |
1028 | ||
1029 | return fail; | |
1030 | ||
1031 | } | |
1032 | ||
1033 | static int _str_in_array(const char *str, const char * const values[]) | |
1034 | { | |
1035 | int i; | |
1036 | ||
1037 | for (i = 0; values[i]; i++) | |
1038 | if (!strcasecmp(str, values[i])) | |
1039 | return 1; | |
1040 | ||
1041 | return 0; | |
1042 | } | |
1043 | ||
1044 | static int _str_to_bool(const char *str, int fail) | |
1045 | { | |
1046 | const char * const _true_values[] = { "y", "yes", "on", "true", NULL }; | |
1047 | const char * const _false_values[] = { "n", "no", "off", "false", NULL }; | |
1048 | ||
1049 | if (_str_in_array(str, _true_values)) | |
1050 | return 1; | |
1051 | ||
1052 | if (_str_in_array(str, _false_values)) | |
1053 | return 0; | |
1054 | ||
1055 | return fail; | |
1056 | } | |
1057 | ||
fe8f5dbe | 1058 | static int _find_config_bool(const void *start, node_lookup_fn find, |
e59e2f7c PR |
1059 | const char *path, int fail) |
1060 | { | |
1061 | const struct dm_config_node *n = find(start, path); | |
1062 | const struct dm_config_value *v; | |
fccc6ea2 AK |
1063 | int b; |
1064 | ||
1065 | if (n) { | |
1066 | v = n->v; | |
1067 | ||
1068 | switch (v->type) { | |
1069 | case DM_CFG_INT: | |
1070 | b = v->v.i ? 1 : 0; | |
9b6a62f9 | 1071 | /* log_very_verbose("Setting %s to %d", path, b); */ |
fccc6ea2 AK |
1072 | return b; |
1073 | ||
1074 | case DM_CFG_STRING: | |
1075 | b = _str_to_bool(v->v.str, fail); | |
9b6a62f9 | 1076 | /* log_very_verbose("Setting %s to %d", path, b); */ |
fccc6ea2 AK |
1077 | return b; |
1078 | default: | |
1079 | ; | |
1080 | } | |
e59e2f7c PR |
1081 | } |
1082 | ||
fccc6ea2 AK |
1083 | log_very_verbose("%s not found in config: defaulting to %d", |
1084 | path, fail); | |
1085 | ||
e59e2f7c PR |
1086 | return fail; |
1087 | } | |
1088 | ||
1089 | /*********************************** | |
1090 | * node-based lookup | |
1091 | **/ | |
1092 | ||
00ab1e3f | 1093 | struct dm_config_node *dm_config_find_node(const struct dm_config_node *cn, |
e68a6fbf | 1094 | const char *path) |
e59e2f7c | 1095 | { |
441edcb5 | 1096 | return (struct dm_config_node *) _find_config_node(cn, path); |
e59e2f7c PR |
1097 | } |
1098 | ||
1099 | int dm_config_find_int(const struct dm_config_node *cn, const char *path, int fail) | |
1100 | { | |
1101 | /* FIXME Add log_error message on overflow */ | |
1102 | return (int) _find_config_int64(cn, _find_config_node, path, (int64_t) fail); | |
1103 | } | |
1104 | ||
82326847 PR |
1105 | int64_t dm_config_find_int64(const struct dm_config_node *cn, const char *path, int64_t fail) |
1106 | { | |
1107 | return _find_config_int64(cn, _find_config_node, path, fail); | |
1108 | } | |
1109 | ||
e59e2f7c PR |
1110 | float dm_config_find_float(const struct dm_config_node *cn, const char *path, |
1111 | float fail) | |
1112 | { | |
1113 | return _find_config_float(cn, _find_config_node, path, fail); | |
1114 | } | |
1115 | ||
1116 | int dm_config_find_bool(const struct dm_config_node *cn, const char *path, int fail) | |
1117 | { | |
1118 | return _find_config_bool(cn, _find_config_node, path, fail); | |
1119 | } | |
1120 | ||
296385c0 PR |
1121 | int dm_config_value_is_bool(const struct dm_config_value *v) { |
1122 | if (!v) | |
1123 | return 0; | |
1124 | ||
1125 | switch(v->type) { | |
1126 | case DM_CFG_INT: | |
1127 | return 1; | |
1128 | case DM_CFG_STRING: | |
1129 | return _str_to_bool(v->v.str, -1) != -1; | |
1130 | default: | |
1131 | return 0; | |
1132 | } | |
1133 | } | |
1134 | ||
e59e2f7c PR |
1135 | /*********************************** |
1136 | * tree-based lookup | |
1137 | **/ | |
1138 | ||
1139 | const struct dm_config_node *dm_config_tree_find_node(const struct dm_config_tree *cft, | |
1140 | const char *path) | |
1141 | { | |
1142 | return _find_first_config_node(cft, path); | |
1143 | } | |
1144 | ||
1145 | const char *dm_config_tree_find_str(const struct dm_config_tree *cft, const char *path, | |
1146 | const char *fail) | |
1147 | { | |
7ad1c43b ZK |
1148 | return _find_config_str(cft, _find_first_config_node, path, fail, 0); |
1149 | } | |
1150 | ||
1151 | const char *dm_config_tree_find_str_allow_empty(const struct dm_config_tree *cft, const char *path, | |
1152 | const char *fail) | |
1153 | { | |
1154 | return _find_config_str(cft, _find_first_config_node, path, fail, 1); | |
e59e2f7c PR |
1155 | } |
1156 | ||
1157 | int dm_config_tree_find_int(const struct dm_config_tree *cft, const char *path, int fail) | |
1158 | { | |
1159 | /* FIXME Add log_error message on overflow */ | |
1160 | return (int) _find_config_int64(cft, _find_first_config_node, path, (int64_t) fail); | |
1161 | } | |
1162 | ||
1163 | int64_t dm_config_tree_find_int64(const struct dm_config_tree *cft, const char *path, int64_t fail) | |
1164 | { | |
1165 | return _find_config_int64(cft, _find_first_config_node, path, fail); | |
1166 | } | |
1167 | ||
1168 | float dm_config_tree_find_float(const struct dm_config_tree *cft, const char *path, | |
1169 | float fail) | |
1170 | { | |
1171 | return _find_config_float(cft, _find_first_config_node, path, fail); | |
1172 | } | |
1173 | ||
1174 | int dm_config_tree_find_bool(const struct dm_config_tree *cft, const char *path, int fail) | |
1175 | { | |
1176 | return _find_config_bool(cft, _find_first_config_node, path, fail); | |
1177 | } | |
1178 | ||
1179 | /************************************/ | |
1180 | ||
1181 | ||
1182 | int dm_config_get_uint32(const struct dm_config_node *cn, const char *path, | |
97a4b516 | 1183 | uint32_t *result) |
e59e2f7c PR |
1184 | { |
1185 | const struct dm_config_node *n; | |
1186 | ||
97a4b516 | 1187 | n = _find_config_node(cn, path); |
e59e2f7c PR |
1188 | |
1189 | if (!n || !n->v || n->v->type != DM_CFG_INT) | |
1190 | return 0; | |
1191 | ||
78042463 | 1192 | if (result) |
864ec23e | 1193 | *result = n->v->v.i; |
e59e2f7c PR |
1194 | return 1; |
1195 | } | |
1196 | ||
1197 | int dm_config_get_uint64(const struct dm_config_node *cn, const char *path, | |
1198 | uint64_t *result) | |
1199 | { | |
1200 | const struct dm_config_node *n; | |
1201 | ||
97a4b516 | 1202 | n = _find_config_node(cn, path); |
e59e2f7c PR |
1203 | |
1204 | if (!n || !n->v || n->v->type != DM_CFG_INT) | |
1205 | return 0; | |
1206 | ||
78042463 | 1207 | if (result) |
864ec23e | 1208 | *result = (uint64_t) n->v->v.i; |
e59e2f7c PR |
1209 | return 1; |
1210 | } | |
1211 | ||
1212 | int dm_config_get_str(const struct dm_config_node *cn, const char *path, | |
1213 | const char **result) | |
1214 | { | |
1215 | const struct dm_config_node *n; | |
1216 | ||
97a4b516 | 1217 | n = _find_config_node(cn, path); |
e59e2f7c PR |
1218 | |
1219 | if (!n || !n->v || n->v->type != DM_CFG_STRING) | |
1220 | return 0; | |
1221 | ||
78042463 | 1222 | if (result) |
864ec23e | 1223 | *result = n->v->v.str; |
e59e2f7c PR |
1224 | return 1; |
1225 | } | |
1226 | ||
97a4b516 PR |
1227 | int dm_config_get_list(const struct dm_config_node *cn, const char *path, |
1228 | const struct dm_config_value **result) | |
1229 | { | |
1230 | const struct dm_config_node *n; | |
1231 | ||
1232 | n = _find_config_node(cn, path); | |
1233 | /* TODO when we represent single-item lists consistently, add a check | |
1234 | * for n->v->next != NULL */ | |
1235 | if (!n || !n->v) | |
1236 | return 0; | |
1237 | ||
864ec23e PR |
1238 | if (result) |
1239 | *result = n->v; | |
97a4b516 PR |
1240 | return 1; |
1241 | } | |
1242 | ||
1243 | int dm_config_get_section(const struct dm_config_node *cn, const char *path, | |
1244 | const struct dm_config_node **result) | |
1245 | { | |
1246 | const struct dm_config_node *n; | |
1247 | ||
1248 | n = _find_config_node(cn, path); | |
1249 | if (!n || n->v) | |
1250 | return 0; | |
1251 | ||
864ec23e PR |
1252 | if (result) |
1253 | *result = n; | |
97a4b516 PR |
1254 | return 1; |
1255 | } | |
1256 | ||
1257 | int dm_config_has_node(const struct dm_config_node *cn, const char *path) | |
1258 | { | |
1259 | return _find_config_node(cn, path) ? 1 : 0; | |
1260 | } | |
1261 | ||
e59e2f7c PR |
1262 | /* |
1263 | * Convert a token type to the char it represents. | |
1264 | */ | |
1265 | static char _token_type_to_char(int type) | |
1266 | { | |
1267 | switch (type) { | |
1268 | case TOK_SECTION_B: | |
1269 | return SECTION_B_CHAR; | |
1270 | case TOK_SECTION_E: | |
1271 | return SECTION_E_CHAR; | |
1272 | default: | |
1273 | return 0; | |
1274 | } | |
1275 | } | |
1276 | ||
1277 | /* | |
1278 | * Returns: | |
1279 | * # of 'type' tokens in 'str'. | |
1280 | */ | |
1281 | static unsigned _count_tokens(const char *str, unsigned len, int type) | |
1282 | { | |
1283 | char c; | |
1284 | ||
1285 | c = _token_type_to_char(type); | |
1286 | ||
1287 | return dm_count_chars(str, len, c); | |
1288 | } | |
1289 | ||
1290 | const char *dm_config_parent_name(const struct dm_config_node *n) | |
1291 | { | |
1292 | return (n->parent ? n->parent->key : "(root)"); | |
1293 | } | |
1294 | /* | |
1295 | * Heuristic function to make a quick guess as to whether a text | |
1296 | * region probably contains a valid config "section". (Useful for | |
1297 | * scanning areas of the disk for old metadata.) | |
1298 | * Config sections contain various tokens, may contain other sections | |
1299 | * and strings, and are delimited by begin (type 'TOK_SECTION_B') and | |
1300 | * end (type 'TOK_SECTION_E') tokens. As a quick heuristic, we just | |
1301 | * count the number of begin and end tokens, and see if they are | |
1302 | * non-zero and the counts match. | |
1303 | * Full validation of the section should be done with another function | |
1304 | * (for example, read_config_fd). | |
1305 | * | |
1306 | * Returns: | |
1307 | * 0 - probably is not a valid config section | |
1308 | * 1 - probably _is_ a valid config section | |
1309 | */ | |
1310 | unsigned dm_config_maybe_section(const char *str, unsigned len) | |
1311 | { | |
1312 | int begin_count; | |
1313 | int end_count; | |
1314 | ||
1315 | begin_count = _count_tokens(str, len, TOK_SECTION_B); | |
1316 | end_count = _count_tokens(str, len, TOK_SECTION_E); | |
1317 | ||
1318 | if (begin_count && end_count && (begin_count == end_count)) | |
1319 | return 1; | |
1320 | else | |
1321 | return 0; | |
1322 | } | |
1323 | ||
807a5a7b | 1324 | __attribute__((nonnull(1, 2))) |
e59e2f7c PR |
1325 | static struct dm_config_value *_clone_config_value(struct dm_pool *mem, |
1326 | const struct dm_config_value *v) | |
1327 | { | |
1328 | struct dm_config_value *new_cv; | |
1329 | ||
e59e2f7c PR |
1330 | if (!(new_cv = _create_value(mem))) { |
1331 | log_error("Failed to clone config value."); | |
1332 | return NULL; | |
1333 | } | |
1334 | ||
1335 | new_cv->type = v->type; | |
1336 | if (v->type == DM_CFG_STRING) { | |
1337 | if (!(new_cv->v.str = dm_pool_strdup(mem, v->v.str))) { | |
1338 | log_error("Failed to clone config string value."); | |
1339 | return NULL; | |
1340 | } | |
1341 | } else | |
1342 | new_cv->v = v->v; | |
1343 | ||
1344 | if (v->next && !(new_cv->next = _clone_config_value(mem, v->next))) | |
1345 | return_NULL; | |
1346 | ||
1347 | return new_cv; | |
1348 | } | |
1349 | ||
1350 | struct dm_config_node *dm_config_clone_node_with_mem(struct dm_pool *mem, const struct dm_config_node *cn, int siblings) | |
1351 | { | |
1352 | struct dm_config_node *new_cn; | |
1353 | ||
d2c25f02 ZK |
1354 | if (!cn) { |
1355 | log_error("Cannot clone NULL config node."); | |
e59e2f7c | 1356 | return NULL; |
d2c25f02 | 1357 | } |
e59e2f7c PR |
1358 | |
1359 | if (!(new_cn = _create_node(mem))) { | |
1360 | log_error("Failed to clone config node."); | |
1361 | return NULL; | |
1362 | } | |
1363 | ||
1364 | if ((cn->key && !(new_cn->key = dm_pool_strdup(mem, cn->key)))) { | |
1365 | log_error("Failed to clone config node key."); | |
1366 | return NULL; | |
1367 | } | |
1368 | ||
f6de196c PR |
1369 | new_cn->id = cn->id; |
1370 | ||
e59e2f7c PR |
1371 | if ((cn->v && !(new_cn->v = _clone_config_value(mem, cn->v))) || |
1372 | (cn->child && !(new_cn->child = dm_config_clone_node_with_mem(mem, cn->child, 1))) || | |
1373 | (siblings && cn->sib && !(new_cn->sib = dm_config_clone_node_with_mem(mem, cn->sib, siblings)))) | |
1374 | return_NULL; /* 'new_cn' released with mem pool */ | |
1375 | ||
1376 | return new_cn; | |
1377 | } | |
1378 | ||
1379 | struct dm_config_node *dm_config_clone_node(struct dm_config_tree *cft, const struct dm_config_node *node, int sib) | |
1380 | { | |
845b1df6 | 1381 | return dm_config_clone_node_with_mem(cft->mem, node, sib); |
e59e2f7c PR |
1382 | } |
1383 | ||
1384 | struct dm_config_node *dm_config_create_node(struct dm_config_tree *cft, const char *key) | |
1385 | { | |
e59e2f7c PR |
1386 | struct dm_config_node *cn; |
1387 | ||
845b1df6 | 1388 | if (!(cn = _create_node(cft->mem))) { |
e59e2f7c PR |
1389 | log_error("Failed to create config node."); |
1390 | return NULL; | |
1391 | } | |
845b1df6 | 1392 | if (!(cn->key = dm_pool_strdup(cft->mem, key))) { |
e59e2f7c PR |
1393 | log_error("Failed to create config node's key."); |
1394 | return NULL; | |
1395 | } | |
e59e2f7c | 1396 | cn->parent = NULL; |
847e2856 PR |
1397 | cn->v = NULL; |
1398 | ||
e59e2f7c PR |
1399 | return cn; |
1400 | } | |
1401 | ||
1402 | struct dm_config_value *dm_config_create_value(struct dm_config_tree *cft) | |
1403 | { | |
845b1df6 | 1404 | return _create_value(cft->mem); |
e59e2f7c PR |
1405 | } |
1406 | ||
9465963f PR |
1407 | void dm_config_value_set_format_flags(struct dm_config_value *cv, uint32_t format_flags) |
1408 | { | |
1409 | if (!cv) | |
1410 | return; | |
1411 | ||
1412 | cv->format_flags = format_flags; | |
1413 | } | |
1414 | ||
1415 | uint32_t dm_config_value_get_format_flags(struct dm_config_value *cv) | |
1416 | { | |
1417 | if (!cv) | |
1418 | return 0; | |
1419 | ||
1420 | return cv->format_flags; | |
1421 | } | |
1422 | ||
e59e2f7c PR |
1423 | struct dm_pool *dm_config_memory(struct dm_config_tree *cft) |
1424 | { | |
845b1df6 | 1425 | return cft->mem; |
e59e2f7c | 1426 | } |
274a7a68 PR |
1427 | |
1428 | static int _override_path(const char *path, struct dm_config_node *node, void *baton) | |
1429 | { | |
1430 | struct dm_config_tree *cft = baton; | |
1431 | struct dm_config_node dummy, *target; | |
1432 | dummy.child = cft->root; | |
7563e69c | 1433 | if (!(target = _find_or_make_node(cft->mem, &dummy, path, 0))) |
274a7a68 PR |
1434 | return_0; |
1435 | if (!(target->v = _clone_config_value(cft->mem, node->v))) | |
1436 | return_0; | |
1437 | cft->root = dummy.child; | |
1438 | return 1; | |
1439 | } | |
1440 | ||
1441 | static int _enumerate(const char *path, struct dm_config_node *cn, int (*cb)(const char *, struct dm_config_node *, void *), void *baton) | |
1442 | { | |
1443 | char *sub = NULL; | |
1444 | ||
1445 | while (cn) { | |
1446 | if (dm_asprintf(&sub, "%s/%s", path, cn->key) < 0) | |
1447 | return_0; | |
1448 | if (cn->child) { | |
1449 | if (!_enumerate(sub, cn->child, cb, baton)) | |
1450 | goto_bad; | |
1451 | } else | |
1452 | if (!cb(sub, cn, baton)) | |
1453 | goto_bad; | |
1454 | dm_free(sub); | |
1455 | cn = cn->sib; | |
1456 | } | |
1457 | return 1; | |
1458 | bad: | |
1459 | dm_free(sub); | |
1460 | return 0; | |
1461 | } | |
1462 | ||
1463 | struct dm_config_tree *dm_config_flatten(struct dm_config_tree *cft) | |
1464 | { | |
1465 | struct dm_config_tree *res = dm_config_create(), *done = NULL, *current = NULL; | |
1466 | ||
1467 | if (!res) | |
1468 | return_NULL; | |
1469 | ||
1470 | while (done != cft) { | |
1471 | current = cft; | |
1472 | while (current->cascade != done) | |
1473 | current = current->cascade; | |
1474 | _enumerate("", current->root, _override_path, res); | |
1475 | done = current; | |
1476 | } | |
1477 | ||
1478 | return res; | |
1479 | } | |
de2c5ab2 | 1480 | |
ccdea661 | 1481 | int dm_config_remove_node(struct dm_config_node *parent, struct dm_config_node *rem_node) |
de2c5ab2 PR |
1482 | { |
1483 | struct dm_config_node *cn = parent->child, *last = NULL; | |
1484 | while (cn) { | |
ccdea661 | 1485 | if (cn == rem_node) { |
de2c5ab2 PR |
1486 | if (last) |
1487 | last->sib = cn->sib; | |
1488 | else | |
1489 | parent->child = cn->sib; | |
1490 | return 1; | |
1491 | } | |
1492 | last = cn; | |
1493 | cn = cn->sib; | |
1494 | } | |
1495 | return 0; | |
1496 | } |