summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Rajnoha <prajnoha@redhat.com>2013-03-05 17:36:10 +0100
committerPeter Rajnoha <prajnoha@redhat.com>2013-03-06 10:46:35 +0100
commit245b85692ec9f30153c1aa7216d81e65bb132792 (patch)
tree784d8017610b9fbec9cd2ca79ed91908ea0a2a80
parente38aaddb5e72a2123a03dedbb40c2aa594495c17 (diff)
downloadlvm2-245b85692ec9f30153c1aa7216d81e65bb132792.tar.gz
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)
-rw-r--r--doc/example.conf.in15
-rw-r--r--lib/commands/toolcontext.c5
-rw-r--r--lib/config/config.c235
-rw-r--r--lib/config/config.h21
-rw-r--r--lib/config/config_settings.h2
5 files changed, 274 insertions, 4 deletions
diff --git a/doc/example.conf.in b/doc/example.conf.in
index ab83c0c7c..9670b92fe 100644
--- a/doc/example.conf.in
+++ b/doc/example.conf.in
@@ -10,6 +10,20 @@
# N.B. Take care that each setting only appears once if uncommenting
# example settings in this file.
+# This section allows you to set the way the configuration settings are handled.
+config {
+
+ # If enabled, any LVM2 configuration mismatch is reported.
+ # This implies checking that the configuration key is understood
+ # by LVM2 and that the value of the key is of a proper type.
+ # If disabled, any configuration mismatch is ignored and default
+ # value is used instead without any warning (a message about the
+ # configuration key not being found is issued in verbose mode only).
+ checks = 1
+
+ # If enabled, any configuration mismatch aborts the LVM2 process.
+ abort_on_errors = 0
+}
# This section allows you to configure which block devices should
# be used by the LVM system.
@@ -359,7 +373,6 @@ shell {
# Miscellaneous global LVM2 settings
global {
-
# The file creation mask for any files and directories created.
# Interpreted as octal if the first digit is zero.
umask = 077
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
index 1cee20417..5dff34004 100644
--- a/lib/commands/toolcontext.c
+++ b/lib/commands/toolcontext.c
@@ -290,6 +290,11 @@ static int _process_config(struct cmd_context *cmd)
const char *lvmetad_socket;
int udev_disabled = 0;
+ if (!config_def_check(cmd, 0, 0, 0) && find_config_tree_bool(cmd, config_abort_on_errors_CFG)) {
+ log_error("LVM configuration invalid.");
+ return 0;
+ }
+
/* umask */
cmd->default_settings.umask = find_config_tree_int(cmd, global_umask_CFG);
diff --git a/lib/config/config.c b/lib/config/config.c
index 6f33c12b4..43f9d21ba 100644
--- a/lib/config/config.c
+++ b/lib/config/config.c
@@ -28,6 +28,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
+#include <ctype.h>
struct config_file {
time_t timestamp;
@@ -851,3 +852,237 @@ int config_write(struct dm_config_tree *cft, const char *file,
return r;
}
+static struct dm_config_value *_get_def_array_values(struct dm_config_tree *cft,
+ cfg_def_item_t *def)
+{
+ char *enc_value, *token, *p, *r;
+ struct dm_config_value *array = NULL, *v = NULL, *oldv = NULL;
+
+ if (!def->default_value.v_CFG_TYPE_STRING) {
+ if (!(array = dm_config_create_value(cft))) {
+ log_error("Failed to create default empty array for %s.", def->name);
+ return NULL;
+ }
+ array->type = DM_CFG_EMPTY_ARRAY;
+ return array;
+ }
+
+ if (!(p = token = enc_value = dm_strdup(def->default_value.v_CFG_TYPE_STRING))) {
+ log_error("_get_def_array_values: dm_strdup failed");
+ return NULL;
+ }
+ /* Proper value always starts with '#'. */
+ if (token[0] != '#')
+ goto bad;
+
+ while (token) {
+ /* Move to type identifier. Error on no char. */
+ token++;
+ if (!token[0])
+ goto bad;
+
+ /* Move to the actual value and decode any "##" into "#". */
+ p = token + 1;
+ while ((p = strchr(p, '#')) && p[1] == '#') {
+ memmove(p, p + 1, strlen(p));
+ p++;
+ }
+ /* Separate the value out of the whole string. */
+ if (p)
+ p[0] = '\0';
+
+ if (!(v = dm_config_create_value(cft))) {
+ log_error("Failed to create default config array value for %s.", def->name);
+ dm_free(enc_value);
+ }
+ if (oldv)
+ oldv->next = v;
+ if (!array)
+ array = v;
+
+ switch (toupper(token[0])) {
+ case 'I':
+ case 'B':
+ v->v.i = strtoll(token + 1, &r, 10);
+ if (*r)
+ goto bad;
+ v->type = DM_CFG_INT;
+ break;
+ case 'F':
+ v->v.f = strtod(token + 1, &r);
+ if (*r)
+ goto bad;
+ v->type = DM_CFG_FLOAT;
+ break;
+ case 'S':
+ if (!(r = dm_pool_alloc(cft->mem, strlen(token + 1)))) {
+ dm_free(enc_value);
+ log_error("Failed to duplicate token for default "
+ "array value of %s.", def->name);
+ return NULL;
+ }
+ memcpy(r, token + 1, strlen(token + 1));
+ v->v.str = r;
+ v->type = DM_CFG_STRING;
+ break;
+ default:
+ goto bad;
+ }
+
+ oldv = v;
+ token = p;
+ }
+
+ dm_free(enc_value);
+ return array;
+bad:
+ log_error(INTERNAL_ERROR "Default array value malformed for \"%s\", "
+ "value: \"%s\", token: \"%s\".", def->name,
+ def->default_value.v_CFG_TYPE_STRING, token);
+ dm_free(enc_value);
+ return NULL;
+}
+
+static struct dm_config_node *_add_def_node(struct dm_config_tree *cft,
+ struct config_def_tree_spec *spec,
+ struct dm_config_node *parent,
+ struct dm_config_node *relay,
+ cfg_def_item_t *def)
+{
+ struct dm_config_node *cn;
+ const char *str;
+
+ if (!(cn = dm_config_create_node(cft, def->name))) {
+ log_error("Failed to create default config setting node.");
+ return NULL;
+ }
+
+ if (!(def->type & CFG_TYPE_SECTION) && (!(cn->v = dm_config_create_value(cft)))) {
+ log_error("Failed to create default config setting node value.");
+ return NULL;
+ }
+
+ if (!(def->type & CFG_TYPE_ARRAY)) {
+ switch (def->type) {
+ case CFG_TYPE_SECTION:
+ cn->v = NULL;
+ break;
+ case CFG_TYPE_BOOL:
+ cn->v->type = DM_CFG_INT;
+ cn->v->v.i = cfg_def_get_default_value(def, CFG_TYPE_BOOL);
+ break;
+ case CFG_TYPE_INT:
+ cn->v->type = DM_CFG_INT;
+ cn->v->v.i = cfg_def_get_default_value(def, CFG_TYPE_INT);
+ break;
+ case CFG_TYPE_FLOAT:
+ cn->v->type = DM_CFG_FLOAT;
+ cn->v->v.f = cfg_def_get_default_value(def, CFG_TYPE_FLOAT);
+ break;
+ case CFG_TYPE_STRING:
+ cn->v->type = DM_CFG_STRING;
+ if (!(str = cfg_def_get_default_value(def, CFG_TYPE_STRING)))
+ str = "";
+ cn->v->v.str = str;
+ break;
+ default:
+ log_error(INTERNAL_ERROR "_add_def_node: unknown type");
+ return NULL;
+ break;
+ }
+ } else
+ cn->v = _get_def_array_values(cft, def);
+
+ cn->child = NULL;
+ if (parent) {
+ cn->parent = parent;
+ if (!parent->child)
+ parent->child = cn;
+ } else
+ cn->parent = cn;
+
+ if (relay)
+ relay->sib = cn;
+
+ return cn;
+}
+
+static int _should_skip_def_node(struct config_def_tree_spec *spec, int section_id, cfg_def_item_t *def)
+{
+ if (def->parent != section_id)
+ return 1;
+
+ switch (spec->type) {
+ case CFG_DEF_TREE_MISSING:
+ if ((def->flags & CFG_USED) ||
+ (def->flags & CFG_NAME_VARIABLE) ||
+ (def->since_version > spec->version))
+ return 1;
+ break;
+ case CFG_DEF_TREE_NEW:
+ if (def->since_version != spec->version)
+ return 1;
+ break;
+ default:
+ if (def->since_version > spec->version)
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
+static struct dm_config_node *_add_def_section_subtree(struct dm_config_tree *cft,
+ struct config_def_tree_spec *spec,
+ struct dm_config_node *parent,
+ struct dm_config_node *relay,
+ int section_id)
+{
+ struct dm_config_node *cn = NULL, *relay_sub = NULL, *tmp;
+ cfg_def_item_t *def;
+ int id;
+
+ for (id = 0; id < CFG_COUNT; id++) {
+ def = cfg_def_get_item_p(id);
+ if (_should_skip_def_node(spec, section_id, def))
+ continue;
+
+ if (!cn && !(cn = _add_def_node(cft, spec, parent, relay, cfg_def_get_item_p(section_id))))
+ goto bad;
+
+ if ((tmp = def->type == CFG_TYPE_SECTION ? _add_def_section_subtree(cft, spec, cn, relay_sub, id)
+ : _add_def_node(cft, spec, cn, relay_sub, def)))
+ relay_sub = tmp;
+ }
+
+ return cn;
+bad:
+ log_error("Failed to create default config section node.");
+ return NULL;
+}
+
+struct dm_config_tree *config_def_create_tree(struct config_def_tree_spec *spec)
+{
+ struct dm_config_tree *cft;
+ struct dm_config_node *root = NULL, *relay = NULL, *tmp;
+ int id;
+
+ if (!(cft = dm_config_create())) {
+ log_error("Failed to create default config tree.");
+ return NULL;
+ }
+
+ for (id = root_CFG_SECTION + 1; id < CFG_COUNT; id++) {
+ if (cfg_def_get_item_p(id)->parent != root_CFG_SECTION)
+ continue;
+
+ if ((tmp = _add_def_section_subtree(cft, spec, root, relay, id))) {
+ relay = tmp;
+ if (!root)
+ root = relay;
+ }
+ }
+
+ cft->root = root;
+ return cft;
+}
diff --git a/lib/config/config.h b/lib/config/config.h
index 624ef59b6..fb4993a20 100644
--- a/lib/config/config.h
+++ b/lib/config/config.h
@@ -76,6 +76,23 @@ typedef struct cfg_def_item {
const char *comment; /* brief comment */
} cfg_def_item_t;
+/* configuration definition tree types */
+typedef enum {
+ CFG_DEF_TREE_CURRENT, /* tree of nodes with values currently set in the config */
+ CFG_DEF_TREE_MISSING, /* tree of nodes missing in current config using default values */
+ CFG_DEF_TREE_COMPLETE, /* CURRENT + MISSING, the tree actually used within execution, not implemented yet */
+ CFG_DEF_TREE_DEFAULT, /* tree of all possible config nodes with default values */
+ CFG_DEF_TREE_NEW /* tree of all new nodes that appeared in given version */
+} cfg_def_tree_t;
+
+/* configuration definition tree specification */
+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 */
+};
+
/*
* Register ID for each possible item in the configuration tree.
*/
@@ -103,8 +120,8 @@ 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);
int config_file_read(struct dm_config_tree *cft);
-int config_write(struct dm_config_tree *cft, const char *file,
- int argc, char **argv);
+int config_write(struct dm_config_tree *cft, const char *file, int argc, char **argv);
+struct dm_config_tree *config_def_create_tree(struct config_def_tree_spec *spec);
void config_file_destroy(struct dm_config_tree *cft);
time_t config_file_timestamp(struct dm_config_tree *cft);
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
index a7d26256b..6229f2e6d 100644
--- a/lib/config/config_settings.h
+++ b/lib/config/config_settings.h
@@ -67,7 +67,7 @@ cfg_section(dmeventd_CFG_SECTION, "dmeventd", root_CFG_SECTION, 0, vsn(1, 2, 3),
cfg_section(tags_CFG_SECTION, "tags", root_CFG_SECTION, 0, vsn(1, 0, 18), NULL)
cfg(config_checks_CFG, "checks", config_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2, 2, 99), "Configuration tree check on each LVM command execution.")
-cfg(config_abort_on_errors_CFG, "abort_on_errors", config_CFG_SECTION, 0, CFG_TYPE_BOOL, 1, vsn(2,2,99), "Abort LVM command execution if configuration is invalid.")
+cfg(config_abort_on_errors_CFG, "abort_on_errors", config_CFG_SECTION, 0, CFG_TYPE_BOOL, 0, vsn(2,2,99), "Abort LVM command execution if configuration is invalid.")
cfg(devices_dir_CFG, "dir", devices_CFG_SECTION, 0, CFG_TYPE_STRING, DEFAULT_DEV_DIR, vsn(1, 0, 0), NULL)
cfg_array(devices_scan_CFG, "scan", devices_CFG_SECTION, 0, CFG_TYPE_STRING, "#S/dev", vsn(1, 0, 0), NULL)