2 * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
5 * This file is part of LVM2.
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.
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
21 #include "toolcontext.h"
22 #include "lvm-string.h"
31 #define SECTION_B_CHAR '{'
32 #define SECTION_E_CHAR '}'
37 TOK_STRING, /* Single quotes */
38 TOK_STRING_ESCAPED, /* Double quotes */
50 const char *fb, *fe; /* file limits */
52 int t; /* token limits and type */
55 int fd; /* descriptor for file being parsed */
56 int line; /* line number we are on */
62 struct config_tree cft;
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);
89 static const int sep = '/';
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); \
101 static int _tok_match(const char *str, const char *b, const char *e)
103 while (*str && (b != e)) {
108 return !(*str || (b != e));
114 struct config_tree *create_config_tree(const char *filename, int keep_open)
117 struct dm_pool *mem = dm_pool_create("config", 10 * 1024);
120 log_error("Failed to allocate config pool.");
124 if (!(c = dm_pool_zalloc(mem, sizeof(*c)))) {
125 log_error("Failed to allocate config tree.");
126 dm_pool_destroy(mem);
131 c->cft.root = (struct config_node *) NULL;
134 c->keep_open = keep_open;
137 c->filename = dm_pool_strdup(c->mem, filename);
141 void destroy_config_tree(struct config_tree *cft)
143 struct cs *c = (struct cs *) cft;
148 dm_pool_destroy(c->mem);
151 static int _parse_config_file(struct parser *p, struct config_tree *cft)
153 p->tb = p->te = p->fb;
155 _get_token(p, TOK_SECTION_E);
156 if (!(cft->root = _file(p)))
162 struct config_tree *create_config_tree_from_string(struct cmd_context *cmd __attribute__((unused)),
163 const char *config_settings)
166 struct config_tree *cft;
169 if (!(cft = create_config_tree(NULL, 0)))
172 c = (struct cs *) cft;
173 if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) {
174 log_error("Failed to allocate config tree parser.");
175 destroy_config_tree(cft);
180 p->fb = config_settings;
181 p->fe = config_settings + strlen(config_settings);
183 if (!_parse_config_file(p, cft)) {
184 destroy_config_tree(cft);
191 int override_config_tree_from_string(struct cmd_context *cmd,
192 const char *config_settings)
194 if (!(cmd->cft_override = create_config_tree_from_string(cmd,config_settings))) {
195 log_error("Failed to set overridden configuration entries.");
202 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)
206 struct cs *c = (struct cs *) cft;
210 off_t mmap_offset = 0;
213 if (!(p = dm_pool_alloc(c->mem, sizeof(*p))))
217 /* Only use mmap with regular files */
218 if (!(dev->flags & DEV_REGULAR) || size2)
222 mmap_offset = offset % lvm_getpagesize();
223 /* memory map the file */
224 p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ,
225 MAP_PRIVATE, dev_fd(dev), offset - mmap_offset);
226 if (p->fb == (caddr_t) (-1)) {
227 log_sys_error("mmap", dev_name(dev));
230 p->fb = p->fb + mmap_offset;
232 if (!(buf = dm_malloc(size + size2)))
234 if (!dev_read_circular(dev, (uint64_t) offset, size,
235 (uint64_t) offset2, size2, buf)) {
241 if (checksum_fn && checksum !=
242 (checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)p->fb, size),
243 (const uint8_t *)(p->fb + size), size2))) {
244 log_error("%s: Checksum error", dev_name(dev));
248 p->fe = p->fb + size + size2;
250 if (!_parse_config_file(p, cft))
260 if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) {
261 log_sys_error("munmap", dev_name(dev));
269 int read_config_file(struct config_tree *cft)
271 struct cs *c = (struct cs *) cft;
275 if (stat(c->filename, &info)) {
276 log_sys_error("stat", c->filename);
281 if (!S_ISREG(info.st_mode)) {
282 log_error("%s is not a regular file", c->filename);
289 if (info.st_size == 0) {
290 log_verbose("%s is empty", c->filename);
295 if (!(c->dev = dev_create_file(c->filename, NULL, NULL, 1)))
298 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);
386 if (n < 0 || n > (int) sizeof buf - 1) {
387 log_error("vsnprintf failed for config line");
391 if (!dm_pool_grow_object(outline->mem, &buf[0], strlen(buf))) {
392 log_error("dm_pool_grow_object failed for config line");
399 #define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0)
401 static int _line_end(struct output_line *outline)
405 if (!dm_pool_grow_object(outline->mem, "\0", 1)) {
406 log_error("dm_pool_grow_object failed for config line");
410 line = dm_pool_end_object(outline->mem);
411 if (outline->putline)
412 outline->putline(line, outline->putline_baton);
415 log_print("%s", line);
417 fprintf(outline->fp, "%s\n", line);
423 static int _write_value(struct output_line *outline, const struct config_value *v)
429 if (!(buf = alloca(escaped_len(v->v.str)))) {
430 log_error("temporary stack allocation for a config "
434 line_append("\"%s\"", escape_double_quotes(buf, v->v.str));
438 line_append("%f", v->v.r);
442 line_append("%" PRId64, v->v.i);
445 case CFG_EMPTY_ARRAY:
450 log_error("_write_value: Unknown value type: %d", v->type);
457 static int _write_config(const struct config_node *n, int only_one,
458 struct output_line *outline, int level)
460 char space[MAX_INDENT + 1];
461 int l = (level < MAX_INDENT) ? level : MAX_INDENT;
467 for (i = 0; i < l; i++)
472 if (!_line_start(outline))
474 line_append("%s%s", space, n->key);
476 /* it's a sub section */
478 if (!_line_end(outline))
480 _write_config(n->child, 0, outline, level + 1);
481 if (!_line_start(outline))
483 line_append("%s}", space);
486 const struct config_value *v = n->v;
491 if (!_write_value(outline, v))
499 if (!_write_value(outline, v))
502 if (!_line_end(outline))
505 } while (n && !only_one);
506 /* FIXME: add error checking */
510 int write_config_node(const struct config_node *cn, putline_fn putline, void *baton)
512 struct output_line outline;
514 if (!(outline.mem = dm_pool_create("config_line", 1024)))
516 outline.putline = putline;
517 outline.putline_baton = baton;
518 if (!_write_config(cn, 0, &outline, 0)) {
519 dm_pool_destroy(outline.mem);
522 dm_pool_destroy(outline.mem);
526 int write_config_file(struct config_tree *cft, const char *file,
527 int argc, char **argv)
529 const struct config_node *cn;
531 struct output_line outline;
533 outline.putline = NULL;
537 else if (!(outline.fp = fopen(file, "w"))) {
538 log_sys_error("open", file);
542 if (!(outline.mem = dm_pool_create("config_line", 1024))) {
547 log_verbose("Dumping configuration to %s", file);
549 if (!_write_config(cft->root, 0, &outline, 0)) {
550 log_error("Failure while writing to %s", file);
553 } else while (argc--) {
554 if ((cn = find_config_node(cft->root, *argv))) {
555 if (!_write_config(cn, 1, &outline, 0)) {
556 log_error("Failure while writing to %s", file);
560 log_error("Configuration node %s not found", *argv);
566 dm_pool_destroy(outline.mem);
569 if (outline.fp && lvm_fclose(outline.fp, file)) {
580 static struct config_node *_file(struct parser *p)
582 struct config_node *root = NULL, *n, *l = NULL;
583 while (p->t != TOK_EOF) {
584 if (!(n = _section(p)))
597 static struct config_node *_section(struct parser *p)
599 /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */
600 struct config_node *root, *n, *l = NULL;
601 if (!(root = _create_node(p->mem)))
604 if (!(root->key = _dup_tok(p)))
607 match(TOK_IDENTIFIER);
609 if (p->t == TOK_SECTION_B) {
610 match(TOK_SECTION_B);
611 while (p->t != TOK_SECTION_E) {
612 if (!(n = _section(p)))
622 match(TOK_SECTION_E);
625 if (!(root->v = _value(p)))
632 static struct config_value *_value(struct parser *p)
634 /* '[' TYPE* ']' | TYPE */
635 struct config_value *h = NULL, *l, *ll = NULL;
636 if (p->t == TOK_ARRAY_B) {
638 while (p->t != TOK_ARRAY_E) {
648 if (p->t == TOK_COMMA)
653 * Special case for an empty array.
656 if (!(h = _create_value(p->mem)))
659 h->type = CFG_EMPTY_ARRAY;
668 static struct config_value *_type(struct parser *p)
670 /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */
671 struct config_value *v = _create_value(p->mem);
680 v->v.i = strtoll(p->tb, NULL, 0); /* FIXME: check error */
686 v->v.r = strtod(p->tb, NULL); /* FIXME: check error */
691 v->type = CFG_STRING;
693 p->tb++, p->te--; /* strip "'s */
694 if (!(v->v.str = _dup_tok(p)))
700 case TOK_STRING_ESCAPED:
701 v->type = CFG_STRING;
703 p->tb++, p->te--; /* strip "'s */
704 if (!(str = _dup_tok(p)))
706 unescape_double_quotes(str);
709 match(TOK_STRING_ESCAPED);
713 log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value",
714 p->tb - p->fb + 1, p->line);
720 static int _match_aux(struct parser *p, int t)
732 static void _get_token(struct parser *p, int tok_prev)
734 int values_allowed = 0;
740 if (p->tb == p->fe || !*p->tb) {
745 /* Should next token be interpreted as value instead of identifier? */
746 if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B ||
747 tok_prev == TOK_COMMA)
750 p->t = TOK_INT; /* fudge so the fall through for
756 p->t = TOK_SECTION_B;
761 p->t = TOK_SECTION_E;
786 p->t = TOK_STRING_ESCAPED;
788 while ((te != p->fe) && (*te) && (*te != '"')) {
789 if ((*te == '\\') && (te + 1 != p->fe) &&
795 if ((te != p->fe) && (*te))
802 while ((te != p->fe) && (*te) && (*te != '\''))
805 if ((te != p->fe) && (*te))
823 if (values_allowed) {
825 while ((te != p->fe) && (*te)) {
827 if (p->t == TOK_FLOAT)
830 } else if (!isdigit((int) *te))
838 p->t = TOK_IDENTIFIER;
839 while ((te != p->fe) && (*te) && !isspace(*te) &&
840 (*te != '#') && (*te != '=') &&
841 (*te != SECTION_B_CHAR) &&
842 (*te != SECTION_E_CHAR))
850 static void _eat_space(struct parser *p)
852 while ((p->tb != p->fe) && (*p->tb)) {
854 while ((p->te != p->fe) && (*p->te) && (*p->te != '\n'))
857 else if (isspace(*p->te)) {
858 while ((p->te != p->fe) && (*p->te) && isspace(*p->te)) {
875 static struct config_value *_create_value(struct dm_pool *mem)
877 return dm_pool_zalloc(mem, sizeof(struct config_value));
880 static struct config_node *_create_node(struct dm_pool *mem)
882 return dm_pool_zalloc(mem, sizeof(struct config_node));
885 static char *_dup_tok(struct parser *p)
887 size_t len = p->te - p->tb;
888 char *str = dm_pool_alloc(p->mem, len + 1);
891 strncpy(str, p->tb, len);
899 static const struct config_node *_find_config_node(const struct config_node *cn,
903 const struct config_node *cn_found = NULL;
906 /* trim any leading slashes */
907 while (*path && (*path == sep))
910 /* find the end of this segment */
911 for (e = path; *e && (*e != sep); e++) ;
913 /* hunt for the node */
916 if (_tok_match(cn->key, path, e)) {
921 log_warn("WARNING: Ignoring duplicate"
923 "seeking %s)", cn->key, path);
930 cn = cn_found->child;
932 break; /* don't move into the last node */
940 static const struct config_node *_find_first_config_node(const struct config_node *cn1,
941 const struct config_node *cn2,
944 const struct config_node *cn;
946 if (cn1 && (cn = _find_config_node(cn1, path)))
949 if (cn2 && (cn = _find_config_node(cn2, path)))
955 const struct config_node *find_config_node(const struct config_node *cn,
958 return _find_config_node(cn, path);
961 static const char *_find_config_str(const struct config_node *cn1,
962 const struct config_node *cn2,
963 const char *path, const char *fail)
965 const struct config_node *n = _find_first_config_node(cn1, cn2, path);
967 /* Empty strings are ignored */
968 if ((n && n->v && n->v->type == CFG_STRING) && (*n->v->v.str)) {
969 log_very_verbose("Setting %s to %s", path, n->v->v.str);
974 log_very_verbose("%s not found in config: defaulting to %s",
979 const char *find_config_str(const struct config_node *cn,
980 const char *path, const char *fail)
982 return _find_config_str(cn, NULL, path, fail);
985 static int64_t _find_config_int64(const struct config_node *cn1,
986 const struct config_node *cn2,
987 const char *path, int64_t fail)
989 const struct config_node *n = _find_first_config_node(cn1, cn2, path);
991 if (n && n->v && n->v->type == CFG_INT) {
992 log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i);
996 log_very_verbose("%s not found in config: defaulting to %" PRId64,
1001 int find_config_int(const struct config_node *cn, const char *path, int fail)
1003 /* FIXME Add log_error message on overflow */
1004 return (int) _find_config_int64(cn, NULL, path, (int64_t) fail);
1007 static float _find_config_float(const struct config_node *cn1,
1008 const struct config_node *cn2,
1009 const char *path, float fail)
1011 const struct config_node *n = _find_first_config_node(cn1, cn2, path);
1013 if (n && n->v && n->v->type == CFG_FLOAT) {
1014 log_very_verbose("Setting %s to %f", path, n->v->v.r);
1018 log_very_verbose("%s not found in config: defaulting to %f",
1025 float find_config_float(const struct config_node *cn, const char *path,
1028 return _find_config_float(cn, NULL, path, fail);
1031 const struct config_node *find_config_tree_node(struct cmd_context *cmd,
1034 return _find_first_config_node(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path);
1037 const char *find_config_tree_str(struct cmd_context *cmd,
1038 const char *path, const char *fail)
1040 return _find_config_str(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1043 int find_config_tree_int(struct cmd_context *cmd, const char *path,
1046 /* FIXME Add log_error message on overflow */
1047 return (int) _find_config_int64(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, (int64_t) fail);
1050 float find_config_tree_float(struct cmd_context *cmd, const char *path,
1053 return _find_config_float(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1056 static int _str_in_array(const char *str, const char * const values[])
1060 for (i = 0; values[i]; i++)
1061 if (!strcasecmp(str, values[i]))
1067 static int _str_to_bool(const char *str, int fail)
1069 const char * const _true_values[] = { "y", "yes", "on", "true", NULL };
1070 const char * const _false_values[] = { "n", "no", "off", "false", NULL };
1072 if (_str_in_array(str, _true_values))
1075 if (_str_in_array(str, _false_values))
1081 static int _find_config_bool(const struct config_node *cn1,
1082 const struct config_node *cn2,
1083 const char *path, int fail)
1085 const struct config_node *n = _find_first_config_node(cn1, cn2, path);
1086 const struct config_value *v;
1095 return v->v.i ? 1 : 0;
1098 return _str_to_bool(v->v.str, fail);
1104 int find_config_bool(const struct config_node *cn, const char *path, int fail)
1106 return _find_config_bool(cn, NULL, path, fail);
1109 int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail)
1111 return _find_config_bool(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail);
1114 int get_config_uint32(const struct config_node *cn, const char *path,
1117 const struct config_node *n;
1119 n = find_config_node(cn, path);
1121 if (!n || !n->v || n->v->type != CFG_INT)
1124 *result = n->v->v.i;
1128 int get_config_uint64(const struct config_node *cn, const char *path,
1131 const struct config_node *n;
1133 n = find_config_node(cn, path);
1135 if (!n || !n->v || n->v->type != CFG_INT)
1138 *result = (uint64_t) n->v->v.i;
1142 int get_config_str(const struct config_node *cn, const char *path,
1143 const char **result)
1145 const struct config_node *n;
1147 n = find_config_node(cn, path);
1149 if (!n || !n->v || n->v->type != CFG_STRING)
1152 *result = n->v->v.str;
1156 /* Insert cn2 after cn1 */
1157 static void _insert_config_node(struct config_node **cn1,
1158 struct config_node *cn2)
1164 cn2->sib = (*cn1)->sib;
1170 * Merge section cn2 into section cn1 (which has the same name)
1171 * overwriting any existing cn1 nodes with matching names.
1173 static void _merge_section(struct config_node *cn1, struct config_node *cn2)
1175 struct config_node *cn, *nextn, *oldn;
1176 struct config_value *cv;
1178 for (cn = cn2->child; cn; cn = nextn) {
1182 if (!strcmp(cn->key, "tags"))
1187 /* Ignore - we don't have any of these yet */
1189 /* Not already present? */
1190 if (!(oldn = (struct config_node*)find_config_node(cn1->child, cn->key))) {
1191 _insert_config_node(&cn1->child, cn);
1194 /* Merge certain value lists */
1195 if ((!strcmp(cn1->key, "activation") &&
1196 !strcmp(cn->key, "volume_list")) ||
1197 (!strcmp(cn1->key, "devices") &&
1198 (!strcmp(cn->key, "filter") || !strcmp(cn->key, "types")))) {
1205 /* Replace values */
1210 static int _match_host_tags(struct dm_list *tags, const struct config_node *tn)
1212 const struct config_value *tv;
1215 for (tv = tn->v; tv; tv = tv->next) {
1216 if (tv->type != CFG_STRING)
1223 if (str_list_match_item(tags, str))
1230 /* Destructively merge a new config tree into an existing one */
1231 int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft,
1232 struct config_tree *newdata)
1234 const struct config_node *root = cft->root;
1235 struct config_node *cn, *nextn, *oldn, *cn2;
1236 const struct config_node *tn;
1238 for (cn = newdata->root; cn; cn = nextn) {
1240 /* Ignore tags section */
1241 if (!strcmp(cn->key, "tags"))
1243 /* If there's a tags node, skip if host tags don't match */
1244 if ((tn = find_config_node(cn->child, "tags"))) {
1245 if (!_match_host_tags(&cmd->tags, tn))
1248 if (!(oldn = (struct config_node *)find_config_node(root, cn->key))) {
1249 _insert_config_node(&cft->root, cn);
1250 /* Remove any "tags" nodes */
1251 for (cn2 = cn->child; cn2; cn2 = cn2->sib) {
1252 if (!strcmp(cn2->key, "tags")) {
1253 cn->child = cn2->sib;
1256 if (cn2->sib && !strcmp(cn2->sib->key, "tags")) {
1257 cn2->sib = cn2->sib->sib;
1263 _merge_section(oldn, cn);
1270 * Convert a token type to the char it represents.
1272 static char _token_type_to_char(int type)
1276 return SECTION_B_CHAR;
1278 return SECTION_E_CHAR;
1286 * # of 'type' tokens in 'str'.
1288 static unsigned _count_tokens(const char *str, unsigned len, int type)
1292 c = _token_type_to_char(type);
1294 return count_chars(str, len, c);
1297 const char *config_parent_name(const struct config_node *n)
1299 return (n->parent ? n->parent->key : "(root)");
1302 * Heuristic function to make a quick guess as to whether a text
1303 * region probably contains a valid config "section". (Useful for
1304 * scanning areas of the disk for old metadata.)
1305 * Config sections contain various tokens, may contain other sections
1306 * and strings, and are delimited by begin (type 'TOK_SECTION_B') and
1307 * end (type 'TOK_SECTION_E') tokens. As a quick heuristic, we just
1308 * count the number of begin and end tokens, and see if they are
1309 * non-zero and the counts match.
1310 * Full validation of the section should be done with another function
1311 * (for example, read_config_fd).
1314 * 0 - probably is not a valid config section
1315 * 1 - probably _is_ a valid config section
1317 unsigned maybe_config_section(const char *str, unsigned len)
1322 begin_count = _count_tokens(str, len, TOK_SECTION_B);
1323 end_count = _count_tokens(str, len, TOK_SECTION_E);
1325 if (begin_count && end_count && (begin_count == end_count))
1331 static struct config_value *_clone_config_value(struct dm_pool *mem, const struct config_value *v)
1333 struct config_value *new_cv;
1338 if (!(new_cv = _create_value(mem))) {
1339 log_error("Failed to clone config value.");
1343 new_cv->type = v->type;
1344 if (v->type == CFG_STRING) {
1345 if (!(new_cv->v.str = dm_pool_strdup(mem, v->v.str))) {
1346 log_error("Failed to clone config string value.");
1352 if (v->next && !(new_cv->next = _clone_config_value(mem, v->next)))
1358 struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn,
1361 struct config_node *new_cn;
1366 if (!(new_cn = _create_node(mem))) {
1367 log_error("Failed to clone config node.");
1371 if ((cn->key && !(new_cn->key = dm_pool_strdup(mem, cn->key)))) {
1372 log_error("Failed to clone config node key.");
1376 if ((cn->v && !(new_cn->v = _clone_config_value(mem, cn->v))) ||
1377 (cn->child && !(new_cn->child = clone_config_node(mem, cn->child, 1))) ||
1378 (siblings && cn->sib && !(new_cn->sib = clone_config_node(mem, cn->sib, siblings))))
1379 return_NULL; /* 'new_cn' released with mem pool */