1 /* $NetBSD: config.c,v 1.1.1.2 2009/12/02 00:26:28 haad Exp $ */
4 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
5 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
7 * This file is part of LVM2.
9 * This copyrighted material is made available to anyone wishing to use,
10 * modify, copy, or redistribute it subject to the terms and conditions
11 * of the GNU Lesser General Public License v.2.1.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software Foundation,
15 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "toolcontext.h"
24 #include "lvm-string.h"
33 #define SECTION_B_CHAR '{'
34 #define SECTION_E_CHAR '}'
39 TOK_STRING, /* Single quotes */
40 TOK_STRING_ESCAPED, /* Double quotes */
52 const char *fb, *fe; /* file limits */
54 int t; /* token limits and type */
57 int fd; /* descriptor for file being parsed */
58 int line; /* line number we are on */
64 struct config_tree cft;
80 static void _get_token(struct parser *p, int tok_prev);
81 static void _eat_space(struct parser *p);
82 static struct config_node *_file(struct parser *p);
83 static struct config_node *_section(struct parser *p);
84 static struct config_value *_value(struct parser *p);
85 static struct config_value *_type(struct parser *p);
86 static int _match_aux(struct parser *p, int t);
87 static struct config_value *_create_value(struct dm_pool *mem);
88 static struct config_node *_create_node(struct dm_pool *mem);
89 static char *_dup_tok(struct parser *p);
91 static const int sep = '/';
95 #define match(t) do {\
96 if (!_match_aux(p, (t))) {\
97 log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \
98 p->tb - p->fb + 1, p->line); \
103 static int _tok_match(const char *str, const char *b, const char *e)
105 while (*str && (b != e)) {
110 return !(*str || (b != e));
116 struct config_tree *create_config_tree(const char *filename, int keep_open)
119 struct dm_pool *mem = dm_pool_create("config", 10 * 1024);
122 log_error("Failed to allocate config pool.");
126 if (!(c = dm_pool_zalloc(mem, sizeof(*c)))) {
127 log_error("Failed to allocate config tree.");
128 dm_pool_destroy(mem);
133 c->cft.root = (struct config_node *) NULL;
136 c->keep_open = keep_open;
139 c->filename = dm_pool_strdup(c->mem, filename);
143 void destroy_config_tree(struct config_tree *cft)
145 struct cs *c = (struct cs *) cft;
150 dm_pool_destroy(c->mem);
153 static int _parse_config_file(struct parser *p, struct config_tree *cft)
155 p->tb = p->te = p->fb;
157 _get_token(p, TOK_SECTION_E);
158 if (!(cft->root = _file(p)))
164 struct config_tree *create_config_tree_from_string(struct cmd_context *cmd __attribute((unused)),
165 const char *config_settings)
168 struct config_tree *cft;
171 if (!(cft = create_config_tree(NULL, 0)))
174 c = (struct cs *) cft;
175 if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) {
176 log_error("Failed to allocate config tree parser.");
177 destroy_config_tree(cft);
182 p->fb = config_settings;
183 p->fe = config_settings + strlen(config_settings);
185 if (!_parse_config_file(p, cft)) {
186 destroy_config_tree(cft);
193 int override_config_tree_from_string(struct cmd_context *cmd,
194 const char *config_settings)
196 if (!(cmd->cft_override = create_config_tree_from_string(cmd,config_settings))) {
197 log_error("Failed to set overridden configuration entries.");
204 int read_config_fd(struct config_tree *cft, struct device *dev,
205 off_t offset, size_t size, off_t offset2, size_t size2,
206 checksum_fn_t checksum_fn, uint32_t checksum)
208 struct cs *c = (struct cs *) cft;
212 off_t mmap_offset = 0;
215 if (!(p = dm_pool_alloc(c->mem, sizeof(*p))))
219 /* Only use mmap with regular files */
220 if (!(dev->flags & DEV_REGULAR) || size2)
224 mmap_offset = offset % lvm_getpagesize();
225 /* memory map the file */
226 p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ,
227 MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
228 if (p->fb == (caddr_t) (-1)) {
229 log_sys_error("mmap", dev_name(dev));
232 p->fb = p->fb + mmap_offset;
234 if (!(buf = dm_malloc(size + size2)))
236 if (!dev_read_circular(dev, (uint64_t) offset, size,
237 (uint64_t) offset2, size2, buf)) {
243 if (checksum_fn && checksum !=
244 (checksum_fn(checksum_fn(INITIAL_CRC, p->fb, size),
245 p->fb + size, size2))) {
246 log_error("%s: Checksum error", dev_name(dev));
250 p->fe = p->fb + size + size2;
252 if (!_parse_config_file(p, cft))
262 if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) {
263 log_sys_error("munmap", dev_name(dev));
271 int read_config_file(struct config_tree *cft)
273 struct cs *c = (struct cs *) cft;
277 if (stat(c->filename, &info)) {
278 log_sys_error("stat", c->filename);
283 if (!S_ISREG(info.st_mode)) {
284 log_error("%s is not a regular file", c->filename);
291 if (info.st_size == 0) {
292 log_verbose("%s is empty", c->filename);
297 if (!(c->dev = dev_create_file(c->filename, NULL, NULL, 1)))
300 if (!dev_open_flags(c->dev, O_RDONLY, 0, 0))
304 r = read_config_fd(cft, c->dev, 0, (size_t) info.st_size, 0, 0,
305 (checksum_fn_t) NULL, 0);
312 c->timestamp = info.st_ctime;
317 time_t config_file_timestamp(struct config_tree *cft)
319 struct cs *c = (struct cs *) cft;
325 * Return 1 if config files ought to be reloaded
327 int config_file_changed(struct config_tree *cft)
329 struct cs *c = (struct cs *) cft;
335 if (stat(c->filename, &info) == -1) {
336 /* Ignore a deleted config file: still use original data */
337 if (errno == ENOENT) {
340 log_very_verbose("Config file %s has disappeared!",
344 log_sys_error("stat", c->filename);
345 log_error("Failed to reload configuration files");
349 if (!S_ISREG(info.st_mode)) {
350 log_error("Configuration file %s is not a regular file",
356 if (c->timestamp == info.st_ctime)
360 log_verbose("Detected config file change to %s", c->filename);
364 static int _line_start(struct output_line *outline)
366 if (!dm_pool_begin_object(outline->mem, 128)) {
367 log_error("dm_pool_begin_object failed for config line");
374 static int _line_append(struct output_line *outline, const char *fmt, ...)
375 __attribute__ ((format(printf, 2, 3)));
376 static int _line_append(struct output_line *outline, const char *fmt, ...)
383 n = vsnprintf(&buf[0], sizeof buf - 1, fmt, ap);
384 if (n < 0 || n > (int) sizeof buf - 1) {
385 log_error("vsnprintf failed for config line");
390 if (!dm_pool_grow_object(outline->mem, &buf[0], strlen(buf))) {
391 log_error("dm_pool_grow_object failed for config line");
398 #define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0)
400 static int _line_end(struct output_line *outline)
404 if (!dm_pool_grow_object(outline->mem, "\0", 1)) {
405 log_error("dm_pool_grow_object failed for config line");
409 line = dm_pool_end_object(outline->mem);
410 if (outline->putline)
411 outline->putline(line, outline->putline_baton);
414 log_print("%s", line);
416 fprintf(outline->fp, "%s\n", line);
422 static int _write_value(struct output_line *outline, struct config_value *v)
428 if (!(buf = alloca(escaped_len(v->v.str)))) {
429 log_error("temporary stack allocation for a config "
433 line_append("\"%s\"", escape_double_quotes(buf, v->v.str));
437 line_append("%f", v->v.r);
441 line_append("%" PRId64, v->v.i);
444 case CFG_EMPTY_ARRAY:
449 log_error("_write_value: Unknown value type: %d", v->type);
456 static int _write_config(const struct config_node *n, int only_one,
457 struct output_line *outline, int level)
459 char space[MAX_INDENT + 1];
460 int l = (level < MAX_INDENT) ? level : MAX_INDENT;
466 for (i = 0; i < l; i++)
471 if (!_line_start(outline))
473 line_append("%s%s", space, n->key);
475 /* it's a sub section */
477 if (!_line_end(outline))
479 _write_config(n->child, 0, outline, level + 1);
480 if (!_line_start(outline))
482 line_append("%s}", space);
485 struct config_value *v = n->v;
490 if (!_write_value(outline, v))
498 if (!_write_value(outline, v))
501 if (!_line_end(outline))
504 } while (n && !only_one);
505 /* FIXME: add error checking */
509 int write_config_node(const struct config_node *cn, putline_fn putline, void *baton)
511 struct output_line outline;
513 outline.mem = dm_pool_create("config_line", 1024);
514 outline.putline = putline;
515 outline.putline_baton = baton;
516 if (!_write_config(cn, 0, &outline, 0)) {
517 dm_pool_destroy(outline.mem);
520 dm_pool_destroy(outline.mem);
524 int write_config_file(struct config_tree *cft, const char *file,
525 int argc, char **argv)
527 struct config_node *cn;
529 struct output_line outline;
531 outline.putline = NULL;
535 else if (!(outline.fp = fopen(file, "w"))) {
536 log_sys_error("open", file);
540 outline.mem = dm_pool_create("config_line", 1024);
542 log_verbose("Dumping configuration to %s", file);
544 if (!_write_config(cft->root, 0, &outline, 0)) {
545 log_error("Failure while writing to %s", file);
548 } else while (argc--) {
549 if ((cn = find_config_node(cft->root, *argv))) {
550 if (!_write_config(cn, 1, &outline, 0)) {
551 log_error("Failure while writing to %s", file);
555 log_error("Configuration node %s not found", *argv);
561 if (outline.fp && lvm_fclose(outline.fp, file)) {
566 dm_pool_destroy(outline.mem);
573 static struct config_node *_file(struct parser *p)
575 struct config_node *root = NULL, *n, *l = NULL;
576 while (p->t != TOK_EOF) {
577 if (!(n = _section(p)))
590 static struct config_node *_section(struct parser *p)
592 /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
593 struct config_node *root, *n, *l = NULL;
594 if (!(root = _create_node(p->mem)))
597 if (!(root->key = _dup_tok(p)))
600 match(TOK_IDENTIFIER);
602 if (p->t == TOK_SECTION_B) {
603 match(TOK_SECTION_B);
604 while (p->t != TOK_SECTION_E) {
605 if (!(n = _section(p)))
615 match(TOK_SECTION_E);
618 if (!(root->v = _value(p)))
625 static struct config_value *_value(struct parser *p)
627 /* '[' TYPE* ']' | TYPE */
628 struct config_value *h = NULL, *l, *ll = NULL;
629 if (p->t == TOK_ARRAY_B) {
631 while (p->t != TOK_ARRAY_E) {
641 if (p->t == TOK_COMMA)
646 * Special case for an empty array.
649 if (!(h = _create_value(p->mem)))
652 h->type = CFG_EMPTY_ARRAY;
661 static struct config_value *_type(struct parser *p)
663 /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
664 struct config_value *v = _create_value(p->mem);
672 v->v.i = strtoll(p->tb, NULL, 0); /* FIXME: check error */
678 v->v.r = strtod(p->tb, NULL); /* FIXME: check error */
683 v->type = CFG_STRING;
685 p->tb++, p->te--; /* strip "'s */
686 if (!(v->v.str = _dup_tok(p)))
692 case TOK_STRING_ESCAPED:
693 v->type = CFG_STRING;
695 p->tb++, p->te--; /* strip "'s */
696 if (!(v->v.str = _dup_tok(p)))
698 unescape_double_quotes(v->v.str);
700 match(TOK_STRING_ESCAPED);
704 log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value",
705 p->tb - p->fb + 1, p->line);
711 static int _match_aux(struct parser *p, int t)
723 static void _get_token(struct parser *p, int tok_prev)
725 int values_allowed = 0;
729 if (p->tb == p->fe || !*p->tb) {
734 /* Should next token be interpreted as value instead of identifier? */
735 if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B ||
736 tok_prev == TOK_COMMA)
739 p->t = TOK_INT; /* fudge so the fall through for
743 p->t = TOK_SECTION_B;
748 p->t = TOK_SECTION_E;
773 p->t = TOK_STRING_ESCAPED;
775 while ((p->te != p->fe) && (*p->te) && (*p->te != '"')) {
776 if ((*p->te == '\\') && (p->te + 1 != p->fe) &&
782 if ((p->te != p->fe) && (*p->te))
789 while ((p->te != p->fe) && (*p->te) && (*p->te != '\''))
792 if ((p->te != p->fe) && (*p->te))
810 if (values_allowed) {
812 while ((p->te != p->fe) && (*p->te)) {
814 if (p->t == TOK_FLOAT)
817 } else if (!isdigit((int) *p->te))
825 p->t = TOK_IDENTIFIER;
826 while ((p->te != p->fe) && (*p->te) && !isspace(*p->te) &&
827 (*p->te != '#') && (*p->te != '=') &&
828 (*p->te != SECTION_B_CHAR) &&
829 (*p->te != SECTION_E_CHAR))
835 static void _eat_space(struct parser *p)
837 while ((p->tb != p->fe) && (*p->tb)) {
839 while ((p->te != p->fe) && (*p->te) && (*p->te != '\n'))
842 else if (isspace(*p->te)) {
843 while ((p->te != p->fe) && (*p->te) && isspace(*p->te)) {
860 static struct config_value *_create_value(struct dm_pool *mem)
862 struct config_value *v = dm_pool_alloc(mem, sizeof(*v));
865 memset(v, 0, sizeof(*v));
870 static struct config_node *_create_node(struct dm_pool *mem)
872 struct config_node *n = dm_pool_alloc(mem, sizeof(*n));
875 memset(n, 0, sizeof(*n));
880 static char *_dup_tok(struct parser *p)
882 size_t len = p->te - p->tb;
883 char *str = dm_pool_alloc(p->mem, len + 1);
886 strncpy(str, p->tb, len);
894 static struct config_node *_find_config_node(const struct config_node *cn,
898 const struct config_node *cn_found = NULL;
901 /* trim any leading slashes */
902 while (*path && (*path == sep))
905 /* find the end of this segment */
906 for (e = path; *e && (*e != sep); e++) ;
908 /* hunt for the node */
911 if (_tok_match(cn->key, path, e)) {
916 log_error("WARNING: Ignoring duplicate"
918 "seeking %s)", cn->key, path);
925 cn = cn_found->child;
927 break; /* don't move into the last node */
932 return (struct config_node *) cn_found;
935 static struct config_node *_find_first_config_node(const struct config_node *cn1,
936 const struct config_node *cn2,
939 struct config_node *cn;
941 if (cn1 && (cn = _find_config_node(cn1, path)))
944 if (cn2 && (cn = _find_config_node(cn2, path)))
950 struct config_node *find_config_node(const struct config_node *cn,
953 return _find_config_node(cn, path);
956 static const char *_find_config_str(const struct config_node *cn1,
957 const struct config_node *cn2,
958 const char *path, const char *fail)
960 const struct config_node *n = _find_first_config_node(cn1, cn2, path);
962 /* Empty strings are ignored */
963 if ((n && n->v && n->v->type == CFG_STRING) && (*n->v->v.str)) {
964 log_very_verbose("Setting %s to %s", path, n->v->v.str);
969 log_very_verbose("%s not found in config: defaulting to %s",
974 const char *find_config_str(const struct config_node *cn,
975 const char *path, const char *fail)
977 return _find_config_str(cn, NULL, path, fail);
980 static int64_t _find_config_int64(const struct config_node *cn1,
981 const struct config_node *cn2,
982 const char *path, int64_t fail)
984 const struct config_node *n = _find_first_config_node(cn1, cn2, path);
986 if (n && n->v && n->v->type == CFG_INT) {
987 log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i);
991 log_very_verbose("%s not found in config: defaulting to %" PRId64,
996 int find_config_int(const struct config_node *cn, const char *path, int fail)
998 /* FIXME Add log_error message on overflow */
999 return (int) _find_config_int64(cn, NULL, path, (int64_t) fail);
1002 static float _find_config_float(const struct config_node *cn1,
1003 const struct config_node *cn2,
1004 const char *path, float fail)
1006 const struct config_node *n = _find_first_config_node(cn1, cn2, path);
1008 if (n && n->v && n->v->type == CFG_FLOAT) {
1009 log_very_verbose("Setting %s to %f", path, n->v->v.r);
1013 log_very_verbose("%s not found in config: defaulting to %f",
1020 float find_config_float(const struct config_node *cn, const char *path,
1023 return _find_config_float(cn, NULL, path, fail);
1026 struct config_node *find_config_tree_node(struct cmd_context *cmd,
1029 return _find_first_config_node(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path);
1032 const char *find_config_tree_str(struct cmd_context *cmd,
1033 const char *path, const char *fail)
1035 return _find_config_str(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1038 int find_config_tree_int(struct cmd_context *cmd, const char *path,
1041 /* FIXME Add log_error message on overflow */
1042 return (int) _find_config_int64(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, (int64_t) fail);
1045 float find_config_tree_float(struct cmd_context *cmd, const char *path,
1048 return _find_config_float(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1051 static int _str_in_array(const char *str, const char * const values[])
1055 for (i = 0; values[i]; i++)
1056 if (!strcasecmp(str, values[i]))
1062 static int _str_to_bool(const char *str, int fail)
1064 const char * const _true_values[] = { "y", "yes", "on", "true", NULL };
1065 const char * const _false_values[] = { "n", "no", "off", "false", NULL };
1067 if (_str_in_array(str, _true_values))
1070 if (_str_in_array(str, _false_values))
1076 static int _find_config_bool(const struct config_node *cn1,
1077 const struct config_node *cn2,
1078 const char *path, int fail)
1080 const struct config_node *n = _find_first_config_node(cn1, cn2, path);
1081 struct config_value *v;
1090 return v->v.i ? 1 : 0;
1093 return _str_to_bool(v->v.str, fail);
1099 int find_config_bool(const struct config_node *cn, const char *path, int fail)
1101 return _find_config_bool(cn, NULL, path, fail);
1104 int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail)
1106 return _find_config_bool(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1109 int get_config_uint32(const struct config_node *cn, const char *path,
1112 const struct config_node *n;
1114 n = find_config_node(cn, path);
1116 if (!n || !n->v || n->v->type != CFG_INT)
1119 *result = n->v->v.i;
1123 int get_config_uint64(const struct config_node *cn, const char *path,
1126 const struct config_node *n;
1128 n = find_config_node(cn, path);
1130 if (!n || !n->v || n->v->type != CFG_INT)
1133 *result = (uint64_t) n->v->v.i;
1137 int get_config_str(const struct config_node *cn, const char *path,
1140 const struct config_node *n;
1142 n = find_config_node(cn, path);
1144 if (!n || !n->v || n->v->type != CFG_STRING)
1147 *result = n->v->v.str;
1151 /* Insert cn2 after cn1 */
1152 static void _insert_config_node(struct config_node **cn1,
1153 struct config_node *cn2)
1159 cn2->sib = (*cn1)->sib;
1165 * Merge section cn2 into section cn1 (which has the same name)
1166 * overwriting any existing cn1 nodes with matching names.
1168 static void _merge_section(struct config_node *cn1, struct config_node *cn2)
1170 struct config_node *cn, *nextn, *oldn;
1171 struct config_value *cv;
1173 for (cn = cn2->child; cn; cn = nextn) {
1177 if (!strcmp(cn->key, "tags"))
1182 /* Ignore - we don't have any of these yet */
1184 /* Not already present? */
1185 if (!(oldn = find_config_node(cn1->child, cn->key))) {
1186 _insert_config_node(&cn1->child, cn);
1189 /* Merge certain value lists */
1190 if ((!strcmp(cn1->key, "activation") &&
1191 !strcmp(cn->key, "volume_list")) ||
1192 (!strcmp(cn1->key, "devices") &&
1193 (!strcmp(cn->key, "filter") || !strcmp(cn->key, "types")))) {
1200 /* Replace values */
1205 static int _match_host_tags(struct dm_list *tags, struct config_node *tn)
1207 struct config_value *tv;
1210 for (tv = tn->v; tv; tv = tv->next) {
1211 if (tv->type != CFG_STRING)
1218 if (str_list_match_item(tags, str))
1225 /* Destructively merge a new config tree into an existing one */
1226 int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft,
1227 struct config_tree *newdata)
1229 struct config_node *root = cft->root;
1230 struct config_node *cn, *nextn, *oldn, *tn, *cn2;
1232 for (cn = newdata->root; cn; cn = nextn) {
1234 /* Ignore tags section */
1235 if (!strcmp(cn->key, "tags"))
1237 /* If there's a tags node, skip if host tags don't match */
1238 if ((tn = find_config_node(cn->child, "tags"))) {
1239 if (!_match_host_tags(&cmd->tags, tn))
1242 if (!(oldn = find_config_node(root, cn->key))) {
1243 _insert_config_node(&cft->root, cn);
1244 /* Remove any "tags" nodes */
1245 for (cn2 = cn->child; cn2; cn2 = cn2->sib) {
1246 if (!strcmp(cn2->key, "tags")) {
1247 cn->child = cn2->sib;
1250 if (cn2->sib && !strcmp(cn2->sib->key, "tags")) {
1251 cn2->sib = cn2->sib->sib;
1257 _merge_section(oldn, cn);
1264 * Convert a token type to the char it represents.
1266 static char _token_type_to_char(int type)
1270 return SECTION_B_CHAR;
1272 return SECTION_E_CHAR;
1280 * # of 'type' tokens in 'str'.
1282 static unsigned _count_tokens(const char *str, unsigned len, int type)
1286 c = _token_type_to_char(type);
1288 return count_chars(str, len, c);
1291 const char *config_parent_name(const struct config_node *n)
1293 return (n->parent ? n->parent->key : "(root)");
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).
1308 * 0 - probably is not a valid config section
1309 * 1 - probably _is_ a valid config section
1311 unsigned maybe_config_section(const char *str, unsigned len)
1316 begin_count = _count_tokens(str, len, TOK_SECTION_B);
1317 end_count = _count_tokens(str, len, TOK_SECTION_E);
1319 if (begin_count && end_count && (begin_count == end_count))
1325 static struct config_value *_clone_config_value(struct dm_pool *mem, const struct config_value *v)
1329 struct config_value *new = _create_value(mem);
1330 new->type = v->type;
1331 if (v->type == CFG_STRING)
1332 new->v.str = dm_pool_strdup(mem, v->v.str);
1335 new->next = _clone_config_value(mem, v->next);
1339 struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
1344 struct config_node *new = _create_node(mem);
1345 new->key = dm_pool_strdup(mem, cn->key);
1346 new->child = clone_config_node(mem, cn->child, 1);
1347 new->v = _clone_config_value(mem, cn->v);
1349 new->sib = clone_config_node(mem, cn->sib, siblings);