Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 : : * Copyright (C) 2004-2007 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,
13 : : * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 : : */
15 : :
16 : : #include "lib.h"
17 : : #include "config.h"
18 : : #include "crc.h"
19 : : #include "device.h"
20 : : #include "str_list.h"
21 : : #include "toolcontext.h"
22 : : #include "lvm-string.h"
23 : : #include "lvm-file.h"
24 : :
25 : : #include <sys/stat.h>
26 : : #include <sys/mman.h>
27 : : #include <unistd.h>
28 : : #include <fcntl.h>
29 : : #include <ctype.h>
30 : :
31 : : #define SECTION_B_CHAR '{'
32 : : #define SECTION_E_CHAR '}'
33 : :
34 : : enum {
35 : : TOK_INT,
36 : : TOK_FLOAT,
37 : : TOK_STRING, /* Single quotes */
38 : : TOK_STRING_ESCAPED, /* Double quotes */
39 : : TOK_EQ,
40 : : TOK_SECTION_B,
41 : : TOK_SECTION_E,
42 : : TOK_ARRAY_B,
43 : : TOK_ARRAY_E,
44 : : TOK_IDENTIFIER,
45 : : TOK_COMMA,
46 : : TOK_EOF
47 : : };
48 : :
49 : : struct parser {
50 : : const char *fb, *fe; /* file limits */
51 : :
52 : : int t; /* token limits and type */
53 : : const char *tb, *te;
54 : :
55 : : int fd; /* descriptor for file being parsed */
56 : : int line; /* line number we are on */
57 : :
58 : : struct dm_pool *mem;
59 : : };
60 : :
61 : : struct cs {
62 : : struct config_tree cft;
63 : : struct dm_pool *mem;
64 : : time_t timestamp;
65 : : char *filename;
66 : : int exists;
67 : : int keep_open;
68 : : struct device *dev;
69 : : };
70 : :
71 : : struct output_line {
72 : : FILE *fp;
73 : : struct dm_pool *mem;
74 : : putline_fn putline;
75 : : void *putline_baton;
76 : : };
77 : :
78 : : static void _get_token(struct parser *p, int tok_prev);
79 : : static void _eat_space(struct parser *p);
80 : : static struct config_node *_file(struct parser *p);
81 : : static struct config_node *_section(struct parser *p);
82 : : static struct config_value *_value(struct parser *p);
83 : : static struct config_value *_type(struct parser *p);
84 : : static int _match_aux(struct parser *p, int t);
85 : : static struct config_value *_create_value(struct dm_pool *mem);
86 : : static struct config_node *_create_node(struct dm_pool *mem);
87 : : static char *_dup_tok(struct parser *p);
88 : :
89 : : static const int sep = '/';
90 : :
91 : : #define MAX_INDENT 32
92 : :
93 : : #define match(t) do {\
94 : : if (!_match_aux(p, (t))) {\
95 : : log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \
96 : : p->tb - p->fb + 1, p->line); \
97 : : return 0;\
98 : : } \
99 : : } while(0);
100 : :
101 : 2514 : static int _tok_match(const char *str, const char *b, const char *e)
102 : : {
103 [ + + ][ + + ]: 4547 : while (*str && (b != e)) {
104 [ + + ]: 4319 : if (*str++ != *b++)
105 : 2286 : return 0;
106 : : }
107 : :
108 [ + + ][ + + ]: 2514 : return !(*str || (b != e));
109 : : }
110 : :
111 : : /*
112 : : * public interface
113 : : */
114 : 8 : struct config_tree *create_config_tree(const char *filename, int keep_open)
115 : : {
116 : : struct cs *c;
117 : 8 : struct dm_pool *mem = dm_pool_create("config", 10 * 1024);
118 : :
119 [ - + ]: 8 : if (!mem) {
120 : 0 : log_error("Failed to allocate config pool.");
121 : 0 : return 0;
122 : : }
123 : :
124 [ - + ]: 8 : if (!(c = dm_pool_zalloc(mem, sizeof(*c)))) {
125 : 0 : log_error("Failed to allocate config tree.");
126 : 0 : dm_pool_destroy(mem);
127 : 0 : return 0;
128 : : }
129 : :
130 : 8 : c->mem = mem;
131 : 8 : c->cft.root = (struct config_node *) NULL;
132 : 8 : c->timestamp = 0;
133 : 8 : c->exists = 0;
134 : 8 : c->keep_open = keep_open;
135 : 8 : c->dev = 0;
136 [ + + ]: 8 : if (filename)
137 : 4 : c->filename = dm_pool_strdup(c->mem, filename);
138 : 8 : return &c->cft;
139 : : }
140 : :
141 : 8 : void destroy_config_tree(struct config_tree *cft)
142 : : {
143 : 8 : struct cs *c = (struct cs *) cft;
144 : :
145 [ + + ]: 8 : if (c->dev)
146 : 1 : dev_close(c->dev);
147 : :
148 : 8 : dm_pool_destroy(c->mem);
149 : 8 : }
150 : :
151 : 5 : static int _parse_config_file(struct parser *p, struct config_tree *cft)
152 : : {
153 : 5 : p->tb = p->te = p->fb;
154 : 5 : p->line = 1;
155 : 5 : _get_token(p, TOK_SECTION_E);
156 [ - + ]: 5 : if (!(cft->root = _file(p)))
157 : 0 : return_0;
158 : :
159 : 5 : return 1;
160 : : }
161 : :
162 : 1 : struct config_tree *create_config_tree_from_string(struct cmd_context *cmd __attribute((unused)),
163 : : const char *config_settings)
164 : : {
165 : : struct cs *c;
166 : : struct config_tree *cft;
167 : : struct parser *p;
168 : :
169 [ - + ]: 1 : if (!(cft = create_config_tree(NULL, 0)))
170 : 0 : return_NULL;
171 : :
172 : 1 : c = (struct cs *) cft;
173 [ - + ]: 1 : if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) {
174 : 0 : log_error("Failed to allocate config tree parser.");
175 : 0 : destroy_config_tree(cft);
176 : 0 : return NULL;
177 : : }
178 : :
179 : 1 : p->mem = c->mem;
180 : 1 : p->fb = config_settings;
181 : 1 : p->fe = config_settings + strlen(config_settings);
182 : :
183 [ - + ]: 1 : if (!_parse_config_file(p, cft)) {
184 : 0 : destroy_config_tree(cft);
185 : 0 : return_NULL;
186 : : }
187 : :
188 : 1 : return cft;
189 : : }
190 : :
191 : 1 : int override_config_tree_from_string(struct cmd_context *cmd,
192 : : const char *config_settings)
193 : : {
194 [ - + ]: 1 : if (!(cmd->cft_override = create_config_tree_from_string(cmd,config_settings))) {
195 : 0 : log_error("Failed to set overridden configuration entries.");
196 : 0 : return 1;
197 : : }
198 : :
199 : 1 : return 0;
200 : : }
201 : :
202 : 4 : int read_config_fd(struct config_tree *cft, struct device *dev,
203 : : off_t offset, size_t size, off_t offset2, size_t size2,
204 : : checksum_fn_t checksum_fn, uint32_t checksum)
205 : : {
206 : 4 : struct cs *c = (struct cs *) cft;
207 : : struct parser *p;
208 : 4 : int r = 0;
209 : 4 : int use_mmap = 1;
210 : 4 : off_t mmap_offset = 0;
211 : 4 : char *buf = NULL;
212 : :
213 [ - + ]: 4 : if (!(p = dm_pool_alloc(c->mem, sizeof(*p))))
214 : 0 : return_0;
215 : 4 : p->mem = c->mem;
216 : :
217 : : /* Only use mmap with regular files */
218 [ + - ][ - + ]: 4 : if (!(dev->flags & DEV_REGULAR) || size2)
219 : 0 : use_mmap = 0;
220 : :
221 [ + - ]: 4 : if (use_mmap) {
222 : 4 : mmap_offset = offset % lvm_getpagesize();
223 : : /* memory map the file */
224 : 4 : p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ,
225 : : MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
226 [ - + ]: 4 : if (p->fb == (caddr_t) (-1)) {
227 : 0 : log_sys_error("mmap", dev_name(dev));
228 : 0 : goto out;
229 : : }
230 : 4 : p->fb = p->fb + mmap_offset;
231 : : } else {
232 [ # # ]: 0 : if (!(buf = dm_malloc(size + size2)))
233 : 0 : return_0;
234 [ # # ]: 0 : if (!dev_read_circular(dev, (uint64_t) offset, size,
235 : : (uint64_t) offset2, size2, buf)) {
236 : 0 : goto out;
237 : : }
238 : 0 : p->fb = buf;
239 : : }
240 : :
241 [ - + # # ]: 4 : if (checksum_fn && checksum !=
242 : 0 : (checksum_fn(checksum_fn(INITIAL_CRC, p->fb, size),
243 : : p->fb + size, size2))) {
244 : 0 : log_error("%s: Checksum error", dev_name(dev));
245 : 0 : goto out;
246 : : }
247 : :
248 : 4 : p->fe = p->fb + size + size2;
249 : :
250 [ - + ]: 4 : if (!_parse_config_file(p, cft))
251 : 0 : goto_out;
252 : :
253 : 4 : r = 1;
254 : :
255 : : out:
256 [ - + ]: 4 : if (!use_mmap)
257 : 0 : dm_free(buf);
258 : : else {
259 : : /* unmap the file */
260 [ - + ]: 4 : if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) {
261 : 0 : log_sys_error("munmap", dev_name(dev));
262 : 0 : r = 0;
263 : : }
264 : : }
265 : :
266 : 4 : return r;
267 : : }
268 : :
269 : 4 : int read_config_file(struct config_tree *cft)
270 : : {
271 : 4 : struct cs *c = (struct cs *) cft;
272 : : struct stat info;
273 : 4 : int r = 1;
274 : :
275 [ - + ]: 4 : if (stat(c->filename, &info)) {
276 : 0 : log_sys_error("stat", c->filename);
277 : 0 : c->exists = 0;
278 : 0 : return 0;
279 : : }
280 : :
281 [ - + ]: 4 : if (!S_ISREG(info.st_mode)) {
282 : 0 : log_error("%s is not a regular file", c->filename);
283 : 0 : c->exists = 0;
284 : 0 : return 0;
285 : : }
286 : :
287 : 4 : c->exists = 1;
288 : :
289 [ - + ]: 4 : if (info.st_size == 0) {
290 : 0 : log_verbose("%s is empty", c->filename);
291 : 0 : return 1;
292 : : }
293 : :
294 [ + - ]: 4 : if (!c->dev) {
295 [ - + ]: 4 : if (!(c->dev = dev_create_file(c->filename, NULL, NULL, 1)))
296 : 0 : return_0;
297 : :
298 [ - + ]: 4 : if (!dev_open_flags(c->dev, O_RDONLY, 0, 0)) {
299 : 0 : c->dev = 0;
300 : 0 : return_0;
301 : : }
302 : : }
303 : :
304 : 4 : r = read_config_fd(cft, c->dev, 0, (size_t) info.st_size, 0, 0,
305 : : (checksum_fn_t) NULL, 0);
306 : :
307 [ + + ]: 4 : if (!c->keep_open) {
308 : 3 : dev_close(c->dev);
309 : 3 : c->dev = 0;
310 : : }
311 : :
312 : 4 : c->timestamp = info.st_ctime;
313 : :
314 : 4 : return r;
315 : : }
316 : :
317 : 1 : time_t config_file_timestamp(struct config_tree *cft)
318 : : {
319 : 1 : struct cs *c = (struct cs *) cft;
320 : :
321 : 1 : return c->timestamp;
322 : : }
323 : :
324 : : /*
325 : : * Return 1 if config files ought to be reloaded
326 : : */
327 : 0 : int config_file_changed(struct config_tree *cft)
328 : : {
329 : 0 : struct cs *c = (struct cs *) cft;
330 : : struct stat info;
331 : :
332 [ # # ]: 0 : if (!c->filename)
333 : 0 : return 0;
334 : :
335 [ # # ]: 0 : if (stat(c->filename, &info) == -1) {
336 : : /* Ignore a deleted config file: still use original data */
337 [ # # ]: 0 : if (errno == ENOENT) {
338 [ # # ]: 0 : if (!c->exists)
339 : 0 : return 0;
340 : 0 : log_very_verbose("Config file %s has disappeared!",
341 : : c->filename);
342 : 0 : goto reload;
343 : : }
344 : 0 : log_sys_error("stat", c->filename);
345 : 0 : log_error("Failed to reload configuration files");
346 : 0 : return 0;
347 : : }
348 : :
349 [ # # ]: 0 : if (!S_ISREG(info.st_mode)) {
350 : 0 : log_error("Configuration file %s is not a regular file",
351 : : c->filename);
352 : 0 : goto reload;
353 : : }
354 : :
355 : : /* Unchanged? */
356 [ # # ]: 0 : if (c->timestamp == info.st_ctime)
357 : 0 : return 0;
358 : :
359 : : reload:
360 : 0 : log_verbose("Detected config file change to %s", c->filename);
361 : 0 : return 1;
362 : : }
363 : :
364 : 0 : static int _line_start(struct output_line *outline)
365 : : {
366 [ # # ]: 0 : if (!dm_pool_begin_object(outline->mem, 128)) {
367 : 0 : log_error("dm_pool_begin_object failed for config line");
368 : 0 : return 0;
369 : : }
370 : :
371 : 0 : return 1;
372 : : }
373 : :
374 : : static int _line_append(struct output_line *outline, const char *fmt, ...)
375 : : __attribute__ ((format(printf, 2, 3)));
376 : 0 : static int _line_append(struct output_line *outline, const char *fmt, ...)
377 : : {
378 : : char buf[4096];
379 : : va_list ap;
380 : : int n;
381 : :
382 : 0 : va_start(ap, fmt);
383 : 0 : n = vsnprintf(&buf[0], sizeof buf - 1, fmt, ap);
384 [ # # # # ]: 0 : if (n < 0 || n > (int) sizeof buf - 1) {
385 : 0 : log_error("vsnprintf failed for config line");
386 : 0 : return 0;
387 : : }
388 : 0 : va_end(ap);
389 : :
390 [ # # ]: 0 : if (!dm_pool_grow_object(outline->mem, &buf[0], strlen(buf))) {
391 : 0 : log_error("dm_pool_grow_object failed for config line");
392 : 0 : return 0;
393 : : }
394 : :
395 : 0 : return 1;
396 : : }
397 : :
398 : : #define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0)
399 : :
400 : 0 : static int _line_end(struct output_line *outline)
401 : : {
402 : : const char *line;
403 : :
404 [ # # ]: 0 : if (!dm_pool_grow_object(outline->mem, "\0", 1)) {
405 : 0 : log_error("dm_pool_grow_object failed for config line");
406 : 0 : return 0;
407 : : }
408 : :
409 : 0 : line = dm_pool_end_object(outline->mem);
410 [ # # ]: 0 : if (outline->putline)
411 : 0 : outline->putline(line, outline->putline_baton);
412 : : else {
413 [ # # ]: 0 : if (!outline->fp)
414 : 0 : log_print("%s", line);
415 : : else
416 : 0 : fprintf(outline->fp, "%s\n", line);
417 : : }
418 : :
419 : 0 : return 1;
420 : : }
421 : :
422 : 0 : static int _write_value(struct output_line *outline, struct config_value *v)
423 : : {
424 : : char *buf;
425 : :
426 [ # # # # : 0 : switch (v->type) {
# ]
427 : : case CFG_STRING:
428 : 0 : if (!(buf = alloca(escaped_len(v->v.str)))) {
429 : : log_error("temporary stack allocation for a config "
430 : : "string failed");
431 : : return 0;
432 : : }
433 [ # # ]: 0 : line_append("\"%s\"", escape_double_quotes(buf, v->v.str));
434 : 0 : break;
435 : :
436 : : case CFG_FLOAT:
437 [ # # ]: 0 : line_append("%f", v->v.r);
438 : 0 : break;
439 : :
440 : : case CFG_INT:
441 [ # # ]: 0 : line_append("%" PRId64, v->v.i);
442 : 0 : break;
443 : :
444 : : case CFG_EMPTY_ARRAY:
445 [ # # ]: 0 : line_append("[]");
446 : 0 : break;
447 : :
448 : : default:
449 : 0 : log_error("_write_value: Unknown value type: %d", v->type);
450 : :
451 : : }
452 : :
453 : 0 : return 1;
454 : : }
455 : :
456 : 0 : static int _write_config(const struct config_node *n, int only_one,
457 : : struct output_line *outline, int level)
458 : : {
459 : : char space[MAX_INDENT + 1];
460 : 0 : int l = (level < MAX_INDENT) ? level : MAX_INDENT;
461 : : int i;
462 : :
463 [ # # ]: 0 : if (!n)
464 : 0 : return 1;
465 : :
466 [ # # ]: 0 : for (i = 0; i < l; i++)
467 : 0 : space[i] = '\t';
468 : 0 : space[i] = '\0';
469 : :
470 : : do {
471 [ # # ]: 0 : if (!_line_start(outline))
472 : 0 : return_0;
473 [ # # ]: 0 : line_append("%s%s", space, n->key);
474 [ # # ]: 0 : if (!n->v) {
475 : : /* it's a sub section */
476 [ # # ]: 0 : line_append(" {");
477 [ # # ]: 0 : if (!_line_end(outline))
478 : 0 : return_0;
479 : 0 : _write_config(n->child, 0, outline, level + 1);
480 [ # # ]: 0 : if (!_line_start(outline))
481 : 0 : return_0;
482 [ # # ]: 0 : line_append("%s}", space);
483 : : } else {
484 : : /* it's a value */
485 : 0 : struct config_value *v = n->v;
486 [ # # ]: 0 : line_append("=");
487 [ # # ]: 0 : if (v->next) {
488 [ # # ]: 0 : line_append("[");
489 [ # # ]: 0 : while (v) {
490 [ # # ]: 0 : if (!_write_value(outline, v))
491 : 0 : return_0;
492 : 0 : v = v->next;
493 [ # # ]: 0 : if (v)
494 [ # # ]: 0 : line_append(", ");
495 : : }
496 [ # # ]: 0 : line_append("]");
497 : : } else
498 [ # # ]: 0 : if (!_write_value(outline, v))
499 : 0 : return_0;
500 : : }
501 [ # # ]: 0 : if (!_line_end(outline))
502 : 0 : return_0;
503 : 0 : n = n->sib;
504 [ # # ][ # # ]: 0 : } while (n && !only_one);
505 : : /* FIXME: add error checking */
506 : 0 : return 1;
507 : : }
508 : :
509 : 0 : int write_config_node(const struct config_node *cn, putline_fn putline, void *baton)
510 : : {
511 : : struct output_line outline;
512 : 0 : outline.fp = NULL;
513 : 0 : outline.mem = dm_pool_create("config_line", 1024);
514 : 0 : outline.putline = putline;
515 : 0 : outline.putline_baton = baton;
516 [ # # ]: 0 : if (!_write_config(cn, 0, &outline, 0)) {
517 : 0 : dm_pool_destroy(outline.mem);
518 : 0 : return_0;
519 : : }
520 : 0 : dm_pool_destroy(outline.mem);
521 : 0 : return 1;
522 : : }
523 : :
524 : 0 : int write_config_file(struct config_tree *cft, const char *file,
525 : : int argc, char **argv)
526 : : {
527 : : struct config_node *cn;
528 : 0 : int r = 1;
529 : : struct output_line outline;
530 : 0 : outline.fp = NULL;
531 : 0 : outline.putline = NULL;
532 : :
533 [ # # ]: 0 : if (!file)
534 : 0 : file = "stdout";
535 [ # # ]: 0 : else if (!(outline.fp = fopen(file, "w"))) {
536 : 0 : log_sys_error("open", file);
537 : 0 : return 0;
538 : : }
539 : :
540 : 0 : outline.mem = dm_pool_create("config_line", 1024);
541 : :
542 : 0 : log_verbose("Dumping configuration to %s", file);
543 [ # # ]: 0 : if (!argc) {
544 [ # # ]: 0 : if (!_write_config(cft->root, 0, &outline, 0)) {
545 : 0 : log_error("Failure while writing to %s", file);
546 : 0 : r = 0;
547 : : }
548 [ # # ]: 0 : } else while (argc--) {
549 [ # # ]: 0 : if ((cn = find_config_node(cft->root, *argv))) {
550 [ # # ]: 0 : if (!_write_config(cn, 1, &outline, 0)) {
551 : 0 : log_error("Failure while writing to %s", file);
552 : 0 : r = 0;
553 : : }
554 : : } else {
555 : 0 : log_error("Configuration node %s not found", *argv);
556 : 0 : r = 0;
557 : : }
558 : 0 : argv++;
559 : : }
560 : :
561 [ # # ][ # # ]: 0 : if (outline.fp && lvm_fclose(outline.fp, file)) {
562 : 0 : stack;
563 : 0 : r = 0;
564 : : }
565 : :
566 : 0 : dm_pool_destroy(outline.mem);
567 : 0 : return r;
568 : : }
569 : :
570 : : /*
571 : : * parser
572 : : */
573 : 5 : static struct config_node *_file(struct parser *p)
574 : : {
575 : 5 : struct config_node *root = NULL, *n, *l = NULL;
576 [ + + ]: 31 : while (p->t != TOK_EOF) {
577 [ - + ]: 26 : if (!(n = _section(p)))
578 : 0 : return_0;
579 : :
580 [ + + ]: 26 : if (!root)
581 : 5 : root = n;
582 : : else
583 : 21 : l->sib = n;
584 : 26 : n->parent = root;
585 : 26 : l = n;
586 : : }
587 : 5 : return root;
588 : : }
589 : :
590 : 157 : static struct config_node *_section(struct parser *p)
591 : : {
592 : : /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
593 : 157 : struct config_node *root, *n, *l = NULL;
594 [ - + ]: 157 : if (!(root = _create_node(p->mem)))
595 : 0 : return_0;
596 : :
597 [ - + ]: 157 : if (!(root->key = _dup_tok(p)))
598 : 0 : return_0;
599 : :
600 [ - + ]: 157 : match(TOK_IDENTIFIER);
601 : :
602 [ + + ]: 157 : if (p->t == TOK_SECTION_B) {
603 [ - + ]: 20 : match(TOK_SECTION_B);
604 [ + + ]: 151 : while (p->t != TOK_SECTION_E) {
605 [ - + ]: 131 : if (!(n = _section(p)))
606 : 0 : return_0;
607 : :
608 [ + + ]: 131 : if (!root->child)
609 : 20 : root->child = n;
610 : : else
611 : 111 : l->sib = n;
612 : 131 : n->parent = root;
613 : 131 : l = n;
614 : : }
615 [ - + ]: 20 : match(TOK_SECTION_E);
616 : : } else {
617 [ - + ]: 137 : match(TOK_EQ);
618 [ - + ]: 137 : if (!(root->v = _value(p)))
619 : 0 : return_0;
620 : : }
621 : :
622 : 157 : return root;
623 : : }
624 : :
625 : 137 : static struct config_value *_value(struct parser *p)
626 : : {
627 : : /* '[' TYPE* ']' | TYPE */
628 : 137 : struct config_value *h = NULL, *l, *ll = NULL;
629 [ + + ]: 137 : if (p->t == TOK_ARRAY_B) {
630 [ - + ]: 14 : match(TOK_ARRAY_B);
631 [ + + ]: 356 : while (p->t != TOK_ARRAY_E) {
632 [ - + ]: 342 : if (!(l = _type(p)))
633 : 0 : return_0;
634 : :
635 [ + + ]: 342 : if (!h)
636 : 11 : h = l;
637 : : else
638 : 331 : ll->next = l;
639 : 342 : ll = l;
640 : :
641 [ + + ]: 342 : if (p->t == TOK_COMMA)
642 [ - + ]: 331 : match(TOK_COMMA);
643 : : }
644 [ - + ]: 14 : match(TOK_ARRAY_E);
645 : : /*
646 : : * Special case for an empty array.
647 : : */
648 [ + + ]: 14 : if (!h) {
649 [ - + ]: 3 : if (!(h = _create_value(p->mem)))
650 : 0 : return NULL;
651 : :
652 : 14 : h->type = CFG_EMPTY_ARRAY;
653 : : }
654 : :
655 : : } else
656 : 123 : h = _type(p);
657 : :
658 : 137 : return h;
659 : : }
660 : :
661 : 465 : static struct config_value *_type(struct parser *p)
662 : : {
663 : : /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
664 : 465 : struct config_value *v = _create_value(p->mem);
665 : :
666 [ - + ]: 465 : if (!v)
667 : 0 : return NULL;
668 : :
669 [ + - - + : 465 : switch (p->t) {
- ]
670 : : case TOK_INT:
671 : 84 : v->type = CFG_INT;
672 : 84 : v->v.i = strtoll(p->tb, NULL, 0); /* FIXME: check error */
673 [ - + ]: 84 : match(TOK_INT);
674 : 84 : break;
675 : :
676 : : case TOK_FLOAT:
677 : 0 : v->type = CFG_FLOAT;
678 : 0 : v->v.r = strtod(p->tb, NULL); /* FIXME: check error */
679 [ # # ]: 0 : match(TOK_FLOAT);
680 : 0 : break;
681 : :
682 : : case TOK_STRING:
683 : 0 : v->type = CFG_STRING;
684 : :
685 : 0 : p->tb++, p->te--; /* strip "'s */
686 [ # # ]: 0 : if (!(v->v.str = _dup_tok(p)))
687 : 0 : return_0;
688 : 0 : p->te++;
689 [ # # ]: 0 : match(TOK_STRING);
690 : 0 : break;
691 : :
692 : : case TOK_STRING_ESCAPED:
693 : 381 : v->type = CFG_STRING;
694 : :
695 : 381 : p->tb++, p->te--; /* strip "'s */
696 [ - + ]: 381 : if (!(v->v.str = _dup_tok(p)))
697 : 0 : return_0;
698 : 381 : unescape_double_quotes(v->v.str);
699 : 381 : p->te++;
700 [ - + ]: 381 : match(TOK_STRING_ESCAPED);
701 : 381 : break;
702 : :
703 : : default:
704 : 0 : log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value",
705 : : p->tb - p->fb + 1, p->line);
706 : 0 : return 0;
707 : : }
708 : 465 : return v;
709 : : }
710 : :
711 : 1158 : static int _match_aux(struct parser *p, int t)
712 : : {
713 [ - + ]: 1158 : if (p->t != t)
714 : 0 : return 0;
715 : :
716 : 1158 : _get_token(p, t);
717 : 1158 : return 1;
718 : : }
719 : :
720 : : /*
721 : : * tokeniser
722 : : */
723 : 1163 : static void _get_token(struct parser *p, int tok_prev)
724 : : {
725 : 1163 : int values_allowed = 0;
726 : :
727 : 1163 : p->tb = p->te;
728 : 1163 : _eat_space(p);
729 [ + + - + ]: 1163 : if (p->tb == p->fe || !*p->tb) {
730 : 5 : p->t = TOK_EOF;
731 : 5 : return;
732 : : }
733 : :
734 : : /* Should next token be interpreted as value instead of identifier? */
735 [ + + ][ + + ]: 1158 : if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B ||
[ + + ]
736 : : tok_prev == TOK_COMMA)
737 : 482 : values_allowed = 1;
738 : :
739 : 1158 : p->t = TOK_INT; /* fudge so the fall through for
740 : : floats works */
741 [ + + + + + : 1158 : switch (*p->te) {
+ + - - +
+ ]
742 : : case SECTION_B_CHAR:
743 : 20 : p->t = TOK_SECTION_B;
744 : 20 : p->te++;
745 : 20 : break;
746 : :
747 : : case SECTION_E_CHAR:
748 : 20 : p->t = TOK_SECTION_E;
749 : 20 : p->te++;
750 : 20 : break;
751 : :
752 : : case '[':
753 : 14 : p->t = TOK_ARRAY_B;
754 : 14 : p->te++;
755 : 14 : break;
756 : :
757 : : case ']':
758 : 14 : p->t = TOK_ARRAY_E;
759 : 14 : p->te++;
760 : 14 : break;
761 : :
762 : : case ',':
763 : 331 : p->t = TOK_COMMA;
764 : 331 : p->te++;
765 : 331 : break;
766 : :
767 : : case '=':
768 : 137 : p->t = TOK_EQ;
769 : 137 : p->te++;
770 : 137 : break;
771 : :
772 : : case '"':
773 : 381 : p->t = TOK_STRING_ESCAPED;
774 : 381 : p->te++;
775 [ + - ][ + - ]: 5273 : while ((p->te != p->fe) && (*p->te) && (*p->te != '"')) {
[ + + ]
776 [ - + ][ # # ]: 4892 : if ((*p->te == '\\') && (p->te + 1 != p->fe) &&
[ # # ]
777 : 0 : *(p->te + 1))
778 : 0 : p->te++;
779 : 4892 : p->te++;
780 : : }
781 : :
782 [ + - ][ + - ]: 381 : if ((p->te != p->fe) && (*p->te))
783 : 381 : p->te++;
784 : 381 : break;
785 : :
786 : : case '\'':
787 : 0 : p->t = TOK_STRING;
788 : 0 : p->te++;
789 [ # # ][ # # ]: 0 : while ((p->te != p->fe) && (*p->te) && (*p->te != '\''))
[ # # ]
790 : 0 : p->te++;
791 : :
792 [ # # ][ # # ]: 0 : if ((p->te != p->fe) && (*p->te))
793 : 0 : p->te++;
794 : 0 : break;
795 : :
796 : : case '.':
797 : 0 : p->t = TOK_FLOAT;
798 : : case '0':
799 : : case '1':
800 : : case '2':
801 : : case '3':
802 : : case '4':
803 : : case '5':
804 : : case '6':
805 : : case '7':
806 : : case '8':
807 : : case '9':
808 : : case '+':
809 : : case '-':
810 [ + - ]: 84 : if (values_allowed) {
811 : 84 : p->te++;
812 [ + - ][ + - ]: 129 : while ((p->te != p->fe) && (*p->te)) {
813 [ - + ]: 129 : if (*p->te == '.') {
814 [ # # ]: 0 : if (p->t == TOK_FLOAT)
815 : 0 : break;
816 : 0 : p->t = TOK_FLOAT;
817 [ + + ]: 129 : } else if (!isdigit((int) *p->te))
818 : 84 : break;
819 : 45 : p->te++;
820 : : }
821 : 84 : break;
822 : : }
823 : :
824 : : default:
825 : 157 : p->t = TOK_IDENTIFIER;
826 [ + - ][ + - ]: 2976 : while ((p->te != p->fe) && (*p->te) && !isspace(*p->te) &&
[ + + ][ + - ]
[ + + ][ + - ]
[ + - ]
827 : 3628 : (*p->te != '#') && (*p->te != '=') &&
828 : 1813 : (*p->te != SECTION_B_CHAR) &&
829 : 1813 : (*p->te != SECTION_E_CHAR))
830 : 1813 : p->te++;
831 : : break;
832 : : }
833 : : }
834 : :
835 : 1163 : static void _eat_space(struct parser *p)
836 : : {
837 [ + + ][ + - ]: 4726 : while ((p->tb != p->fe) && (*p->tb)) {
838 [ + + ]: 3563 : if (*p->te == '#')
839 [ + - ][ + - ]: 40416 : while ((p->te != p->fe) && (*p->te) && (*p->te != '\n'))
[ + + ]
840 : 39623 : p->te++;
841 : :
842 [ + + ]: 2770 : else if (isspace(*p->te)) {
843 [ + + ][ + - ]: 6910 : while ((p->te != p->fe) && (*p->te) && isspace(*p->te)) {
[ + + ]
844 [ + + ]: 5298 : if (*p->te == '\n')
845 : 1225 : p->line++;
846 : 5298 : p->te++;
847 : : }
848 : : }
849 : :
850 : : else
851 : 1158 : return;
852 : :
853 : 2405 : p->tb = p->te;
854 : : }
855 : : }
856 : :
857 : : /*
858 : : * memory management
859 : : */
860 : 468 : static struct config_value *_create_value(struct dm_pool *mem)
861 : : {
862 : 468 : struct config_value *v = dm_pool_alloc(mem, sizeof(*v));
863 : :
864 [ + - ]: 468 : if (v)
865 : 468 : memset(v, 0, sizeof(*v));
866 : :
867 : 468 : return v;
868 : : }
869 : :
870 : 157 : static struct config_node *_create_node(struct dm_pool *mem)
871 : : {
872 : 157 : struct config_node *n = dm_pool_alloc(mem, sizeof(*n));
873 : :
874 [ + - ]: 157 : if (n)
875 : 157 : memset(n, 0, sizeof(*n));
876 : :
877 : 157 : return n;
878 : : }
879 : :
880 : 538 : static char *_dup_tok(struct parser *p)
881 : : {
882 : 538 : size_t len = p->te - p->tb;
883 : 538 : char *str = dm_pool_alloc(p->mem, len + 1);
884 [ - + ]: 538 : if (!str)
885 : 0 : return_0;
886 : 538 : strncpy(str, p->tb, len);
887 : 538 : str[len] = '\0';
888 : 538 : return str;
889 : : }
890 : :
891 : : /*
892 : : * utility functions
893 : : */
894 : 247 : static struct config_node *_find_config_node(const struct config_node *cn,
895 : : const char *path)
896 : : {
897 : : const char *e;
898 : 247 : const struct config_node *cn_found = NULL;
899 : :
900 [ + + ]: 387 : while (cn) {
901 : : /* trim any leading slashes */
902 [ + - ][ + + ]: 497 : while (*path && (*path == sep))
903 : 140 : path++;
904 : :
905 : : /* find the end of this segment */
906 [ + + ][ + + ]: 3183 : for (e = path; *e && (*e != sep); e++) ;
907 : :
908 : : /* hunt for the node */
909 : 357 : cn_found = NULL;
910 [ + + ]: 2871 : while (cn) {
911 [ + + ]: 2514 : if (_tok_match(cn->key, path, e)) {
912 : : /* Inefficient */
913 [ + - ]: 224 : if (!cn_found)
914 : 224 : cn_found = cn;
915 : : else
916 : 0 : log_warn("WARNING: Ignoring duplicate"
917 : : " config node: %s ("
918 : : "seeking %s)", cn->key, path);
919 : : }
920 : :
921 : 2514 : cn = cn->sib;
922 : : }
923 : :
924 [ + + ][ + + ]: 357 : if (cn_found && *e)
925 : 140 : cn = cn_found->child;
926 : : else
927 : : break; /* don't move into the last node */
928 : :
929 : 140 : path = e;
930 : : }
931 : :
932 : 247 : return (struct config_node *) cn_found;
933 : : }
934 : :
935 : 143 : static struct config_node *_find_first_config_node(const struct config_node *cn1,
936 : : const struct config_node *cn2,
937 : : const char *path)
938 : : {
939 : : struct config_node *cn;
940 : :
941 [ + + ][ + + ]: 143 : if (cn1 && (cn = _find_config_node(cn1, path)))
942 : 1 : return cn;
943 : :
944 [ + - ][ + + ]: 142 : if (cn2 && (cn = _find_config_node(cn2, path)))
945 : 82 : return cn;
946 : :
947 : 143 : return NULL;
948 : : }
949 : :
950 : 52 : struct config_node *find_config_node(const struct config_node *cn,
951 : : const char *path)
952 : : {
953 : 52 : return _find_config_node(cn, path);
954 : : }
955 : :
956 : 39 : static const char *_find_config_str(const struct config_node *cn1,
957 : : const struct config_node *cn2,
958 : : const char *path, const char *fail)
959 : : {
960 : 39 : const struct config_node *n = _find_first_config_node(cn1, cn2, path);
961 : :
962 : : /* Empty strings are ignored */
963 [ + + + - ]: 39 : if ((n && n->v && n->v->type == CFG_STRING) && (*n->v->v.str)) {
[ + - ][ + + ]
964 : 21 : log_very_verbose("Setting %s to %s", path, n->v->v.str);
965 : 21 : return n->v->v.str;
966 : : }
967 : :
968 [ + + ]: 18 : if (fail)
969 : 9 : log_very_verbose("%s not found in config: defaulting to %s",
970 : : path, fail);
971 : 39 : return fail;
972 : : }
973 : :
974 : 0 : const char *find_config_str(const struct config_node *cn,
975 : : const char *path, const char *fail)
976 : : {
977 : 0 : return _find_config_str(cn, NULL, path, fail);
978 : : }
979 : :
980 : 65 : static int64_t _find_config_int64(const struct config_node *cn1,
981 : : const struct config_node *cn2,
982 : : const char *path, int64_t fail)
983 : : {
984 : 65 : const struct config_node *n = _find_first_config_node(cn1, cn2, path);
985 : :
986 [ + + + - ]: 65 : if (n && n->v && n->v->type == CFG_INT) {
[ + - ]
987 : 42 : log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i);
988 : 42 : return n->v->v.i;
989 : : }
990 : :
991 : 23 : log_very_verbose("%s not found in config: defaulting to %" PRId64,
992 : : path, fail);
993 : 65 : return fail;
994 : : }
995 : :
996 : 0 : int find_config_int(const struct config_node *cn, const char *path, int fail)
997 : : {
998 : : /* FIXME Add log_error message on overflow */
999 : 0 : return (int) _find_config_int64(cn, NULL, path, (int64_t) fail);
1000 : : }
1001 : :
1002 : 0 : static float _find_config_float(const struct config_node *cn1,
1003 : : const struct config_node *cn2,
1004 : : const char *path, float fail)
1005 : : {
1006 : 0 : const struct config_node *n = _find_first_config_node(cn1, cn2, path);
1007 : :
1008 [ # # # # ]: 0 : if (n && n->v && n->v->type == CFG_FLOAT) {
[ # # ]
1009 : 0 : log_very_verbose("Setting %s to %f", path, n->v->v.r);
1010 : 0 : return n->v->v.r;
1011 : : }
1012 : :
1013 : 0 : log_very_verbose("%s not found in config: defaulting to %f",
1014 : : path, fail);
1015 : :
1016 : 0 : return fail;
1017 : :
1018 : : }
1019 : :
1020 : 0 : float find_config_float(const struct config_node *cn, const char *path,
1021 : : float fail)
1022 : : {
1023 : 0 : return _find_config_float(cn, NULL, path, fail);
1024 : : }
1025 : :
1026 : 30 : struct config_node *find_config_tree_node(struct cmd_context *cmd,
1027 : : const char *path)
1028 : : {
1029 [ + + ]: 30 : return _find_first_config_node(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path);
1030 : : }
1031 : :
1032 : 39 : const char *find_config_tree_str(struct cmd_context *cmd,
1033 : : const char *path, const char *fail)
1034 : : {
1035 [ + + ]: 39 : return _find_config_str(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1036 : : }
1037 : :
1038 : 65 : int find_config_tree_int(struct cmd_context *cmd, const char *path,
1039 : : int fail)
1040 : : {
1041 : : /* FIXME Add log_error message on overflow */
1042 [ + + ]: 65 : return (int) _find_config_int64(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, (int64_t) fail);
1043 : : }
1044 : :
1045 : 0 : float find_config_tree_float(struct cmd_context *cmd, const char *path,
1046 : : float fail)
1047 : : {
1048 [ # # ]: 0 : return _find_config_float(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1049 : : }
1050 : :
1051 : 0 : static int _str_in_array(const char *str, const char * const values[])
1052 : : {
1053 : : int i;
1054 : :
1055 [ # # ]: 0 : for (i = 0; values[i]; i++)
1056 [ # # ]: 0 : if (!strcasecmp(str, values[i]))
1057 : 0 : return 1;
1058 : :
1059 : 0 : return 0;
1060 : : }
1061 : :
1062 : 0 : static int _str_to_bool(const char *str, int fail)
1063 : : {
1064 : 0 : const char * const _true_values[] = { "y", "yes", "on", "true", NULL };
1065 : 0 : const char * const _false_values[] = { "n", "no", "off", "false", NULL };
1066 : :
1067 [ # # ]: 0 : if (_str_in_array(str, _true_values))
1068 : 0 : return 1;
1069 : :
1070 [ # # ]: 0 : if (_str_in_array(str, _false_values))
1071 : 0 : return 0;
1072 : :
1073 : 0 : return fail;
1074 : : }
1075 : :
1076 : 9 : static int _find_config_bool(const struct config_node *cn1,
1077 : : const struct config_node *cn2,
1078 : : const char *path, int fail)
1079 : : {
1080 : 9 : const struct config_node *n = _find_first_config_node(cn1, cn2, path);
1081 : : struct config_value *v;
1082 : :
1083 [ + + ]: 9 : if (!n)
1084 : 1 : return fail;
1085 : :
1086 : 8 : v = n->v;
1087 : :
1088 [ + - - ]: 8 : switch (v->type) {
1089 : : case CFG_INT:
1090 : 8 : return v->v.i ? 1 : 0;
1091 : :
1092 : : case CFG_STRING:
1093 : 0 : return _str_to_bool(v->v.str, fail);
1094 : : }
1095 : :
1096 : 9 : return fail;
1097 : : }
1098 : :
1099 : 0 : int find_config_bool(const struct config_node *cn, const char *path, int fail)
1100 : : {
1101 : 0 : return _find_config_bool(cn, NULL, path, fail);
1102 : : }
1103 : :
1104 : 9 : int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail)
1105 : : {
1106 [ + + ]: 9 : return _find_config_bool(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1107 : : }
1108 : :
1109 : 0 : int get_config_uint32(const struct config_node *cn, const char *path,
1110 : : uint32_t *result)
1111 : : {
1112 : : const struct config_node *n;
1113 : :
1114 : 0 : n = find_config_node(cn, path);
1115 : :
1116 [ # # # # ]: 0 : if (!n || !n->v || n->v->type != CFG_INT)
[ # # ]
1117 : 0 : return 0;
1118 : :
1119 : 0 : *result = n->v->v.i;
1120 : 0 : return 1;
1121 : : }
1122 : :
1123 : 0 : int get_config_uint64(const struct config_node *cn, const char *path,
1124 : : uint64_t *result)
1125 : : {
1126 : : const struct config_node *n;
1127 : :
1128 : 0 : n = find_config_node(cn, path);
1129 : :
1130 [ # # # # ]: 0 : if (!n || !n->v || n->v->type != CFG_INT)
[ # # ]
1131 : 0 : return 0;
1132 : :
1133 : 0 : *result = (uint64_t) n->v->v.i;
1134 : 0 : return 1;
1135 : : }
1136 : :
1137 : 0 : int get_config_str(const struct config_node *cn, const char *path,
1138 : : char **result)
1139 : : {
1140 : : const struct config_node *n;
1141 : :
1142 : 0 : n = find_config_node(cn, path);
1143 : :
1144 [ # # # # ]: 0 : if (!n || !n->v || n->v->type != CFG_STRING)
[ # # ]
1145 : 0 : return 0;
1146 : :
1147 : 0 : *result = n->v->v.str;
1148 : 0 : return 1;
1149 : : }
1150 : :
1151 : : /* Insert cn2 after cn1 */
1152 : 24 : static void _insert_config_node(struct config_node **cn1,
1153 : : struct config_node *cn2)
1154 : : {
1155 [ + + ]: 24 : if (!*cn1) {
1156 : 3 : *cn1 = cn2;
1157 : 3 : cn2->sib = NULL;
1158 : : } else {
1159 : 21 : cn2->sib = (*cn1)->sib;
1160 : 21 : (*cn1)->sib = cn2;
1161 : : }
1162 : 24 : }
1163 : :
1164 : : /*
1165 : : * Merge section cn2 into section cn1 (which has the same name)
1166 : : * overwriting any existing cn1 nodes with matching names.
1167 : : */
1168 : 0 : static void _merge_section(struct config_node *cn1, struct config_node *cn2)
1169 : : {
1170 : : struct config_node *cn, *nextn, *oldn;
1171 : : struct config_value *cv;
1172 : :
1173 [ # # ]: 0 : for (cn = cn2->child; cn; cn = nextn) {
1174 : 0 : nextn = cn->sib;
1175 : :
1176 : : /* Skip "tags" */
1177 [ # # ]: 0 : if (!strcmp(cn->key, "tags"))
1178 : 0 : continue;
1179 : :
1180 : : /* Subsection? */
1181 [ # # ]: 0 : if (!cn->v)
1182 : : /* Ignore - we don't have any of these yet */
1183 : 0 : continue;
1184 : : /* Not already present? */
1185 [ # # ]: 0 : if (!(oldn = find_config_node(cn1->child, cn->key))) {
1186 : 0 : _insert_config_node(&cn1->child, cn);
1187 : 0 : continue;
1188 : : }
1189 : : /* Merge certain value lists */
1190 [ # # ][ # # ]: 0 : if ((!strcmp(cn1->key, "activation") &&
[ # # ][ # # ]
[ # # ]
1191 : 0 : !strcmp(cn->key, "volume_list")) ||
1192 : 0 : (!strcmp(cn1->key, "devices") &&
1193 : 0 : (!strcmp(cn->key, "filter") || !strcmp(cn->key, "types")))) {
1194 : 0 : cv = cn->v;
1195 [ # # ]: 0 : while (cv->next)
1196 : 0 : cv = cv->next;
1197 : 0 : cv->next = oldn->v;
1198 : : }
1199 : :
1200 : : /* Replace values */
1201 : 0 : oldn->v = cn->v;
1202 : : }
1203 : 0 : }
1204 : :
1205 : 0 : static int _match_host_tags(struct dm_list *tags, struct config_node *tn)
1206 : : {
1207 : : struct config_value *tv;
1208 : : const char *str;
1209 : :
1210 [ # # ]: 0 : for (tv = tn->v; tv; tv = tv->next) {
1211 [ # # ]: 0 : if (tv->type != CFG_STRING)
1212 : 0 : continue;
1213 : 0 : str = tv->v.str;
1214 [ # # ]: 0 : if (*str == '@')
1215 : 0 : str++;
1216 [ # # ]: 0 : if (!*str)
1217 : 0 : continue;
1218 [ # # ]: 0 : if (str_list_match_item(tags, str))
1219 : 0 : return 1;
1220 : : }
1221 : :
1222 : 0 : return 0;
1223 : : }
1224 : :
1225 : : /* Destructively merge a new config tree into an existing one */
1226 : 3 : int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft,
1227 : : struct config_tree *newdata)
1228 : : {
1229 : 3 : struct config_node *root = cft->root;
1230 : : struct config_node *cn, *nextn, *oldn, *tn, *cn2;
1231 : :
1232 [ + + ]: 27 : for (cn = newdata->root; cn; cn = nextn) {
1233 : 24 : nextn = cn->sib;
1234 : : /* Ignore tags section */
1235 [ - + ]: 24 : if (!strcmp(cn->key, "tags"))
1236 : 0 : continue;
1237 : : /* If there's a tags node, skip if host tags don't match */
1238 [ - + ]: 24 : if ((tn = find_config_node(cn->child, "tags"))) {
1239 [ # # ]: 0 : if (!_match_host_tags(&cmd->tags, tn))
1240 : 0 : continue;
1241 : : }
1242 [ + - ]: 24 : if (!(oldn = find_config_node(root, cn->key))) {
1243 : 24 : _insert_config_node(&cft->root, cn);
1244 : : /* Remove any "tags" nodes */
1245 [ + + ]: 153 : for (cn2 = cn->child; cn2; cn2 = cn2->sib) {
1246 [ - + ]: 129 : if (!strcmp(cn2->key, "tags")) {
1247 : 0 : cn->child = cn2->sib;
1248 : 0 : continue;
1249 : : }
1250 [ + + ][ - + ]: 129 : if (cn2->sib && !strcmp(cn2->sib->key, "tags")) {
1251 : 0 : cn2->sib = cn2->sib->sib;
1252 : 0 : continue;
1253 : : }
1254 : : }
1255 : 24 : continue;
1256 : : }
1257 : 0 : _merge_section(oldn, cn);
1258 : : }
1259 : :
1260 : 3 : return 1;
1261 : : }
1262 : :
1263 : : /*
1264 : : * Convert a token type to the char it represents.
1265 : : */
1266 : 0 : static char _token_type_to_char(int type)
1267 : : {
1268 [ # # # ]: 0 : switch (type) {
1269 : : case TOK_SECTION_B:
1270 : 0 : return SECTION_B_CHAR;
1271 : : case TOK_SECTION_E:
1272 : 0 : return SECTION_E_CHAR;
1273 : : default:
1274 : 0 : return 0;
1275 : : }
1276 : : }
1277 : :
1278 : : /*
1279 : : * Returns:
1280 : : * # of 'type' tokens in 'str'.
1281 : : */
1282 : 0 : static unsigned _count_tokens(const char *str, unsigned len, int type)
1283 : : {
1284 : : char c;
1285 : :
1286 : 0 : c = _token_type_to_char(type);
1287 : :
1288 : 0 : return count_chars(str, len, c);
1289 : : }
1290 : :
1291 : 0 : const char *config_parent_name(const struct config_node *n)
1292 : : {
1293 [ # # ]: 0 : return (n->parent ? n->parent->key : "(root)");
1294 : : }
1295 : : /*
1296 : : * Heuristic function to make a quick guess as to whether a text
1297 : : * region probably contains a valid config "section". (Useful for
1298 : : * scanning areas of the disk for old metadata.)
1299 : : * Config sections contain various tokens, may contain other sections
1300 : : * and strings, and are delimited by begin (type 'TOK_SECTION_B') and
1301 : : * end (type 'TOK_SECTION_E') tokens. As a quick heuristic, we just
1302 : : * count the number of begin and end tokens, and see if they are
1303 : : * non-zero and the counts match.
1304 : : * Full validation of the section should be done with another function
1305 : : * (for example, read_config_fd).
1306 : : *
1307 : : * Returns:
1308 : : * 0 - probably is not a valid config section
1309 : : * 1 - probably _is_ a valid config section
1310 : : */
1311 : 0 : unsigned maybe_config_section(const char *str, unsigned len)
1312 : : {
1313 : : int begin_count;
1314 : : int end_count;
1315 : :
1316 : 0 : begin_count = _count_tokens(str, len, TOK_SECTION_B);
1317 : 0 : end_count = _count_tokens(str, len, TOK_SECTION_E);
1318 : :
1319 [ # # # # ]: 0 : if (begin_count && end_count && (begin_count == end_count))
[ # # ]
1320 : 0 : return 1;
1321 : : else
1322 : 0 : return 0;
1323 : : }
1324 : :
1325 : 0 : static struct config_value *_clone_config_value(struct dm_pool *mem, const struct config_value *v)
1326 : : {
1327 [ # # ]: 0 : if (!v)
1328 : 0 : return NULL;
1329 : 0 : struct config_value *new = _create_value(mem);
1330 : 0 : new->type = v->type;
1331 [ # # ]: 0 : if (v->type == CFG_STRING)
1332 : 0 : new->v.str = dm_pool_strdup(mem, v->v.str);
1333 : : else
1334 : 0 : new->v = v->v;
1335 : 0 : new->next = _clone_config_value(mem, v->next);
1336 : 0 : return new;
1337 : : }
1338 : :
1339 : 0 : struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
1340 : : int siblings)
1341 : : {
1342 [ # # ]: 0 : if (!cn)
1343 : 0 : return NULL;
1344 : 0 : struct config_node *new = _create_node(mem);
1345 : 0 : new->key = dm_pool_strdup(mem, cn->key);
1346 : 0 : new->child = clone_config_node(mem, cn->child, 1);
1347 : 0 : new->v = _clone_config_value(mem, cn->v);
1348 [ # # ]: 0 : if (siblings)
1349 : 0 : new->sib = clone_config_node(mem, cn->sib, siblings);
1350 : : else
1351 : 0 : new->sib = NULL;
1352 : 0 : return new;
1353 : : }
|