1
0
mirror of git://sourceware.org/git/lvm2.git synced 2025-02-10 17:57:54 +03:00
lvm2/lib/config/config.c

1198 lines
30 KiB
C
Raw Normal View History

/*
* Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
2011-05-24 13:53:26 +00:00
* Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved.
*
2004-03-30 19:35:44 +00:00
* This file is part of LVM2.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License v.2.1.
2004-03-30 19:35:44 +00:00
*
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 19:35:44 +00:00
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
2002-11-18 14:01:16 +00:00
#include "lib.h"
2002-11-18 14:01:16 +00:00
#include "config.h"
#include "crc.h"
#include "device.h"
2004-05-04 18:28:15 +00:00
#include "str_list.h"
#include "toolcontext.h"
#include "lvm-file.h"
2002-11-18 14:01:16 +00:00
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
config: use config checks and add support for creating trees from config definition (config_def_create_tree fn) Configuration checking is initiated during config load/processing (_process_config fn) which is part of the command context creation/refresh. This patch also defines 5 types of trees that could be created from the configuration definition (config_settings.h), the cfg_def_tree_t: - CFG_DEF_TREE_CURRENT that denotes a tree of all the configuration nodes that are explicitly defined in lvm.conf/--config - CFG_DEF_TREE_MISSING that denotes a tree of all missing configuration nodes for which default valus are used since they're not explicitly used in lvm.conf/--config - CFG_DEF_TREE_DEFAULT that denotes a tree of all possible configuration nodes with default values assigned, no matter what the actual lvm.conf/--config is - CFG_DEF_TREE_NEW that denotes a tree of all new configuration nodes that appeared in given version - CFG_DEF_TREE_COMPLETE that denotes a tree of the whole configuration tree that is used in LVM2 (a combination of CFG_DEF_TREE_CURRENT + CFG_DEF_TREE_MISSING). This is not implemented yet, it will be added later... The function that creates the definition tree of given type: struct dm_config_tree *config_def_create_tree(struct config_def_tree_spec *spec); Where the "spec" specifies the tree type to be created: struct config_def_tree_spec { cfg_def_tree_t type; /* tree type */ uint16_t version; /* tree at this LVM2 version */ int ignoreadvanced; /* do not include advanced configs */ int ignoreunsupported; /* do not include unsupported configs */ }; This tree can be passed to already existing functions that write the tree on output (like we already do with cmd->cft). There is a new lvm.conf section called "config" with two new options: - config/checks which enables/disables checking (enabled by default) - config/abort_on_errors which enables/disables aborts on any type of mismatch found in the config (disabled by default)
2013-03-05 17:36:10 +01:00
#include <ctype.h>
struct config_file {
time_t timestamp;
off_t st_size;
char *filename;
int exists;
int keep_open;
struct device *dev;
};
static char _cfg_path[CFG_PATH_MAX_LEN];
/*
* Map each ID to respective definition of the configuration item.
*/
static struct cfg_def_item _cfg_def_items[CFG_COUNT + 1] = {
#define cfg_section(id, name, parent, flags, since_version, comment) {id, parent, name, CFG_TYPE_SECTION, {0}, flags, since_version, comment},
#define cfg(id, name, parent, flags, type, default_value, since_version, comment) {id, parent, name, type, {.v_##type = default_value}, flags, since_version, comment},
#define cfg_array(id, name, parent, flags, types, default_value, since_version, comment) {id, parent, name, CFG_TYPE_ARRAY | types, {.v_CFG_TYPE_STRING = default_value}, flags, since_version, comment},
#include "config_settings.h"
#undef cfg_section
#undef cfg
#undef cfg_array
};
/*
* public interface
*/
struct dm_config_tree *config_file_open(const char *filename, int keep_open)
2002-01-07 10:23:52 +00:00
{
struct dm_config_tree *cft = dm_config_create();
struct config_file *cf;
if (!cft)
return NULL;
if (!(cf = dm_pool_zalloc(cft->mem, sizeof(struct config_file)))) {
log_error("Failed to allocate config tree.");
goto fail;
}
2002-01-07 10:23:52 +00:00
cf->keep_open = keep_open;
dm_config_set_custom(cft, cf);
if (filename &&
!(cf->filename = dm_pool_strdup(cft->mem, filename))) {
log_error("Failed to duplicate filename.");
goto fail;
}
return cft;
fail:
dm_config_destroy(cft);
return NULL;
}
/*
* 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 config_file *cf = dm_config_get_custom(cft);
struct stat _info;
if (!info)
info = &_info;
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", cf->filename);
cf->exists = 0;
return 0;
}
cf->exists = 1;
cf->timestamp = info->st_ctime;
cf->st_size = info->st_size;
if (info->st_size == 0)
log_verbose("%s is empty", cf->filename);
else if (filename)
*filename = cf->filename;
return 1;
}
/*
* Return 1 if config files ought to be reloaded
*/
int config_file_changed(struct dm_config_tree *cft)
{
struct config_file *cf = dm_config_get_custom(cft);
struct stat info;
if (!cf->filename)
return 0;
if (stat(cf->filename, &info) == -1) {
/* Ignore a deleted config file: still use original data */
if (errno == ENOENT) {
if (!cf->exists)
return 0;
log_very_verbose("Config file %s has disappeared!",
cf->filename);
goto reload;
}
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",
cf->filename);
goto reload;
}
/* Unchanged? */
if (cf->timestamp == info.st_ctime && cf->st_size == info.st_size)
return 0;
reload:
log_verbose("Detected config file change to %s", cf->filename);
return 1;
}
void config_file_destroy(struct dm_config_tree *cft)
{
struct config_file *cf = dm_config_get_custom(cft);
if (cf && cf->dev)
if (!dev_close(cf->dev))
stack;
2002-01-07 10:23:52 +00:00
dm_config_destroy(cft);
}
struct dm_config_tree *config_file_open_and_read(const char *config_file)
{
struct dm_config_tree *cft;
struct stat info;
if (!(cft = config_file_open(config_file, 0))) {
log_error("config_tree allocation failed");
return NULL;
}
/* Is there a config file? */
if (stat(config_file, &info) == -1) {
if (errno == ENOENT)
return cft;
log_sys_error("stat", config_file);
goto bad;
}
log_very_verbose("Loading config file: %s", config_file);
if (!config_file_read(cft)) {
log_error("Failed to load config file %s", config_file);
goto bad;
}
return cft;
bad:
config_file_destroy(cft);
return NULL;
}
/*
* Returns config tree if it was removed.
*/
struct dm_config_tree *remove_overridden_config_tree(struct cmd_context *cmd)
{
struct dm_config_tree *old_cft = cmd->cft;
struct dm_config_tree *cft = dm_config_remove_cascaded_tree(cmd->cft);
if (!cft)
return NULL;
cmd->cft = cft;
return old_cft;
}
int override_config_tree_from_string(struct cmd_context *cmd,
const char *config_settings)
{
struct dm_config_tree *cft_new;
if (!(cft_new = dm_config_from_string(config_settings))) {
log_error("Failed to set overridden configuration entries.");
return 0;
}
cmd->cft = dm_config_insert_cascaded_tree(cft_new, cmd->cft);
return 1;
}
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 *fb, *fe;
2002-11-18 14:01:16 +00:00
int r = 0;
int use_mmap = 1;
off_t mmap_offset = 0;
char *buf = NULL;
/* Only use mmap with regular files */
if (!(dev->flags & DEV_REGULAR) || size2)
use_mmap = 0;
if (use_mmap) {
2006-08-17 18:23:44 +00:00
mmap_offset = offset % lvm_getpagesize();
2002-11-18 14:01:16 +00:00
/* 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));
2002-11-18 14:01:16 +00:00
goto out;
}
fb = fb + mmap_offset;
} else {
if (!(buf = dm_malloc(size + size2))) {
log_error("Failed to allocate circular buffer.");
return 0;
}
if (!dev_read_circular(dev, (uint64_t) offset, size,
(uint64_t) offset2, size2, buf)) {
goto out;
}
fb = buf;
2002-11-18 14:01:16 +00:00
}
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));
2002-11-18 14:01:16 +00:00
goto out;
}
fe = fb + size + size2;
if (!dm_config_parse(cft, fb, fe))
2008-01-30 13:19:47 +00:00
goto_out;
2002-11-18 14:01:16 +00:00
r = 1;
out:
if (!use_mmap)
dm_free(buf);
2002-11-18 14:01:16 +00:00
else {
/* unmap the file */
if (munmap(fb - mmap_offset, size + mmap_offset)) {
log_sys_error("munmap", dev_name(dev));
2002-11-18 14:01:16 +00:00
r = 0;
}
}
return r;
}
int config_file_read(struct dm_config_tree *cft)
2002-11-18 14:01:16 +00:00
{
const char *filename = NULL;
struct config_file *cf = dm_config_get_custom(cft);
2002-11-18 14:01:16 +00:00
struct stat info;
int r;
if (!config_file_check(cft, &filename, &info))
2011-09-01 21:04:14 +00:00
return_0;
2004-05-04 18:28:15 +00:00
/* Nothing to do. E.g. empty file. */
if (!filename)
return 1;
if (!cf->dev) {
if (!(cf->dev = dev_create_file(filename, NULL, NULL, 1)))
return_0;
if (!dev_open_readonly_buffered(cf->dev))
return_0;
}
r = config_file_read_fd(cft, cf->dev, 0, (size_t) info.st_size, 0, 0,
(checksum_fn_t) NULL, 0);
2002-11-18 14:01:16 +00:00
if (!cf->keep_open) {
if (!dev_close(cf->dev))
stack;
cf->dev = NULL;
}
2002-11-18 14:01:16 +00:00
return r;
}
time_t config_file_timestamp(struct dm_config_tree *cft)
{
struct config_file *cf = dm_config_get_custom(cft);
assert(cf);
return cf->timestamp;
}
#define cfg_def_get_item_p(id) (&_cfg_def_items[id])
#define cfg_def_get_default_value(item,type) item->default_value.v_##type
#define cfg_def_get_path(item) (_cfg_def_make_path(_cfg_path,CFG_PATH_MAX_LEN,item->id,item),_cfg_path)
static int _cfg_def_make_path(char *buf, size_t buf_size, int id, cfg_def_item_t *item)
{
int parent_id = item->parent;
int count, n;
if (id == parent_id)
return 0;
count = _cfg_def_make_path(buf, buf_size, parent_id, cfg_def_get_item_p(parent_id));
if ((n = dm_snprintf(buf + count, buf_size - count, "%s%s",
count ? "/" : "",
item->flags & CFG_NAME_VARIABLE ? "#" : item->name)) < 0) {
log_error(INTERNAL_ERROR "_cfg_def_make_path: supplied buffer too small for %s/%s",
cfg_def_get_item_p(parent_id)->name, item->name);
buf[0] = '\0';
return 0;
}
return count + n;
}
int config_def_get_path(char *buf, size_t buf_size, int id)
{
return _cfg_def_make_path(buf, buf_size, id, cfg_def_get_item_p(id));
}
static void _get_type_name(char *buf, size_t buf_size, cfg_def_type_t type)
{
(void) dm_snprintf(buf, buf_size, "%s%s%s%s%s%s",
(type & CFG_TYPE_ARRAY) ?
((type & ~CFG_TYPE_ARRAY) ?
" array with values of type:" : " array") : "",
(type & CFG_TYPE_SECTION) ? " section" : "",
(type & CFG_TYPE_BOOL) ? " boolean" : "",
(type & CFG_TYPE_INT) ? " integer" : "",
(type & CFG_TYPE_FLOAT) ? " float" : "",
(type & CFG_TYPE_STRING) ? " string" : "");
}
static void _log_type_error(const char *path, cfg_def_type_t actual,
cfg_def_type_t expected, int suppress_messages)
{
static char actual_type_name[128];
static char expected_type_name[128];
_get_type_name(actual_type_name, sizeof(actual_type_name), actual);
_get_type_name(expected_type_name, sizeof(expected_type_name), expected);
log_warn_suppress(suppress_messages, "Configuration setting \"%s\" has invalid type. "
"Found%s, expected%s.", path,
actual_type_name, expected_type_name);
}
static int _config_def_check_node_single_value(const char *rp, const struct dm_config_value *v,
const cfg_def_item_t *def, int suppress_messages)
{
/* Check empty array first if present. */
if (v->type == DM_CFG_EMPTY_ARRAY) {
if (!(def->type & CFG_TYPE_ARRAY)) {
_log_type_error(rp, CFG_TYPE_ARRAY, def->type, suppress_messages);
return 0;
}
if (!(def->flags & CFG_ALLOW_EMPTY)) {
log_warn_suppress(suppress_messages,
"Configuration setting \"%s\" invalid. Empty value not allowed.", rp);
return 0;
}
return 1;
}
switch (v->type) {
case DM_CFG_INT:
if (!(def->type & CFG_TYPE_INT) && !(def->type & CFG_TYPE_BOOL)) {
_log_type_error(rp, CFG_TYPE_INT, def->type, suppress_messages);
return 0;
}
break;
case DM_CFG_FLOAT:
if (!(def->type & CFG_TYPE_FLOAT)) {
_log_type_error(rp, CFG_TYPE_FLOAT, def->type, suppress_messages);
return 0;
}
break;
case DM_CFG_STRING:
if (def->type & CFG_TYPE_BOOL) {
if (!dm_config_value_is_bool(v)) {
log_warn_suppress(suppress_messages,
"Configuration setting \"%s\" invalid. "
"Found string value \"%s\", "
"expected boolean value: 0/1, \"y/n\", "
"\"yes/no\", \"on/off\", "
"\"true/false\".", rp, v->v.str);
return 0;
}
} else if (!(def->type & CFG_TYPE_STRING)) {
_log_type_error(rp, CFG_TYPE_STRING, def->type, suppress_messages);
return 0;
}
break;
default: ;
}
return 1;
}
static int _config_def_check_node_value(const char *rp, const struct dm_config_value *v,
const cfg_def_item_t *def, int suppress_messages)
{
if (!v) {
if (def->type != CFG_TYPE_SECTION) {
_log_type_error(rp, CFG_TYPE_SECTION, def->type, suppress_messages);
return 0;
}
return 1;
}
if (v->next) {
if (!(def->type & CFG_TYPE_ARRAY)) {
_log_type_error(rp, CFG_TYPE_ARRAY, def->type, suppress_messages);
return 0;
}
}
do {
if (!_config_def_check_node_single_value(rp, v, def, suppress_messages))
return 0;
v = v->next;
} while (v);
return 1;
}
static int _config_def_check_node(const char *vp, char *pvp, char *rp, char *prp,
size_t buf_size, struct dm_config_node *cn,
struct dm_hash_table *ht, int suppress_messages)
{
cfg_def_item_t *def;
int sep = vp != pvp; /* don't use '/' separator for top-level node */
if (dm_snprintf(pvp, buf_size, "%s%s", sep ? "/" : "", cn->key) < 0 ||
dm_snprintf(prp, buf_size, "%s%s", sep ? "/" : "", cn->key) < 0) {
log_error("Failed to construct path for configuration node %s.", cn->key);
return 0;
}
if (!(def = (cfg_def_item_t *) dm_hash_lookup(ht, vp))) {
/* If the node is not a section but a setting, fail now. */
if (cn->v) {
log_warn_suppress(suppress_messages,
"Configuration setting \"%s\" unknown.", rp);
config: add support for enhanced config node output There's a possibility to interconnect the dm_config_node with an ID, which in our case is used to reference the configuration definition ID from config_settings.h. So simply interconnecting struct dm_config_node with struct cfg_def_item. This patch also adds support for enhanced config node output besides existing "output line by line". This patch adds a possibility to register a callback that gets called *before* the config node is processed line by line (for example to include any headers on output) and *after* the config node is processed line by line (to include any footers on output). Also, it adds the config node reference itself as the callback arg in addition to have a possibility to extract more information from the config node itself if needed when processing the output callback (e.g. the key name, the id, or whether this is a section or a value etc...). If the config node from lvm.conf/--config tree is recognized and valid, it's always coupled with the config node definition ID from config_settings.h: struct dm_config_node { int id; const char *key; struct dm_config_node *parent, *sib, *child; struct dm_config_value *v; } For example if the dm_config_node *cn holds "devices/dev" configuration, then the cn->id holds "devices_dev_CFG" ID from config_settings.h, -1 if not found in config_settings.h and 0 if matching has not yet been done. To support the enhanced config node output, a new structure has been defined in libdevmapper to register it: struct dm_config_node_out_spec { dm_config_node_out_fn prefix_fn; /* called before processing config node lines */ dm_config_node_out_fn line_fn; /* called for each config node line */ dm_config_node_out_fn suffix_fn; /* called after processing config node lines */ }; Where dm_config_node_out_fn is: typedef int (*dm_config_node_out_fn)(const struct dm_config_node *cn, const char *line, void *baton); (so in comparison to existing callbacks for config node output, it has an extra dm_config_node *cn arg in addition) This patch also adds these functions to libdevmapper: - dm_config_write_node_out - dm_config_write_one_node_out ...which have exactly the same functionality as their counterparts without the "out" suffix. The "*_out" functions adds the extra hooks for enhanced config output (prefix_fn and suffix_fn mentioned above). One can still use the old interface for config node output, this is just an enhancement for those who'd like to modify the output more extensively.
2013-03-05 18:02:13 +01:00
cn->id = -1;
return 0;
}
/* If the node is a section, try if the section name is variable. */
/* Modify virtual path vp in situ and replace the key name with a '#'. */
/* The real path without '#' is still stored in rp variable. */
pvp[sep] = '#', pvp[sep + 1] = '\0';
if (!(def = (cfg_def_item_t *) dm_hash_lookup(ht, vp))) {
log_warn_suppress(suppress_messages,
"Configuration section \"%s\" unknown.", rp);
config: add support for enhanced config node output There's a possibility to interconnect the dm_config_node with an ID, which in our case is used to reference the configuration definition ID from config_settings.h. So simply interconnecting struct dm_config_node with struct cfg_def_item. This patch also adds support for enhanced config node output besides existing "output line by line". This patch adds a possibility to register a callback that gets called *before* the config node is processed line by line (for example to include any headers on output) and *after* the config node is processed line by line (to include any footers on output). Also, it adds the config node reference itself as the callback arg in addition to have a possibility to extract more information from the config node itself if needed when processing the output callback (e.g. the key name, the id, or whether this is a section or a value etc...). If the config node from lvm.conf/--config tree is recognized and valid, it's always coupled with the config node definition ID from config_settings.h: struct dm_config_node { int id; const char *key; struct dm_config_node *parent, *sib, *child; struct dm_config_value *v; } For example if the dm_config_node *cn holds "devices/dev" configuration, then the cn->id holds "devices_dev_CFG" ID from config_settings.h, -1 if not found in config_settings.h and 0 if matching has not yet been done. To support the enhanced config node output, a new structure has been defined in libdevmapper to register it: struct dm_config_node_out_spec { dm_config_node_out_fn prefix_fn; /* called before processing config node lines */ dm_config_node_out_fn line_fn; /* called for each config node line */ dm_config_node_out_fn suffix_fn; /* called after processing config node lines */ }; Where dm_config_node_out_fn is: typedef int (*dm_config_node_out_fn)(const struct dm_config_node *cn, const char *line, void *baton); (so in comparison to existing callbacks for config node output, it has an extra dm_config_node *cn arg in addition) This patch also adds these functions to libdevmapper: - dm_config_write_node_out - dm_config_write_one_node_out ...which have exactly the same functionality as their counterparts without the "out" suffix. The "*_out" functions adds the extra hooks for enhanced config output (prefix_fn and suffix_fn mentioned above). One can still use the old interface for config node output, this is just an enhancement for those who'd like to modify the output more extensively.
2013-03-05 18:02:13 +01:00
cn->id = -1;
return 0;
}
}
def->flags |= CFG_USED;
config: add support for enhanced config node output There's a possibility to interconnect the dm_config_node with an ID, which in our case is used to reference the configuration definition ID from config_settings.h. So simply interconnecting struct dm_config_node with struct cfg_def_item. This patch also adds support for enhanced config node output besides existing "output line by line". This patch adds a possibility to register a callback that gets called *before* the config node is processed line by line (for example to include any headers on output) and *after* the config node is processed line by line (to include any footers on output). Also, it adds the config node reference itself as the callback arg in addition to have a possibility to extract more information from the config node itself if needed when processing the output callback (e.g. the key name, the id, or whether this is a section or a value etc...). If the config node from lvm.conf/--config tree is recognized and valid, it's always coupled with the config node definition ID from config_settings.h: struct dm_config_node { int id; const char *key; struct dm_config_node *parent, *sib, *child; struct dm_config_value *v; } For example if the dm_config_node *cn holds "devices/dev" configuration, then the cn->id holds "devices_dev_CFG" ID from config_settings.h, -1 if not found in config_settings.h and 0 if matching has not yet been done. To support the enhanced config node output, a new structure has been defined in libdevmapper to register it: struct dm_config_node_out_spec { dm_config_node_out_fn prefix_fn; /* called before processing config node lines */ dm_config_node_out_fn line_fn; /* called for each config node line */ dm_config_node_out_fn suffix_fn; /* called after processing config node lines */ }; Where dm_config_node_out_fn is: typedef int (*dm_config_node_out_fn)(const struct dm_config_node *cn, const char *line, void *baton); (so in comparison to existing callbacks for config node output, it has an extra dm_config_node *cn arg in addition) This patch also adds these functions to libdevmapper: - dm_config_write_node_out - dm_config_write_one_node_out ...which have exactly the same functionality as their counterparts without the "out" suffix. The "*_out" functions adds the extra hooks for enhanced config output (prefix_fn and suffix_fn mentioned above). One can still use the old interface for config node output, this is just an enhancement for those who'd like to modify the output more extensively.
2013-03-05 18:02:13 +01:00
cn->id = def->id;
if (!_config_def_check_node_value(rp, cn->v, def, suppress_messages))
return 0;
def->flags |= CFG_VALID;
return 1;
}
static int _config_def_check_tree(const char *vp, char *pvp, char *rp, char *prp,
size_t buf_size, struct dm_config_node *root,
struct dm_hash_table *ht, int suppress_messages)
{
struct dm_config_node *cn;
int valid, r = 1;
size_t len;
for (cn = root->child; cn; cn = cn->sib) {
if ((valid = _config_def_check_node(vp, pvp, rp, prp, buf_size,
cn, ht, suppress_messages)) && !cn->v) {
len = strlen(rp);
valid = _config_def_check_tree(vp, pvp + strlen(pvp),
rp, prp + len, buf_size - len, cn, ht,
suppress_messages);
}
if (!valid)
r = 0;
}
return r;
}
int config_def_check(struct cmd_context *cmd, int force, int skip, int suppress_messages)
{
cfg_def_item_t *def;
struct dm_config_node *cn;
char *vp = _cfg_path, rp[CFG_PATH_MAX_LEN];
size_t rplen;
int id, r = 1;
/*
* vp = virtual path, it might contain substitutes for variable parts
* of the path, used while working with the hash
* rp = real path, the real path of the config element as found in the
* configuration, used for message output
*/
/*
* If the check has already been done and 'skip' is set,
* skip the actual check and use last result if available.
* If not available, we must do the check. The global status
* is stored in root node.
*/
def = cfg_def_get_item_p(root_CFG_SECTION);
if (skip && (def->flags & CFG_USED))
return def->flags & CFG_VALID;
/* Clear 'used' and 'valid' status flags. */
for (id = 0; id < CFG_COUNT; id++) {
def = cfg_def_get_item_p(id);
def->flags &= ~(CFG_USED | CFG_VALID);
}
if (!force && !find_config_tree_bool(cmd, config_checks_CFG)) {
if (cmd->cft_def_hash) {
dm_hash_destroy(cmd->cft_def_hash);
cmd->cft_def_hash = NULL;
}
return 1;
}
/*
* Create a hash of all possible configuration
* sections and settings with full path as a key.
* If section name is variable, use '#' as a substitute.
*/
if (!cmd->cft_def_hash) {
if (!(cmd->cft_def_hash = dm_hash_create(64))) {
log_error("Failed to create configuration definition hash.");
r = 0; goto out;
}
for (id = 1; id < CFG_COUNT; id++) {
def = cfg_def_get_item_p(id);
if (!cfg_def_get_path(def)) {
dm_hash_destroy(cmd->cft_def_hash);
cmd->cft_def_hash = NULL;
r = 0; goto out;
}
if (!dm_hash_insert(cmd->cft_def_hash, vp, def)) {
log_error("Failed to insert configuration to hash.");
r = 0;
goto out;
}
}
}
cfg_def_get_item_p(root_CFG_SECTION)->flags |= CFG_USED;
/*
* Allow only sections as top-level elements.
* Iterate top-level sections and dive deeper.
* If any of subsequent checks fails, the whole check fails.
*/
for (cn = cmd->cft->root; cn; cn = cn->sib) {
if (!cn->v) {
/* top level node: vp=vp, rp=rp */
if (!_config_def_check_node(vp, vp, rp, rp,
CFG_PATH_MAX_LEN,
cn, cmd->cft_def_hash,
suppress_messages)) {
r = 0; continue;
}
rplen = strlen(rp);
if (!_config_def_check_tree(vp, vp + strlen(vp),
rp, rp + rplen,
CFG_PATH_MAX_LEN - rplen,
cn, cmd->cft_def_hash,
suppress_messages))
r = 0;
} else {
log_error_suppress(suppress_messages,
"Configuration setting \"%s\" invalid. "
"It's not part of any section.", cn->key);
r = 0;
}
}
out:
if (r) {
cfg_def_get_item_p(root_CFG_SECTION)->flags |= CFG_VALID;
def->flags |= CFG_VALID;
} else {
cfg_def_get_item_p(root_CFG_SECTION)->flags &= ~CFG_VALID;
def->flags &= ~CFG_VALID;
}
return r;
}
const struct dm_config_node *find_config_tree_node(struct cmd_context *cmd, int id)
{
return dm_config_tree_find_node(cmd->cft, cfg_def_get_path(cfg_def_get_item_p(id)));
}
const char *find_config_tree_str(struct cmd_context *cmd, int id)
{
cfg_def_item_t *item = cfg_def_get_item_p(id);
const char *path = cfg_def_get_path(item);
if (item->type != CFG_TYPE_STRING)
log_error(INTERNAL_ERROR "%s cfg tree element not declared as string.", path);
return dm_config_tree_find_str(cmd->cft, path, cfg_def_get_default_value(item, CFG_TYPE_STRING));
}
const char *find_config_tree_str_allow_empty(struct cmd_context *cmd, int id)
{
cfg_def_item_t *item = cfg_def_get_item_p(id);
const char *path = cfg_def_get_path(item);
if (item->type != CFG_TYPE_STRING)
log_error(INTERNAL_ERROR "%s cfg tree element not declared as string.", path);
if (!(item->flags & CFG_ALLOW_EMPTY))
log_error(INTERNAL_ERROR "%s cfg tree element not declared to allow empty values.", path);
return dm_config_tree_find_str_allow_empty(cmd->cft, path, cfg_def_get_default_value(item, CFG_TYPE_STRING));
}
int find_config_tree_int(struct cmd_context *cmd, int id)
{
cfg_def_item_t *item = cfg_def_get_item_p(id);
const char *path = cfg_def_get_path(item);
if (item->type != CFG_TYPE_INT)
log_error(INTERNAL_ERROR "%s cfg tree element not declared as integer.", path);
return dm_config_tree_find_int(cmd->cft, path, cfg_def_get_default_value(item, CFG_TYPE_INT));
}
int64_t find_config_tree_int64(struct cmd_context *cmd, int id)
2011-02-18 14:08:22 +00:00
{
cfg_def_item_t *item = cfg_def_get_item_p(id);
const char *path = cfg_def_get_path(item);
if (item->type != CFG_TYPE_INT)
log_error(INTERNAL_ERROR "%s cfg tree element not declared as integer.", path);
return dm_config_tree_find_int64(cmd->cft, path, cfg_def_get_default_value(item, CFG_TYPE_INT));
2011-02-18 14:08:22 +00:00
}
float find_config_tree_float(struct cmd_context *cmd, int id)
{
cfg_def_item_t *item = cfg_def_get_item_p(id);
const char *path = cfg_def_get_path(item);
if (item->type != CFG_TYPE_FLOAT)
log_error(INTERNAL_ERROR "%s cfg tree element not declared as float.", path);
return dm_config_tree_find_float(cmd->cft, path, cfg_def_get_default_value(item, CFG_TYPE_FLOAT));
}
int find_config_tree_bool(struct cmd_context *cmd, int id)
{
cfg_def_item_t *item = cfg_def_get_item_p(id);
const char *path = cfg_def_get_path(item);
if (item->type != CFG_TYPE_BOOL)
log_error(INTERNAL_ERROR "%s cfg tree element not declared as boolean.", path);
return dm_config_tree_find_bool(cmd->cft, path, cfg_def_get_default_value(item, CFG_TYPE_BOOL));
2002-07-11 14:07:43 +00:00
}
2004-05-04 18:28:15 +00:00
/* Insert cn2 after cn1 */
static void _insert_config_node(struct dm_config_node **cn1,
struct dm_config_node *cn2)
2004-05-04 18:28:15 +00:00
{
if (!*cn1) {
*cn1 = cn2;
cn2->sib = NULL;
} else {
cn2->sib = (*cn1)->sib;
(*cn1)->sib = cn2;
}
}
/*
* Merge section cn2 into section cn1 (which has the same name)
* overwriting any existing cn1 nodes with matching names.
*/
static void _merge_section(struct dm_config_node *cn1, struct dm_config_node *cn2)
2004-05-04 18:28:15 +00:00
{
struct dm_config_node *cn, *nextn, *oldn;
struct dm_config_value *cv;
2004-05-04 18:28:15 +00:00
for (cn = cn2->child; cn; cn = nextn) {
nextn = cn->sib;
/* Skip "tags" */
if (!strcmp(cn->key, "tags"))
continue;
/* Subsection? */
if (!cn->v)
/* Ignore - we don't have any of these yet */
continue;
/* Not already present? */
if (!(oldn = dm_config_find_node(cn1->child, cn->key))) {
2004-05-04 18:28:15 +00:00
_insert_config_node(&cn1->child, cn);
continue;
}
/* Merge certain value lists */
if ((!strcmp(cn1->key, "activation") &&
!strcmp(cn->key, "volume_list")) ||
(!strcmp(cn1->key, "devices") &&
(!strcmp(cn->key, "filter") || !strcmp(cn->key, "types")))) {
cv = cn->v;
while (cv->next)
cv = cv->next;
cv->next = oldn->v;
}
/* Replace values */
oldn->v = cn->v;
}
}
static int _match_host_tags(struct dm_list *tags, const struct dm_config_node *tn)
2004-05-04 18:28:15 +00:00
{
const struct dm_config_value *tv;
2004-05-04 18:28:15 +00:00
const char *str;
for (tv = tn->v; tv; tv = tv->next) {
if (tv->type != DM_CFG_STRING)
2004-05-04 18:28:15 +00:00
continue;
str = tv->v.str;
if (*str == '@')
str++;
if (!*str)
continue;
if (str_list_match_item(tags, str))