diff options
Diffstat (limited to 'lib/config/config.c')
-rw-r--r-- | lib/config/config.c | 1322 |
1 files changed, 216 insertions, 1106 deletions
diff --git a/lib/config/config.c b/lib/config/config.c index 72908f2..00bfcab 100644 --- a/lib/config/config.c +++ b/lib/config/config.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. - * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. + * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. * * This file is part of LVM2. * @@ -13,1149 +13,325 @@ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "lib.h" + #include "config.h" #include "crc.h" #include "device.h" #include "str_list.h" #include "toolcontext.h" -#include "lvm-string.h" #include "lvm-file.h" #include <sys/stat.h> #include <sys/mman.h> #include <unistd.h> #include <fcntl.h> -#include <ctype.h> - -#define SECTION_B_CHAR '{' -#define SECTION_E_CHAR '}' - -enum { - TOK_INT, - TOK_FLOAT, - TOK_STRING, /* Single quotes */ - TOK_STRING_ESCAPED, /* Double quotes */ - TOK_EQ, - TOK_SECTION_B, - TOK_SECTION_E, - TOK_ARRAY_B, - TOK_ARRAY_E, - TOK_IDENTIFIER, - TOK_COMMA, - TOK_EOF -}; - -struct parser { - const char *fb, *fe; /* file limits */ - - int t; /* token limits and type */ - const char *tb, *te; - - int fd; /* descriptor for file being parsed */ - int line; /* line number we are on */ - - struct dm_pool *mem; -}; +#include <assert.h> -struct cs { - struct config_tree cft; - struct dm_pool *mem; +struct config_file { time_t timestamp; + off_t st_size; char *filename; int exists; int keep_open; struct device *dev; }; -struct output_line { - FILE *fp; - struct dm_pool *mem; - putline_fn putline; - void *putline_baton; -}; - -static void _get_token(struct parser *p, int tok_prev); -static void _eat_space(struct parser *p); -static struct config_node *_file(struct parser *p); -static struct config_node *_section(struct parser *p); -static struct config_value *_value(struct parser *p); -static struct config_value *_type(struct parser *p); -static int _match_aux(struct parser *p, int t); -static struct config_value *_create_value(struct dm_pool *mem); -static struct config_node *_create_node(struct dm_pool *mem); -static char *_dup_tok(struct parser *p); - -static const int sep = '/'; - -#define MAX_INDENT 32 - -#define match(t) do {\ - if (!_match_aux(p, (t))) {\ - log_error("Parse error at byte %" PRIptrdiff_t " (line %d): unexpected token", \ - p->tb - p->fb + 1, p->line); \ - return 0;\ - } \ -} while(0); - -static int _tok_match(const char *str, const char *b, const char *e) -{ - while (*str && (b != e)) { - if (*str++ != *b++) - return 0; - } - - return !(*str || (b != e)); -} - /* * public interface */ -struct config_tree *create_config_tree(const char *filename, int keep_open) -{ - struct cs *c; - struct dm_pool *mem = dm_pool_create("config", 10 * 1024); - - if (!mem) { - log_error("Failed to allocate config pool."); - return 0; - } - - if (!(c = dm_pool_zalloc(mem, sizeof(*c)))) { - log_error("Failed to allocate config tree."); - dm_pool_destroy(mem); - return 0; - } - - c->mem = mem; - c->cft.root = (struct config_node *) NULL; - c->timestamp = 0; - c->exists = 0; - c->keep_open = keep_open; - c->dev = 0; - if (filename) - c->filename = dm_pool_strdup(c->mem, filename); - return &c->cft; -} - -void destroy_config_tree(struct config_tree *cft) -{ - struct cs *c = (struct cs *) cft; - - if (c->dev) - dev_close(c->dev); - - dm_pool_destroy(c->mem); -} - -static int _parse_config_file(struct parser *p, struct config_tree *cft) -{ - p->tb = p->te = p->fb; - p->line = 1; - _get_token(p, TOK_SECTION_E); - if (!(cft->root = _file(p))) - return_0; - - return 1; -} - -struct config_tree *create_config_tree_from_string(struct cmd_context *cmd __attribute__((unused)), - const char *config_settings) +struct dm_config_tree *config_file_open(const char *filename, int keep_open) { - struct cs *c; - struct config_tree *cft; - struct parser *p; - - if (!(cft = create_config_tree(NULL, 0))) - return_NULL; - - c = (struct cs *) cft; - if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) { - log_error("Failed to allocate config tree parser."); - destroy_config_tree(cft); + struct dm_config_tree *cft = dm_config_create(); + struct config_file *cf; + if (!cft) return NULL; - } - p->mem = c->mem; - p->fb = config_settings; - p->fe = config_settings + strlen(config_settings); + cf = dm_pool_zalloc(cft->mem, sizeof(struct config_file)); + if (!cf) goto fail; - if (!_parse_config_file(p, cft)) { - destroy_config_tree(cft); - return_NULL; - } - - return cft; -} + cf->timestamp = 0; + cf->exists = 0; + cf->keep_open = keep_open; + dm_config_set_custom(cft, cf); -int override_config_tree_from_string(struct cmd_context *cmd, - const char *config_settings) -{ - if (!(cmd->cft_override = create_config_tree_from_string(cmd,config_settings))) { - log_error("Failed to set overridden configuration entries."); - return 1; + if (filename && + !(cf->filename = dm_pool_strdup(cft->mem, filename))) { + log_error("Failed to duplicate filename."); + goto fail; } - return 0; + return cft; +fail: + dm_config_destroy(cft); + return NULL; } -int read_config_fd(struct config_tree *cft, struct device *dev, - off_t offset, size_t size, off_t offset2, size_t size2, - checksum_fn_t checksum_fn, uint32_t checksum) +/* + * Doesn't populate filename if the file is empty. + */ +int config_file_check(struct dm_config_tree *cft, const char **filename, struct stat *info) { - struct cs *c = (struct cs *) cft; - struct parser *p; - int r = 0; - int use_mmap = 1; - off_t mmap_offset = 0; - char *buf = NULL; + struct config_file *cf = dm_config_get_custom(cft); + struct stat _info; - if (!(p = dm_pool_alloc(c->mem, sizeof(*p)))) - return_0; - p->mem = c->mem; - - /* Only use mmap with regular files */ - if (!(dev->flags & DEV_REGULAR) || size2) - use_mmap = 0; - - if (use_mmap) { - mmap_offset = offset % lvm_getpagesize(); - /* memory map the file */ - p->fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ, - MAP_PRIVATE, dev_fd(dev), offset - mmap_offset); - if (p->fb == (caddr_t) (-1)) { - log_sys_error("mmap", dev_name(dev)); - goto out; - } - p->fb = p->fb + mmap_offset; - } else { - if (!(buf = dm_malloc(size + size2))) - return_0; - if (!dev_read_circular(dev, (uint64_t) offset, size, - (uint64_t) offset2, size2, buf)) { - goto out; - } - p->fb = buf; - } - - if (checksum_fn && checksum != - (checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)p->fb, size), - (const uint8_t *)(p->fb + size), size2))) { - log_error("%s: Checksum error", dev_name(dev)); - goto out; - } - - p->fe = p->fb + size + size2; - - if (!_parse_config_file(p, cft)) - goto_out; - - r = 1; + if (!info) + info = &_info; - out: - if (!use_mmap) - dm_free(buf); - else { - /* unmap the file */ - if (munmap((char *) (p->fb - mmap_offset), size + mmap_offset)) { - log_sys_error("munmap", dev_name(dev)); - r = 0; - } - } - - return r; -} - -int read_config_file(struct config_tree *cft) -{ - struct cs *c = (struct cs *) cft; - struct stat info; - int r = 1; - - if (stat(c->filename, &info)) { - log_sys_error("stat", c->filename); - c->exists = 0; + if (stat(cf->filename, info)) { + log_sys_error("stat", cf->filename); + cf->exists = 0; return 0; } - if (!S_ISREG(info.st_mode)) { - log_error("%s is not a regular file", c->filename); - c->exists = 0; + if (!S_ISREG(info->st_mode)) { + log_error("%s is not a regular file", cf->filename); + cf->exists = 0; return 0; } - c->exists = 1; - - if (info.st_size == 0) { - log_verbose("%s is empty", c->filename); - return 1; - } - - if (!c->dev) { - if (!(c->dev = dev_create_file(c->filename, NULL, NULL, 1))) - return_0; + cf->exists = 1; + cf->timestamp = info->st_ctime; + cf->st_size = info->st_size; - if (!dev_open_flags(c->dev, O_RDONLY, 0, 0)) { - c->dev = 0; - return_0; - } - } - - r = read_config_fd(cft, c->dev, 0, (size_t) info.st_size, 0, 0, - (checksum_fn_t) NULL, 0); - - if (!c->keep_open) { - dev_close(c->dev); - c->dev = 0; - } + if (info->st_size == 0) + log_verbose("%s is empty", cf->filename); + else if (filename) + *filename = cf->filename; - c->timestamp = info.st_ctime; - - return r; -} - -time_t config_file_timestamp(struct config_tree *cft) -{ - struct cs *c = (struct cs *) cft; - - return c->timestamp; + return 1; } /* * Return 1 if config files ought to be reloaded */ -int config_file_changed(struct config_tree *cft) +int config_file_changed(struct dm_config_tree *cft) { - struct cs *c = (struct cs *) cft; + struct config_file *cf = dm_config_get_custom(cft); struct stat info; - if (!c->filename) + if (!cf->filename) return 0; - if (stat(c->filename, &info) == -1) { + if (stat(cf->filename, &info) == -1) { /* Ignore a deleted config file: still use original data */ if (errno == ENOENT) { - if (!c->exists) + if (!cf->exists) return 0; log_very_verbose("Config file %s has disappeared!", - c->filename); + cf->filename); goto reload; } - log_sys_error("stat", c->filename); + log_sys_error("stat", cf->filename); log_error("Failed to reload configuration files"); return 0; } if (!S_ISREG(info.st_mode)) { log_error("Configuration file %s is not a regular file", - c->filename); + cf->filename); goto reload; } /* Unchanged? */ - if (c->timestamp == info.st_ctime) + if (cf->timestamp == info.st_ctime && cf->st_size == info.st_size) return 0; reload: - log_verbose("Detected config file change to %s", c->filename); + log_verbose("Detected config file change to %s", cf->filename); return 1; } -static int _line_start(struct output_line *outline) +void config_file_destroy(struct dm_config_tree *cft) { - if (!dm_pool_begin_object(outline->mem, 128)) { - log_error("dm_pool_begin_object failed for config line"); - return 0; - } + struct config_file *cf = dm_config_get_custom(cft); - return 1; + if (cf && cf->dev) + if (!dev_close(cf->dev)) + stack; + + dm_config_destroy(cft); } -static int _line_append(struct output_line *outline, const char *fmt, ...) - __attribute__ ((format(printf, 2, 3))); -static int _line_append(struct output_line *outline, const char *fmt, ...) +/* + * Returns config tree if it was removed. + */ +struct dm_config_tree *remove_overridden_config_tree(struct cmd_context *cmd) { - char buf[4096]; - va_list ap; - int n; - - va_start(ap, fmt); - n = vsnprintf(&buf[0], sizeof buf - 1, fmt, ap); - va_end(ap); + struct dm_config_tree *old_cft = cmd->cft; + struct dm_config_tree *cft = dm_config_remove_cascaded_tree(cmd->cft); - if (n < 0 || n > (int) sizeof buf - 1) { - log_error("vsnprintf failed for config line"); - return 0; - } + if (!cft) + return NULL; - if (!dm_pool_grow_object(outline->mem, &buf[0], strlen(buf))) { - log_error("dm_pool_grow_object failed for config line"); - return 0; - } + cmd->cft = cft; - return 1; + return old_cft; } -#define line_append(args...) do {if (!_line_append(outline, args)) {return_0;}} while (0) - -static int _line_end(struct output_line *outline) +int override_config_tree_from_string(struct cmd_context *cmd, + const char *config_settings) { - const char *line; + struct dm_config_tree *cft_new; - if (!dm_pool_grow_object(outline->mem, "\0", 1)) { - log_error("dm_pool_grow_object failed for config line"); - return 0; + if (!(cft_new = dm_config_from_string(config_settings))) { + log_error("Failed to set overridden configuration entries."); + return 1; } - line = dm_pool_end_object(outline->mem); - if (outline->putline) - outline->putline(line, outline->putline_baton); - else { - if (!outline->fp) - log_print("%s", line); - else - fprintf(outline->fp, "%s\n", line); - } + cmd->cft = dm_config_insert_cascaded_tree(cft_new, cmd->cft); - return 1; + return 0; } -static int _write_value(struct output_line *outline, const struct config_value *v) +int config_file_read_fd(struct dm_config_tree *cft, struct device *dev, + off_t offset, size_t size, off_t offset2, size_t size2, + checksum_fn_t checksum_fn, uint32_t checksum) { - char *buf; + char *fb, *fe; + int r = 0; + int use_mmap = 1; + off_t mmap_offset = 0; + char *buf = NULL; - switch (v->type) { - case CFG_STRING: - if (!(buf = alloca(escaped_len(v->v.str)))) { - log_error("temporary stack allocation for a config " - "string failed"); + /* Only use mmap with regular files */ + if (!(dev->flags & DEV_REGULAR) || size2) + use_mmap = 0; + + if (use_mmap) { + mmap_offset = offset % lvm_getpagesize(); + /* memory map the file */ + fb = mmap((caddr_t) 0, size + mmap_offset, PROT_READ, + MAP_PRIVATE, dev_fd(dev), offset - mmap_offset); + if (fb == (caddr_t) (-1)) { + log_sys_error("mmap", dev_name(dev)); + goto out; + } + fb = fb + mmap_offset; + } else { + if (!(buf = dm_malloc(size + size2))) { + log_error("Failed to allocate circular buffer."); return 0; } - line_append("\"%s\"", escape_double_quotes(buf, v->v.str)); - break; - - case CFG_FLOAT: - line_append("%f", v->v.r); - break; - - case CFG_INT: - line_append("%" PRId64, v->v.i); - break; - - case CFG_EMPTY_ARRAY: - line_append("[]"); - break; - - default: - log_error("_write_value: Unknown value type: %d", v->type); - - } - - return 1; -} - -static int _write_config(const struct config_node *n, int only_one, - struct output_line *outline, int level) -{ - char space[MAX_INDENT + 1]; - int l = (level < MAX_INDENT) ? level : MAX_INDENT; - int i; - - if (!n) - return 1; - - for (i = 0; i < l; i++) - space[i] = '\t'; - space[i] = '\0'; - - do { - if (!_line_start(outline)) - return_0; - line_append("%s%s", space, n->key); - if (!n->v) { - /* it's a sub section */ - line_append(" {"); - if (!_line_end(outline)) - return_0; - _write_config(n->child, 0, outline, level + 1); - if (!_line_start(outline)) - return_0; - line_append("%s}", space); - } else { - /* it's a value */ - const struct config_value *v = n->v; - line_append("="); - if (v->next) { - line_append("["); - while (v) { - if (!_write_value(outline, v)) - return_0; - v = v->next; - if (v) - line_append(", "); - } - line_append("]"); - } else - if (!_write_value(outline, v)) - return_0; + if (!dev_read_circular(dev, (uint64_t) offset, size, + (uint64_t) offset2, size2, buf)) { + goto out; } - if (!_line_end(outline)) - return_0; - n = n->sib; - } while (n && !only_one); - /* FIXME: add error checking */ - return 1; -} - -int write_config_node(const struct config_node *cn, putline_fn putline, void *baton) -{ - struct output_line outline; - outline.fp = NULL; - if (!(outline.mem = dm_pool_create("config_line", 1024))) - return_0; - outline.putline = putline; - outline.putline_baton = baton; - if (!_write_config(cn, 0, &outline, 0)) { - dm_pool_destroy(outline.mem); - return_0; + fb = buf; } - dm_pool_destroy(outline.mem); - return 1; -} -int write_config_file(struct config_tree *cft, const char *file, - int argc, char **argv) -{ - const struct config_node *cn; - int r = 1; - struct output_line outline; - outline.fp = NULL; - outline.putline = NULL; - - if (!file) - file = "stdout"; - else if (!(outline.fp = fopen(file, "w"))) { - log_sys_error("open", file); - return 0; + if (checksum_fn && checksum != + (checksum_fn(checksum_fn(INITIAL_CRC, (const uint8_t *)fb, size), + (const uint8_t *)(fb + size), size2))) { + log_error("%s: Checksum error", dev_name(dev)); + goto out; } - if (!(outline.mem = dm_pool_create("config_line", 1024))) { - r = 0; + fe = fb + size + size2; + if (!dm_config_parse(cft, fb, fe)) goto_out; - } - log_verbose("Dumping configuration to %s", file); - if (!argc) { - if (!_write_config(cft->root, 0, &outline, 0)) { - log_error("Failure while writing to %s", file); - r = 0; - } - } else while (argc--) { - if ((cn = find_config_node(cft->root, *argv))) { - if (!_write_config(cn, 1, &outline, 0)) { - log_error("Failure while writing to %s", file); - r = 0; - } - } else { - log_error("Configuration node %s not found", *argv); + r = 1; + + out: + if (!use_mmap) + dm_free(buf); + else { + /* unmap the file */ + if (munmap(fb - mmap_offset, size + mmap_offset)) { + log_sys_error("munmap", dev_name(dev)); r = 0; } - argv++; - } - - dm_pool_destroy(outline.mem); - -out: - if (outline.fp && lvm_fclose(outline.fp, file)) { - stack; - r = 0; } return r; } -/* - * parser - */ -static struct config_node *_file(struct parser *p) -{ - struct config_node *root = NULL, *n, *l = NULL; - while (p->t != TOK_EOF) { - if (!(n = _section(p))) - return_0; - - if (!root) - root = n; - else - l->sib = n; - n->parent = root; - l = n; - } - return root; -} - -static struct config_node *_section(struct parser *p) +int config_file_read(struct dm_config_tree *cft) { - /* IDENTIFIER SECTION_B_CHAR VALUE* SECTION_E_CHAR */ - struct config_node *root, *n, *l = NULL; - if (!(root = _create_node(p->mem))) - return_0; + const char *filename = NULL; + struct config_file *cf = dm_config_get_custom(cft); + struct stat info; + int r; - if (!(root->key = _dup_tok(p))) + if (!config_file_check(cft, &filename, &info)) return_0; - match(TOK_IDENTIFIER); - - if (p->t == TOK_SECTION_B) { - match(TOK_SECTION_B); - while (p->t != TOK_SECTION_E) { - if (!(n = _section(p))) - return_0; - - if (!root->child) - root->child = n; - else - l->sib = n; - n->parent = root; - l = n; - } - match(TOK_SECTION_E); - } else { - match(TOK_EQ); - if (!(root->v = _value(p))) - return_0; - } - - return root; -} - -static struct config_value *_value(struct parser *p) -{ - /* '[' TYPE* ']' | TYPE */ - struct config_value *h = NULL, *l, *ll = NULL; - if (p->t == TOK_ARRAY_B) { - match(TOK_ARRAY_B); - while (p->t != TOK_ARRAY_E) { - if (!(l = _type(p))) - return_0; - - if (!h) - h = l; - else - ll->next = l; - ll = l; - - if (p->t == TOK_COMMA) - match(TOK_COMMA); - } - match(TOK_ARRAY_E); - /* - * Special case for an empty array. - */ - if (!h) { - if (!(h = _create_value(p->mem))) - return NULL; - - h->type = CFG_EMPTY_ARRAY; - } - - } else - h = _type(p); - - return h; -} - -static struct config_value *_type(struct parser *p) -{ - /* [+-]{0,1}[0-9]+ | [0-9]*\.[0-9]* | ".*" */ - struct config_value *v = _create_value(p->mem); - char *str; - - if (!v) - return NULL; - - switch (p->t) { - case TOK_INT: - v->type = CFG_INT; - v->v.i = strtoll(p->tb, NULL, 0); /* FIXME: check error */ - match(TOK_INT); - break; - - case TOK_FLOAT: - v->type = CFG_FLOAT; - v->v.r = strtod(p->tb, NULL); /* FIXME: check error */ - match(TOK_FLOAT); - break; - - case TOK_STRING: - v->type = CFG_STRING; + /* Nothing to do. E.g. empty file. */ + if (!filename) + return 1; - p->tb++, p->te--; /* strip "'s */ - if (!(v->v.str = _dup_tok(p))) + if (!cf->dev) { + if (!(cf->dev = dev_create_file(filename, NULL, NULL, 1))) return_0; - p->te++; - match(TOK_STRING); - break; - case TOK_STRING_ESCAPED: - v->type = CFG_STRING; - - p->tb++, p->te--; /* strip "'s */ - if (!(str = _dup_tok(p))) + if (!dev_open_readonly_buffered(cf->dev)) return_0; - unescape_double_quotes(str); - v->v.str = str; - p->te++; - match(TOK_STRING_ESCAPED); - break; - - default: - log_error("Parse error at byte %" PRIptrdiff_t " (line %d): expected a value", - p->tb - p->fb + 1, p->line); - return 0; } - return v; -} - -static int _match_aux(struct parser *p, int t) -{ - if (p->t != t) - return 0; - - _get_token(p, t); - return 1; -} - -/* - * tokeniser - */ -static void _get_token(struct parser *p, int tok_prev) -{ - int values_allowed = 0; - - const char *te; - - p->tb = p->te; - _eat_space(p); - if (p->tb == p->fe || !*p->tb) { - p->t = TOK_EOF; - return; - } - - /* Should next token be interpreted as value instead of identifier? */ - if (tok_prev == TOK_EQ || tok_prev == TOK_ARRAY_B || - tok_prev == TOK_COMMA) - values_allowed = 1; - - p->t = TOK_INT; /* fudge so the fall through for - floats works */ - - te = p->te; - switch (*te) { - case SECTION_B_CHAR: - p->t = TOK_SECTION_B; - te++; - break; - - case SECTION_E_CHAR: - p->t = TOK_SECTION_E; - te++; - break; - - case '[': - p->t = TOK_ARRAY_B; - te++; - break; - - case ']': - p->t = TOK_ARRAY_E; - te++; - break; - - case ',': - p->t = TOK_COMMA; - te++; - break; - - case '=': - p->t = TOK_EQ; - te++; - break; - - case '"': - p->t = TOK_STRING_ESCAPED; - te++; - while ((te != p->fe) && (*te) && (*te != '"')) { - if ((*te == '\\') && (te + 1 != p->fe) && - *(te + 1)) - te++; - te++; - } - - if ((te != p->fe) && (*te)) - te++; - break; - - case '\'': - p->t = TOK_STRING; - te++; - while ((te != p->fe) && (*te) && (*te != '\'')) - te++; - - if ((te != p->fe) && (*te)) - te++; - break; - - case '.': - p->t = TOK_FLOAT; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '+': - case '-': - if (values_allowed) { - te++; - while ((te != p->fe) && (*te)) { - if (*te == '.') { - if (p->t == TOK_FLOAT) - break; - p->t = TOK_FLOAT; - } else if (!isdigit((int) *te)) - break; - te++; - } - break; - } - - default: - p->t = TOK_IDENTIFIER; - while ((te != p->fe) && (*te) && !isspace(*te) && - (*te != '#') && (*te != '=') && - (*te != SECTION_B_CHAR) && - (*te != SECTION_E_CHAR)) - te++; - break; - } - - p->te = te; -} - -static void _eat_space(struct parser *p) -{ - while ((p->tb != p->fe) && (*p->tb)) { - if (*p->te == '#') - while ((p->te != p->fe) && (*p->te) && (*p->te != '\n')) - p->te++; - - else if (isspace(*p->te)) { - while ((p->te != p->fe) && (*p->te) && isspace(*p->te)) { - if (*p->te == '\n') - p->line++; - p->te++; - } - } - else - return; + r = config_file_read_fd(cft, cf->dev, 0, (size_t) info.st_size, 0, 0, + (checksum_fn_t) NULL, 0); - p->tb = p->te; + if (!cf->keep_open) { + if (!dev_close(cf->dev)) + stack; + cf->dev = NULL; } -} -/* - * memory management - */ -static struct config_value *_create_value(struct dm_pool *mem) -{ - return dm_pool_zalloc(mem, sizeof(struct config_value)); + return r; } -static struct config_node *_create_node(struct dm_pool *mem) +time_t config_file_timestamp(struct dm_config_tree *cft) { - return dm_pool_zalloc(mem, sizeof(struct config_node)); + struct config_file *cf = dm_config_get_custom(cft); + assert(cf); + return cf->timestamp; } -static char *_dup_tok(struct parser *p) -{ - size_t len = p->te - p->tb; - char *str = dm_pool_alloc(p->mem, len + 1); - if (!str) - return_0; - strncpy(str, p->tb, len); - str[len] = '\0'; - return str; -} - -/* - * utility functions - */ -static const struct config_node *_find_config_node(const struct config_node *cn, +const struct dm_config_node *find_config_tree_node(struct cmd_context *cmd, const char *path) { - const char *e; - const struct config_node *cn_found = NULL; - - while (cn) { - /* trim any leading slashes */ - while (*path && (*path == sep)) - path++; - - /* find the end of this segment */ - for (e = path; *e && (*e != sep); e++) ; - - /* hunt for the node */ - cn_found = NULL; - while (cn) { - if (_tok_match(cn->key, path, e)) { - /* Inefficient */ - if (!cn_found) - cn_found = cn; - else - log_warn("WARNING: Ignoring duplicate" - " config node: %s (" - "seeking %s)", cn->key, path); - } - - cn = cn->sib; - } - - if (cn_found && *e) - cn = cn_found->child; - else - break; /* don't move into the last node */ - - path = e; - } - - return cn_found; -} - -static const struct config_node *_find_first_config_node(const struct config_node *cn1, - const struct config_node *cn2, - const char *path) -{ - const struct config_node *cn; - - if (cn1 && (cn = _find_config_node(cn1, path))) - return cn; - - if (cn2 && (cn = _find_config_node(cn2, path))) - return cn; - - return NULL; -} - -const struct config_node *find_config_node(const struct config_node *cn, - const char *path) -{ - return _find_config_node(cn, path); -} - -static const char *_find_config_str(const struct config_node *cn1, - const struct config_node *cn2, - const char *path, const char *fail) -{ - const struct config_node *n = _find_first_config_node(cn1, cn2, path); - - /* Empty strings are ignored */ - if ((n && n->v && n->v->type == CFG_STRING) && (*n->v->v.str)) { - log_very_verbose("Setting %s to %s", path, n->v->v.str); - return n->v->v.str; - } - - if (fail) - log_very_verbose("%s not found in config: defaulting to %s", - path, fail); - return fail; -} - -const char *find_config_str(const struct config_node *cn, - const char *path, const char *fail) -{ - return _find_config_str(cn, NULL, path, fail); -} - -static int64_t _find_config_int64(const struct config_node *cn1, - const struct config_node *cn2, - const char *path, int64_t fail) -{ - const struct config_node *n = _find_first_config_node(cn1, cn2, path); - - if (n && n->v && n->v->type == CFG_INT) { - log_very_verbose("Setting %s to %" PRId64, path, n->v->v.i); - return n->v->v.i; - } - - log_very_verbose("%s not found in config: defaulting to %" PRId64, - path, fail); - return fail; -} - -int find_config_int(const struct config_node *cn, const char *path, int fail) -{ - /* FIXME Add log_error message on overflow */ - return (int) _find_config_int64(cn, NULL, path, (int64_t) fail); -} - -static float _find_config_float(const struct config_node *cn1, - const struct config_node *cn2, - const char *path, float fail) -{ - const struct config_node *n = _find_first_config_node(cn1, cn2, path); - - if (n && n->v && n->v->type == CFG_FLOAT) { - log_very_verbose("Setting %s to %f", path, n->v->v.r); - return n->v->v.r; - } - - log_very_verbose("%s not found in config: defaulting to %f", - path, fail); - - return fail; - -} - -float find_config_float(const struct config_node *cn, const char *path, - float fail) -{ - return _find_config_float(cn, NULL, path, fail); -} - -const struct config_node *find_config_tree_node(struct cmd_context *cmd, - const char *path) -{ - return _find_first_config_node(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path); + return dm_config_tree_find_node(cmd->cft, path); } const char *find_config_tree_str(struct cmd_context *cmd, const char *path, const char *fail) { - return _find_config_str(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail); + return dm_config_tree_find_str(cmd->cft, path, fail); } -int find_config_tree_int(struct cmd_context *cmd, const char *path, - int fail) +const char *find_config_tree_str_allow_empty(struct cmd_context *cmd, + const char *path, const char *fail) { - /* FIXME Add log_error message on overflow */ - return (int) _find_config_int64(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, (int64_t) fail); + return dm_config_tree_find_str_allow_empty(cmd->cft, path, fail); } -float find_config_tree_float(struct cmd_context *cmd, const char *path, - float fail) -{ - return _find_config_float(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail); -} - -static int _str_in_array(const char *str, const char * const values[]) -{ - int i; - - for (i = 0; values[i]; i++) - if (!strcasecmp(str, values[i])) - return 1; - - return 0; -} - -static int _str_to_bool(const char *str, int fail) +int find_config_tree_int(struct cmd_context *cmd, const char *path, + int fail) { - const char * const _true_values[] = { "y", "yes", "on", "true", NULL }; - const char * const _false_values[] = { "n", "no", "off", "false", NULL }; - - if (_str_in_array(str, _true_values)) - return 1; - - if (_str_in_array(str, _false_values)) - return 0; - - return fail; + return dm_config_tree_find_int(cmd->cft, path, fail); } -static int _find_config_bool(const struct config_node *cn1, - const struct config_node *cn2, - const char *path, int fail) +int64_t find_config_tree_int64(struct cmd_context *cmd, const char *path, int64_t fail) { - const struct config_node *n = _find_first_config_node(cn1, cn2, path); - const struct config_value *v; - - if (!n) - return fail; - - v = n->v; - - switch (v->type) { - case CFG_INT: - return v->v.i ? 1 : 0; - - case CFG_STRING: - return _str_to_bool(v->v.str, fail); - } - - return fail; + return dm_config_tree_find_int64(cmd->cft, path, fail); } -int find_config_bool(const struct config_node *cn, const char *path, int fail) +float find_config_tree_float(struct cmd_context *cmd, const char *path, + float fail) { - return _find_config_bool(cn, NULL, path, fail); + return dm_config_tree_find_float(cmd->cft, path, fail); } int find_config_tree_bool(struct cmd_context *cmd, const char *path, int fail) { - return _find_config_bool(cmd->cft_override ? cmd->cft_override->root : NULL, cmd->cft->root, path, fail); -} - -int get_config_uint32(const struct config_node *cn, const char *path, - uint32_t *result) -{ - const struct config_node *n; - - n = find_config_node(cn, path); - - if (!n || !n->v || n->v->type != CFG_INT) - return 0; - - *result = n->v->v.i; - return 1; -} - -int get_config_uint64(const struct config_node *cn, const char *path, - uint64_t *result) -{ - const struct config_node *n; - - n = find_config_node(cn, path); - - if (!n || !n->v || n->v->type != CFG_INT) - return 0; - - *result = (uint64_t) n->v->v.i; - return 1; -} - -int get_config_str(const struct config_node *cn, const char *path, - const char **result) -{ - const struct config_node *n; - - n = find_config_node(cn, path); - - if (!n || !n->v || n->v->type != CFG_STRING) - return 0; - - *result = n->v->v.str; - return 1; + return dm_config_tree_find_bool(cmd->cft, path, fail); } /* Insert cn2 after cn1 */ -static void _insert_config_node(struct config_node **cn1, - struct config_node *cn2) +static void _insert_config_node(struct dm_config_node **cn1, + struct dm_config_node *cn2) { if (!*cn1) { *cn1 = cn2; @@ -1170,10 +346,10 @@ static void _insert_config_node(struct config_node **cn1, * Merge section cn2 into section cn1 (which has the same name) * overwriting any existing cn1 nodes with matching names. */ -static void _merge_section(struct config_node *cn1, struct config_node *cn2) +static void _merge_section(struct dm_config_node *cn1, struct dm_config_node *cn2) { - struct config_node *cn, *nextn, *oldn; - struct config_value *cv; + struct dm_config_node *cn, *nextn, *oldn; + struct dm_config_value *cv; for (cn = cn2->child; cn; cn = nextn) { nextn = cn->sib; @@ -1187,7 +363,7 @@ static void _merge_section(struct config_node *cn1, struct config_node *cn2) /* Ignore - we don't have any of these yet */ continue; /* Not already present? */ - if (!(oldn = (struct config_node*)find_config_node(cn1->child, cn->key))) { + if (!(oldn = dm_config_find_node(cn1->child, cn->key))) { _insert_config_node(&cn1->child, cn); continue; } @@ -1207,13 +383,13 @@ static void _merge_section(struct config_node *cn1, struct config_node *cn2) } } -static int _match_host_tags(struct dm_list *tags, const struct config_node *tn) +static int _match_host_tags(struct dm_list *tags, const struct dm_config_node *tn) { - const struct config_value *tv; + const struct dm_config_value *tv; const char *str; for (tv = tn->v; tv; tv = tv->next) { - if (tv->type != CFG_STRING) + if (tv->type != DM_CFG_STRING) continue; str = tv->v.str; if (*str == '@') @@ -1228,12 +404,12 @@ static int _match_host_tags(struct dm_list *tags, const struct config_node *tn) } /* Destructively merge a new config tree into an existing one */ -int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft, - struct config_tree *newdata) +int merge_config_tree(struct cmd_context *cmd, struct dm_config_tree *cft, + struct dm_config_tree *newdata) { - const struct config_node *root = cft->root; - struct config_node *cn, *nextn, *oldn, *cn2; - const struct config_node *tn; + struct dm_config_node *root = cft->root; + struct dm_config_node *cn, *nextn, *oldn, *cn2; + const struct dm_config_node *tn; for (cn = newdata->root; cn; cn = nextn) { nextn = cn->sib; @@ -1241,11 +417,11 @@ int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft, if (!strcmp(cn->key, "tags")) continue; /* If there's a tags node, skip if host tags don't match */ - if ((tn = find_config_node(cn->child, "tags"))) { + if ((tn = dm_config_find_node(cn->child, "tags"))) { if (!_match_host_tags(&cmd->tags, tn)) continue; } - if (!(oldn = (struct config_node *)find_config_node(root, cn->key))) { + if (!(oldn = dm_config_find_node(root, cn->key))) { _insert_config_node(&cft->root, cn); /* Remove any "tags" nodes */ for (cn2 = cn->child; cn2; cn2 = cn2->sib) { @@ -1266,117 +442,51 @@ int merge_config_tree(struct cmd_context *cmd, struct config_tree *cft, return 1; } -/* - * Convert a token type to the char it represents. - */ -static char _token_type_to_char(int type) -{ - switch (type) { - case TOK_SECTION_B: - return SECTION_B_CHAR; - case TOK_SECTION_E: - return SECTION_E_CHAR; - default: - return 0; - } -} - -/* - * Returns: - * # of 'type' tokens in 'str'. - */ -static unsigned _count_tokens(const char *str, unsigned len, int type) -{ - char c; - - c = _token_type_to_char(type); - - return count_chars(str, len, c); -} +static int _putline_fn(const char *line, void *baton) { + FILE *fp = baton; + fprintf(fp, "%s\n", line); + return 1; +}; -const char *config_parent_name(const struct config_node *n) +int config_write(struct dm_config_tree *cft, const char *file, + int argc, char **argv) { - return (n->parent ? n->parent->key : "(root)"); -} -/* - * Heuristic function to make a quick guess as to whether a text - * region probably contains a valid config "section". (Useful for - * scanning areas of the disk for old metadata.) - * Config sections contain various tokens, may contain other sections - * and strings, and are delimited by begin (type 'TOK_SECTION_B') and - * end (type 'TOK_SECTION_E') tokens. As a quick heuristic, we just - * count the number of begin and end tokens, and see if they are - * non-zero and the counts match. - * Full validation of the section should be done with another function - * (for example, read_config_fd). - * - * Returns: - * 0 - probably is not a valid config section - * 1 - probably _is_ a valid config section - */ -unsigned maybe_config_section(const char *str, unsigned len) -{ - int begin_count; - int end_count; - - begin_count = _count_tokens(str, len, TOK_SECTION_B); - end_count = _count_tokens(str, len, TOK_SECTION_E); + const struct dm_config_node *cn; + int r = 1; + FILE *fp = NULL; - if (begin_count && end_count && (begin_count == end_count)) - return 1; - else + if (!file) { + fp = stdout; + file = "stdout"; + } else if (!(fp = fopen(file, "w"))) { + log_sys_error("open", file); return 0; -} - -static struct config_value *_clone_config_value(struct dm_pool *mem, const struct config_value *v) -{ - struct config_value *new_cv; - - if (!v) - return NULL; - - if (!(new_cv = _create_value(mem))) { - log_error("Failed to clone config value."); - return NULL; } - new_cv->type = v->type; - if (v->type == CFG_STRING) { - if (!(new_cv->v.str = dm_pool_strdup(mem, v->v.str))) { - log_error("Failed to clone config string value."); - return NULL; + log_verbose("Dumping configuration to %s", file); + if (!argc) { + if (!dm_config_write_node(cft->root, _putline_fn, fp)) { + log_error("Failure while writing to %s", file); + r = 0; } - } else - new_cv->v = v->v; - - if (v->next && !(new_cv->next = _clone_config_value(mem, v->next))) - return_NULL; - - return new_cv; -} - -struct config_node *clone_config_node(struct dm_pool *mem, const struct config_node *cn, - int siblings) -{ - struct config_node *new_cn; - - if (!cn) - return NULL; - - if (!(new_cn = _create_node(mem))) { - log_error("Failed to clone config node."); - return NULL; + } else while (argc--) { + if ((cn = dm_config_find_node(cft->root, *argv))) { + if (!dm_config_write_one_node(cn, _putline_fn, fp)) { + log_error("Failure while writing to %s", file); + r = 0; + } + } else { + log_error("Configuration node %s not found", *argv); + r = 0; + } + argv++; } - if ((cn->key && !(new_cn->key = dm_pool_strdup(mem, cn->key)))) { - log_error("Failed to clone config node key."); - return NULL; + if (fp && dm_fclose(fp)) { + stack; + r = 0; } - if ((cn->v && !(new_cn->v = _clone_config_value(mem, cn->v))) || - (cn->child && !(new_cn->child = clone_config_node(mem, cn->child, 1))) || - (siblings && cn->sib && !(new_cn->sib = clone_config_node(mem, cn->sib, siblings)))) - return_NULL; /* 'new_cn' released with mem pool */ - - return new_cn; + return r; } + |