diff options
Diffstat (limited to 'ctdb/common/conf.c')
-rw-r--r-- | ctdb/common/conf.c | 1391 |
1 files changed, 1391 insertions, 0 deletions
diff --git a/ctdb/common/conf.c b/ctdb/common/conf.c new file mode 100644 index 0000000..e849ff4 --- /dev/null +++ b/ctdb/common/conf.c @@ -0,0 +1,1391 @@ +/* + Configuration file handling on top of tini + + Copyright (C) Amitay Isaacs 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/locale.h" + +#include <talloc.h> + +#include "lib/util/dlinklist.h" +#include "lib/util/tini.h" +#include "lib/util/debug.h" + +#include "common/conf.h" + +struct conf_value { + enum conf_type type; + union { + const char *string; + int integer; + bool boolean; + } data; +}; + +union conf_pointer { + const char **string; + int *integer; + bool *boolean; +}; + +struct conf_option { + struct conf_option *prev, *next; + + const char *name; + enum conf_type type; + void *validate; + + struct conf_value default_value; + bool default_set; + + struct conf_value *value, *new_value; + union conf_pointer ptr; + bool temporary_modified; +}; + +struct conf_section { + struct conf_section *prev, *next; + + const char *name; + conf_validate_section_fn validate; + struct conf_option *option; +}; + +struct conf_context { + const char *filename; + struct conf_section *section; + bool define_failed; + bool ignore_unknown; + bool reload; + bool validation_active; +}; + +/* + * Functions related to conf_value + */ + +static int string_to_string(TALLOC_CTX *mem_ctx, + const char *str, + const char **str_val) +{ + char *t; + + if (str == NULL) { + return EINVAL; + } + + t = talloc_strdup(mem_ctx, str); + if (t == NULL) { + return ENOMEM; + } + + *str_val = t; + return 0; +} + +static int string_to_integer(const char *str, int *int_val) +{ + long t; + char *endptr = NULL; + + if (str == NULL) { + return EINVAL; + } + + t = strtol(str, &endptr, 0); + if (*str != '\0' || endptr == NULL) { + if (t < 0 || t > INT_MAX) { + return EINVAL; + } + + *int_val = (int)t; + return 0; + } + + return EINVAL; +} + +static int string_to_boolean(const char *str, bool *bool_val) +{ + if (strcasecmp(str, "true") == 0 || strcasecmp(str, "yes") == 0) { + *bool_val = true; + return 0; + } + + if (strcasecmp(str, "false") == 0 || strcasecmp(str, "no") == 0) { + *bool_val = false; + return 0; + } + + return EINVAL; +} + +static int conf_value_from_string(TALLOC_CTX *mem_ctx, + const char *str, + struct conf_value *value) +{ + int ret; + + switch (value->type) { + case CONF_STRING: + ret = string_to_string(mem_ctx, str, &value->data.string); + break; + + case CONF_INTEGER: + ret = string_to_integer(str, &value->data.integer); + break; + + case CONF_BOOLEAN: + ret = string_to_boolean(str, &value->data.boolean); + break; + + default: + return EINVAL; + } + + return ret; +} + +static bool conf_value_compare(struct conf_value *old, struct conf_value *new) +{ + if (old == NULL || new == NULL) { + return false; + } + + if (old->type != new->type) { + return false; + } + + switch (old->type) { + case CONF_STRING: + if (old->data.string == NULL && new->data.string == NULL) { + return true; + } + if (old->data.string != NULL && new->data.string != NULL) { + if (strcmp(old->data.string, new->data.string) == 0) { + return true; + } + } + break; + + case CONF_INTEGER: + if (old->data.integer == new->data.integer) { + return true; + } + break; + + case CONF_BOOLEAN: + if (old->data.boolean == new->data.boolean) { + return true; + } + break; + } + + return false; +} + +static int conf_value_copy(TALLOC_CTX *mem_ctx, + struct conf_value *src, + struct conf_value *dst) +{ + if (src->type != dst->type) { + return EINVAL; + } + + switch (src->type) { + case CONF_STRING: + if (dst->data.string != NULL) { + talloc_free(discard_const(dst->data.string)); + } + if (src->data.string == NULL) { + dst->data.string = NULL; + } else { + dst->data.string = talloc_strdup( + mem_ctx, src->data.string); + if (dst->data.string == NULL) { + return ENOMEM; + } + } + break; + + case CONF_INTEGER: + dst->data.integer = src->data.integer; + break; + + case CONF_BOOLEAN: + dst->data.boolean = src->data.boolean; + break; + + default: + return EINVAL; + } + + return 0; +} + +static void conf_value_dump(const char *key, + struct conf_value *value, + bool is_default, + bool is_temporary, + FILE *fp) +{ + if ((value->type == CONF_STRING && value->data.string == NULL) || + is_default) { + fprintf(fp, "\t# %s = ", key); + } else { + fprintf(fp, "\t%s = ", key); + } + + switch (value->type) { + case CONF_STRING: + if (value->data.string != NULL) { + fprintf(fp, "%s", value->data.string); + } + break; + + case CONF_INTEGER: + fprintf(fp, "%d", value->data.integer); + break; + + case CONF_BOOLEAN: + fprintf(fp, "%s", (value->data.boolean ? "true" : "false")); + break; + } + + if (is_temporary) { + fprintf(fp, " # temporary"); + } + + fprintf(fp, "\n"); +} + +/* + * Functions related to conf_option + */ + +static struct conf_option *conf_option_find(struct conf_section *s, + const char *key) +{ + struct conf_option *opt; + + for (opt = s->option; opt != NULL; opt = opt->next) { + if (strcmp(opt->name, key) == 0) { + return opt; + } + } + + return NULL; +} + +static void conf_option_set_ptr_value(struct conf_option *opt) +{ + switch (opt->type) { + case CONF_STRING: + if (opt->ptr.string != NULL) { + *(opt->ptr.string) = opt->value->data.string; + } + break; + + case CONF_INTEGER: + if (opt->ptr.integer != NULL) { + *(opt->ptr.integer) = opt->value->data.integer; + } + break; + + case CONF_BOOLEAN: + if (opt->ptr.boolean != NULL) { + *(opt->ptr.boolean) = opt->value->data.boolean; + } + break; + } +} + +static void conf_option_default(struct conf_option *opt); + +static int conf_option_add(struct conf_section *s, + const char *key, + enum conf_type type, + void *validate, + struct conf_option **popt) +{ + struct conf_option *opt; + + opt = conf_option_find(s, key); + if (opt != NULL) { + D_ERR("conf: option \"%s\" already exists\n", key); + return EEXIST; + } + + opt = talloc_zero(s, struct conf_option); + if (opt == NULL) { + return ENOMEM; + } + + opt->name = talloc_strdup(opt, key); + if (opt->name == NULL) { + talloc_free(opt); + return ENOMEM; + } + + opt->type = type; + opt->validate = validate; + + DLIST_ADD_END(s->option, opt); + + if (popt != NULL) { + *popt = opt; + } + + return 0; +} + +static int conf_option_set_default(struct conf_option *opt, + struct conf_value *default_value) +{ + int ret; + + opt->default_value.type = opt->type; + + ret = conf_value_copy(opt, default_value, &opt->default_value); + if (ret != 0) { + return ret; + } + + opt->default_set = true; + opt->temporary_modified = false; + + return 0; +} + +static void conf_option_set_ptr(struct conf_option *opt, + union conf_pointer *ptr) +{ + opt->ptr = *ptr; +} + +static bool conf_option_validate_string(struct conf_option *opt, + struct conf_value *value, + enum conf_update_mode mode) +{ + conf_validate_string_option_fn validate = + (conf_validate_string_option_fn)opt->validate; + + return validate(opt->name, + opt->value->data.string, + value->data.string, + mode); +} + +static bool conf_option_validate_integer(struct conf_option *opt, + struct conf_value *value, + enum conf_update_mode mode) +{ + conf_validate_integer_option_fn validate = + (conf_validate_integer_option_fn)opt->validate; + + return validate(opt->name, + opt->value->data.integer, + value->data.integer, + mode); +} + +static bool conf_option_validate_boolean(struct conf_option *opt, + struct conf_value *value, + enum conf_update_mode mode) +{ + conf_validate_boolean_option_fn validate = + (conf_validate_boolean_option_fn)opt->validate; + + return validate(opt->name, + opt->value->data.boolean, + value->data.boolean, + mode); +} + +static bool conf_option_validate(struct conf_option *opt, + struct conf_value *value, + enum conf_update_mode mode) +{ + int ret; + + if (opt->validate == NULL) { + return true; + } + + switch (opt->type) { + case CONF_STRING: + ret = conf_option_validate_string(opt, value, mode); + break; + + case CONF_INTEGER: + ret = conf_option_validate_integer(opt, value, mode); + break; + + case CONF_BOOLEAN: + ret = conf_option_validate_boolean(opt, value, mode); + break; + + default: + ret = EINVAL; + } + + return ret; +} + +static bool conf_option_same_value(struct conf_option *opt, + struct conf_value *new_value) +{ + return conf_value_compare(opt->value, new_value); +} + +static int conf_option_new_value(struct conf_option *opt, + struct conf_value *new_value, + enum conf_update_mode mode) +{ + int ret; + bool ok; + + if (opt->new_value != &opt->default_value) { + TALLOC_FREE(opt->new_value); + } + + if (new_value == &opt->default_value) { + /* + * This happens only during load/reload. Set the value to + * default value, so if the config option is dropped from + * config file, then it get's reset to default. + */ + opt->new_value = &opt->default_value; + } else { + ok = conf_option_validate(opt, new_value, mode); + if (!ok) { + D_ERR("conf: validation for option \"%s\" failed\n", + opt->name); + return EINVAL; + } + + opt->new_value = talloc_zero(opt, struct conf_value); + if (opt->new_value == NULL) { + return ENOMEM; + } + + opt->new_value->type = opt->value->type; + ret = conf_value_copy(opt, new_value, opt->new_value); + if (ret != 0) { + return ret; + } + } + + conf_option_set_ptr_value(opt); + + if (new_value != &opt->default_value) { + if (mode == CONF_MODE_API) { + opt->temporary_modified = true; + } else { + opt->temporary_modified = false; + } + } + + return 0; +} + +static int conf_option_new_default_value(struct conf_option *opt, + enum conf_update_mode mode) +{ + return conf_option_new_value(opt, &opt->default_value, mode); +} + +static void conf_option_default(struct conf_option *opt) +{ + if (! opt->default_set) { + return; + } + + if (opt->value != &opt->default_value) { + TALLOC_FREE(opt->value); + } + + opt->value = &opt->default_value; + conf_option_set_ptr_value(opt); +} + +static void conf_option_reset(struct conf_option *opt) +{ + if (opt->new_value != &opt->default_value) { + TALLOC_FREE(opt->new_value); + } + + conf_option_set_ptr_value(opt); +} + +static void conf_option_update(struct conf_option *opt) +{ + if (opt->new_value == NULL) { + return; + } + + if (opt->value != &opt->default_value) { + TALLOC_FREE(opt->value); + } + + opt->value = opt->new_value; + opt->new_value = NULL; + + conf_option_set_ptr_value(opt); +} + +static void conf_option_reset_temporary(struct conf_option *opt) +{ + opt->temporary_modified = false; +} + +static bool conf_option_is_default(struct conf_option *opt) +{ + return (opt->value == &opt->default_value); +} + +static void conf_option_dump(struct conf_option *opt, FILE *fp) +{ + bool is_default; + + is_default = conf_option_is_default(opt); + + conf_value_dump(opt->name, + opt->value, + is_default, + opt->temporary_modified, + fp); +} + +/* + * Functions related to conf_section + */ + +static struct conf_section *conf_section_find(struct conf_context *conf, + const char *section) +{ + struct conf_section *s; + + for (s = conf->section; s != NULL; s = s->next) { + if (strcasecmp(s->name, section) == 0) { + return s; + } + } + + return NULL; +} + +static int conf_section_add(struct conf_context *conf, + const char *section, + conf_validate_section_fn validate) +{ + struct conf_section *s; + + s = conf_section_find(conf, section); + if (s != NULL) { + return EEXIST; + } + + s = talloc_zero(conf, struct conf_section); + if (s == NULL) { + return ENOMEM; + } + + s->name = talloc_strdup(s, section); + if (s->name == NULL) { + talloc_free(s); + return ENOMEM; + } + + s->validate = validate; + + DLIST_ADD_END(conf->section, s); + return 0; +} + +static bool conf_section_validate(struct conf_context *conf, + struct conf_section *s, + enum conf_update_mode mode) +{ + bool ok; + + if (s->validate == NULL) { + return true; + } + + ok = s->validate(conf, s->name, mode); + if (!ok) { + D_ERR("conf: validation for section [%s] failed\n", s->name); + } + + return ok; +} + +static void conf_section_dump(struct conf_section *s, FILE *fp) +{ + fprintf(fp, "[%s]\n", s->name); +} + +/* + * Functions related to conf_context + */ + +static void conf_all_default(struct conf_context *conf) +{ + struct conf_section *s; + struct conf_option *opt; + + for (s = conf->section; s != NULL; s = s->next) { + for (opt = s->option; opt != NULL; opt = opt->next) { + conf_option_default(opt); + } + } +} + +static int conf_all_temporary_default(struct conf_context *conf, + enum conf_update_mode mode) +{ + struct conf_section *s; + struct conf_option *opt; + int ret; + + for (s = conf->section; s != NULL; s = s->next) { + for (opt = s->option; opt != NULL; opt = opt->next) { + ret = conf_option_new_default_value(opt, mode); + if (ret != 0) { + return ret; + } + } + } + + return 0; +} + +static void conf_all_reset(struct conf_context *conf) +{ + struct conf_section *s; + struct conf_option *opt; + + for (s = conf->section; s != NULL; s = s->next) { + for (opt = s->option; opt != NULL; opt = opt->next) { + conf_option_reset(opt); + } + } +} + +static void conf_all_update(struct conf_context *conf) +{ + struct conf_section *s; + struct conf_option *opt; + + for (s = conf->section; s != NULL; s = s->next) { + for (opt = s->option; opt != NULL; opt = opt->next) { + conf_option_update(opt); + conf_option_reset_temporary(opt); + } + } +} + +/* + * API functions + */ + +int conf_init(TALLOC_CTX *mem_ctx, struct conf_context **result) +{ + struct conf_context *conf; + + conf = talloc_zero(mem_ctx, struct conf_context); + if (conf == NULL) { + return ENOMEM; + } + + conf->define_failed = false; + + *result = conf; + return 0; +} + +void conf_define_section(struct conf_context *conf, + const char *section, + conf_validate_section_fn validate) +{ + int ret; + + if (conf->define_failed) { + return; + } + + if (section == NULL) { + conf->define_failed = true; + return; + } + + ret = conf_section_add(conf, section, validate); + if (ret != 0) { + conf->define_failed = true; + return; + } +} + +static struct conf_option *conf_define(struct conf_context *conf, + const char *section, + const char *key, + enum conf_type type, + conf_validate_string_option_fn validate) +{ + struct conf_section *s; + struct conf_option *opt; + int ret; + + s = conf_section_find(conf, section); + if (s == NULL) { + D_ERR("conf: unknown section [%s]\n", section); + return NULL; + } + + if (key == NULL) { + D_ERR("conf: option name null in section [%s]\n", section); + return NULL; + } + + ret = conf_option_add(s, key, type, validate, &opt); + if (ret != 0) { + return NULL; + } + + return opt; +} + +static void conf_define_post(struct conf_context *conf, + struct conf_option *opt, + struct conf_value *default_value) +{ + int ret; + + ret = conf_option_set_default(opt, default_value); + if (ret != 0) { + conf->define_failed = true; + return; + } + + conf_option_default(opt); +} + +void conf_define_string(struct conf_context *conf, + const char *section, + const char *key, + const char *default_str_val, + conf_validate_string_option_fn validate) +{ + struct conf_option *opt; + struct conf_value default_value; + + if (! conf_valid(conf)) { + return; + } + + opt = conf_define(conf, section, key, CONF_STRING, validate); + if (opt == NULL) { + conf->define_failed = true; + return; + } + + default_value.type = CONF_STRING; + default_value.data.string = default_str_val; + + conf_define_post(conf, opt, &default_value); +} + +void conf_define_integer(struct conf_context *conf, + const char *section, + const char *key, + const int default_int_val, + conf_validate_integer_option_fn validate) +{ + struct conf_option *opt; + struct conf_value default_value; + + if (! conf_valid(conf)) { + return; + } + + opt = conf_define(conf, section, key, CONF_INTEGER, (void *)validate); + if (opt == NULL) { + conf->define_failed = true; + return; + } + + default_value.type = CONF_INTEGER; + default_value.data.integer = default_int_val; + + conf_define_post(conf, opt, &default_value); +} + + +void conf_define_boolean(struct conf_context *conf, + const char *section, + const char *key, + const bool default_bool_val, + conf_validate_boolean_option_fn validate) +{ + struct conf_option *opt; + struct conf_value default_value; + + if (! conf_valid(conf)) { + return; + } + + opt = conf_define(conf, section, key, CONF_BOOLEAN, (void *)validate); + if (opt == NULL) { + conf->define_failed = true; + return; + } + + default_value.type = CONF_BOOLEAN; + default_value.data.boolean = default_bool_val; + + conf_define_post(conf, opt, &default_value); +} + +static struct conf_option *_conf_option(struct conf_context *conf, + const char *section, + const char *key) +{ + struct conf_section *s; + struct conf_option *opt; + + s = conf_section_find(conf, section); + if (s == NULL) { + return NULL; + } + + opt = conf_option_find(s, key); + return opt; +} + +void conf_assign_string_pointer(struct conf_context *conf, + const char *section, + const char *key, + const char **str_ptr) +{ + struct conf_option *opt; + union conf_pointer ptr; + + opt = _conf_option(conf, section, key); + if (opt == NULL) { + D_ERR("conf: unknown option [%s] -> \"%s\"\n", section, key); + conf->define_failed = true; + return; + } + + if (opt->type != CONF_STRING) { + conf->define_failed = true; + return; + } + + ptr.string = str_ptr; + conf_option_set_ptr(opt, &ptr); + conf_option_set_ptr_value(opt); +} + +void conf_assign_integer_pointer(struct conf_context *conf, + const char *section, + const char *key, + int *int_ptr) +{ + struct conf_option *opt; + union conf_pointer ptr; + + opt = _conf_option(conf, section, key); + if (opt == NULL) { + D_ERR("conf: unknown option [%s] -> \"%s\"\n", section, key); + conf->define_failed = true; + return; + } + + if (opt->type != CONF_INTEGER) { + conf->define_failed = true; + return; + } + + ptr.integer = int_ptr; + conf_option_set_ptr(opt, &ptr); + conf_option_set_ptr_value(opt); +} + +void conf_assign_boolean_pointer(struct conf_context *conf, + const char *section, + const char *key, + bool *bool_ptr) +{ + struct conf_option *opt; + union conf_pointer ptr; + + opt = _conf_option(conf, section, key); + if (opt == NULL) { + D_ERR("conf: unknown option [%s] -> \"%s\"\n", section, key); + conf->define_failed = true; + return; + } + + if (opt->type != CONF_BOOLEAN) { + conf->define_failed = true; + return; + } + + ptr.boolean = bool_ptr; + conf_option_set_ptr(opt, &ptr); + conf_option_set_ptr_value(opt); +} + +bool conf_query(struct conf_context *conf, + const char *section, + const char *key, + enum conf_type *type) +{ + struct conf_section *s; + struct conf_option *opt; + + if (! conf_valid(conf)) { + return false; + } + + s = conf_section_find(conf, section); + if (s == NULL) { + return false; + } + + opt = conf_option_find(s, key); + if (opt == NULL) { + return false; + } + + if (type != NULL) { + *type = opt->type; + } + return true; +} + +bool conf_valid(struct conf_context *conf) +{ + if (conf->define_failed) { + return false; + } + + return true; +} + +void conf_set_defaults(struct conf_context *conf) +{ + conf_all_default(conf); +} + +struct conf_load_state { + struct conf_context *conf; + struct conf_section *s; + enum conf_update_mode mode; + int err; +}; + +static bool conf_load_section(const char *section, void *private_data); +static bool conf_load_option(const char *name, + const char *value_str, + void *private_data); + +static int conf_load_internal(struct conf_context *conf) +{ + struct conf_load_state state; + FILE *fp; + int ret; + bool ok; + + state = (struct conf_load_state) { + .conf = conf, + .mode = (conf->reload ? CONF_MODE_RELOAD : CONF_MODE_LOAD), + }; + + ret = conf_all_temporary_default(conf, state.mode); + if (ret != 0) { + return ret; + } + + fp = fopen(conf->filename, "r"); + if (fp == NULL) { + return errno; + } + + ok = tini_parse(fp, + false, + conf_load_section, + conf_load_option, + &state); + fclose(fp); + if (!ok) { + goto fail; + } + + /* Process the last section */ + if (state.s != NULL) { + ok = conf_section_validate(conf, state.s, state.mode); + if (!ok) { + state.err = EINVAL; + goto fail; + } + } + + if (state.err != 0) { + goto fail; + } + + conf_all_update(conf); + return 0; + +fail: + conf_all_reset(conf); + return state.err; +} + +static bool conf_load_section(const char *section, void *private_data) +{ + struct conf_load_state *state = + (struct conf_load_state *)private_data; + bool ok; + + if (state->s != NULL) { + ok = conf_section_validate(state->conf, state->s, state->mode); + if (!ok) { + state->err = EINVAL; + return true; + } + } + + state->s = conf_section_find(state->conf, section); + if (state->s == NULL) { + if (state->conf->ignore_unknown) { + D_DEBUG("conf: ignoring unknown section [%s]\n", + section); + } else { + D_ERR("conf: unknown section [%s]\n", section); + state->err = EINVAL; + return true; + } + } + + return true; +} + +static bool conf_load_option(const char *name, + const char *value_str, + void *private_data) +{ + struct conf_load_state *state = + (struct conf_load_state *)private_data; + struct conf_option *opt; + TALLOC_CTX *tmp_ctx; + struct conf_value value; + int ret; + bool ok; + + if (state->s == NULL) { + if (state->conf->ignore_unknown) { + D_DEBUG("conf: unknown section for option \"%s\"\n", + name); + return true; + } else { + D_ERR("conf: unknown section for option \"%s\"\n", + name); + state->err = EINVAL; + return true; + } + } + + opt = conf_option_find(state->s, name); + if (opt == NULL) { + if (state->conf->ignore_unknown) { + D_DEBUG("conf: unknown option [%s] -> \"%s\"\n", + state->s->name, + name); + return true; + } else { + D_ERR("conf: unknown option [%s] -> \"%s\"\n", + state->s->name, + name); + state->err = EINVAL; + return true; + } + } + + if (strlen(value_str) == 0) { + D_ERR("conf: empty value [%s] -> \"%s\"\n", + state->s->name, + name); + state->err = EINVAL; + return true; + } + + tmp_ctx = talloc_new(state->conf); + if (tmp_ctx == NULL) { + state->err = ENOMEM; + return false; + } + + value.type = opt->type; + ret = conf_value_from_string(tmp_ctx, value_str, &value); + if (ret != 0) { + D_ERR("conf: invalid value [%s] -> \"%s\" = \"%s\"\n", + state->s->name, + name, + value_str); + talloc_free(tmp_ctx); + state->err = ret; + return true; + } + + ok = conf_option_same_value(opt, &value); + if (ok) { + goto done; + } + + ret = conf_option_new_value(opt, &value, state->mode); + if (ret != 0) { + talloc_free(tmp_ctx); + state->err = ret; + return true; + } + +done: + talloc_free(tmp_ctx); + return true; + +} + +int conf_load(struct conf_context *conf, + const char *filename, + bool ignore_unknown) +{ + conf->filename = talloc_strdup(conf, filename); + if (conf->filename == NULL) { + return ENOMEM; + } + + conf->ignore_unknown = ignore_unknown; + + D_NOTICE("Reading config file %s\n", filename); + + return conf_load_internal(conf); +} + +int conf_reload(struct conf_context *conf) +{ + int ret; + + if (conf->filename == NULL) { + return EPERM; + } + + D_NOTICE("Re-reading config file %s\n", conf->filename); + + conf->reload = true; + ret = conf_load_internal(conf); + conf->reload = false; + + return ret; +} + +static int conf_set(struct conf_context *conf, + const char *section, + const char *key, + struct conf_value *value) +{ + struct conf_section *s; + struct conf_option *opt; + int ret; + bool ok; + + s = conf_section_find(conf, section); + if (s == NULL) { + return EINVAL; + } + + opt = conf_option_find(s, key); + if (opt == NULL) { + return EINVAL; + } + + if (opt->type != value->type) { + return EINVAL; + } + + ok = conf_option_same_value(opt, value); + if (ok) { + return 0; + } + + ret = conf_option_new_value(opt, value, CONF_MODE_API); + if (ret != 0) { + conf_option_reset(opt); + return ret; + } + + ok = conf_section_validate(conf, s, CONF_MODE_API); + if (!ok) { + conf_option_reset(opt); + return EINVAL; + } + + conf_option_update(opt); + return 0; +} + +int conf_set_string(struct conf_context *conf, + const char *section, + const char *key, + const char *str_val) +{ + struct conf_value value; + + value.type = CONF_STRING; + value.data.string = str_val; + + return conf_set(conf, section, key, &value); +} + +int conf_set_integer(struct conf_context *conf, + const char *section, + const char *key, + int int_val) +{ + struct conf_value value; + + value.type = CONF_INTEGER; + value.data.integer = int_val; + + return conf_set(conf, section, key, &value); +} + +int conf_set_boolean(struct conf_context *conf, + const char *section, + const char *key, + bool bool_val) +{ + struct conf_value value; + + value.type = CONF_BOOLEAN; + value.data.boolean = bool_val; + + return conf_set(conf, section, key, &value); +} + +static int conf_get(struct conf_context *conf, + const char *section, + const char *key, + enum conf_type type, + const struct conf_value **value, + bool *is_default) +{ + struct conf_section *s; + struct conf_option *opt; + + s = conf_section_find(conf, section); + if (s == NULL) { + return EINVAL; + } + + opt = conf_option_find(s, key); + if (opt == NULL) { + return EINVAL; + } + + if (opt->type != type) { + return EINVAL; + } + + *value = opt->value; + if (is_default != NULL) { + *is_default = conf_option_is_default(opt); + } + + return 0; +} + +int conf_get_string(struct conf_context *conf, + const char *section, + const char *key, + const char **str_val, + bool *is_default) +{ + const struct conf_value *value; + int ret; + + ret = conf_get(conf, section, key, CONF_STRING, &value, is_default); + if (ret != 0) { + return ret; + } + + *str_val = value->data.string; + return 0; +} + +int conf_get_integer(struct conf_context *conf, + const char *section, + const char *key, + int *int_val, + bool *is_default) +{ + const struct conf_value *value; + int ret; + + ret = conf_get(conf, section, key, CONF_INTEGER, &value, is_default); + if (ret != 0) { + return ret; + } + + *int_val = value->data.integer; + return 0; +} + +int conf_get_boolean(struct conf_context *conf, + const char *section, + const char *key, + bool *bool_val, + bool *is_default) +{ + const struct conf_value *value; + int ret; + + ret = conf_get(conf, section, key, CONF_BOOLEAN, &value, is_default); + if (ret != 0) { + return ret; + } + + *bool_val = value->data.boolean; + return 0; +} + +void conf_dump(struct conf_context *conf, FILE *fp) +{ + struct conf_section *s; + struct conf_option *opt; + + for (s = conf->section; s != NULL; s = s->next) { + conf_section_dump(s, fp); + for (opt = s->option; opt != NULL; opt = opt->next) { + conf_option_dump(opt, fp); + } + } +} |