--- /dev/null
+/*
+ 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) {
+ *bool_val = true;
+ return 0;
+ }
+
+ if (strcasecmp(str, "false") == 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 ENOENT;
+ }
+
+ return ret;
+}
+
+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 ENOENT;
+ }
+
+ 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 int conf_option_new_value(struct conf_option *opt,
+ struct conf_value *new_value,
+ enum conf_update_mode mode)
+{
+ int ret;
+ bool ok;
+
+ ok = conf_option_validate(opt, new_value, mode);
+ if (!ok) {
+ D_ERR("conf: validation for option \"%s\" failed\n",
+ opt->name);
+ return EINVAL;
+ }
+
+ TALLOC_FREE(opt->new_value);
+ 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 (mode == CONF_MODE_API) {
+ opt->temporary_modified = true;
+ } else {
+ opt->temporary_modified = false;
+ }
+
+ return 0;
+}
+
+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)
+{
+ 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 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 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);
+ }
+ }
+}
+
+/*
+ * 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;
+ bool ok;
+
+ fp = fopen(conf->filename, "r");
+ if (fp == NULL) {
+ return errno;
+ }
+
+ state = (struct conf_load_state) {
+ .conf = conf,
+ .mode = (conf->reload ? CONF_MODE_RELOAD : CONF_MODE_LOAD),
+ };
+
+ 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;
+ }
+ }
+
+ 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 false;
+ }
+ }
+
+ 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 false;
+ }
+ }
+
+ 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;
+
+ if (state->s == NULL) {
+ if (state->conf->ignore_unknown) {
+ D_DEBUG("conf: ignoring unknown option \"%s\"\n",
+ name);
+ return true;
+ } else {
+ D_ERR("conf: unknown option \"%s\"\n", name);
+ state->err = EINVAL;
+ return false;
+ }
+ }
+
+ opt = conf_option_find(state->s, name);
+ if (opt == NULL) {
+ if (state->conf->ignore_unknown) {
+ return true;
+ } else {
+ state->err = ENOENT;
+ return false;
+ }
+ }
+
+ 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) {
+ talloc_free(tmp_ctx);
+ state->err = ret;
+ return false;
+ }
+
+ ret = conf_option_new_value(opt, &value, state->mode);
+ if (ret != 0) {
+ talloc_free(tmp_ctx);
+ state->err = ret;
+ return false;
+ }
+
+ 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 ENOENT;
+ }
+
+ opt = conf_option_find(s, key);
+ if (opt == NULL) {
+ return ENOENT;
+ }
+
+ if (opt->type != value->type) {
+ return ENOENT;
+ }
+
+ 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 ENOENT;
+ }
+
+ opt = conf_option_find(s, key);
+ if (opt == NULL) {
+ return ENOENT;
+ }
+
+ 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);
+ }
+ }
+}
--- /dev/null
+/*
+ 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/>.
+*/
+
+#ifndef __CTDB_CONF_H__
+#define __CTDB_CONF_H__
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <talloc.h>
+
+/**
+ * @file conf.h
+ *
+ * @brief Configuration file handling with sections and key-value pairs
+ *
+ * CTDB settings can be written in a configuration file ctdb.conf (similar to
+ * samba's smb.conf). Various daemons and tools will consult the configuration
+ * file for runtime settings.
+ *
+ * The configuration will be organized in sections depending on various
+ * components. Each section will have various configuration options in the form
+ * of key-value pairs.
+ *
+ * [section1]
+ * key1 = value1
+ * ...
+ *
+ * [section2]
+ * key2 = value2
+ * ...
+ *
+ * ...
+ *
+ */
+
+/**
+ * @brief Abstract data structure holding the configuration options
+ */
+struct conf_context;
+
+/**
+ * @brief configuration option update mode
+ *
+ * When a value of configuration option is changed, update mode is set
+ * appropriately.
+ *
+ * CONF_MODE_API - value modified using set functions
+ * CONF_MODE_LOAD - value modified via conf_load
+ * CONF_MODE_RELOAD - value modified via conf_reload
+ */
+enum conf_update_mode {
+ CONF_MODE_API,
+ CONF_MODE_LOAD,
+ CONF_MODE_RELOAD,
+};
+
+/**
+ * @brief configuration option type
+ */
+enum conf_type {
+ CONF_STRING,
+ CONF_INTEGER,
+ CONF_BOOLEAN,
+};
+
+/**
+ * @brief Configuration section validation function
+ *
+ * Check if all the configuration options are consistent with each-other
+ */
+typedef bool (*conf_validate_section_fn)(struct conf_context *conf,
+ const char *section,
+ enum conf_update_mode mode);
+
+/**
+ * @brief Configuration option validation function for string
+ *
+ * Check if a configuration option value is valid
+ */
+typedef bool (*conf_validate_string_option_fn)(const char *key,
+ const char *old_value,
+ const char *new_value,
+ enum conf_update_mode mode);
+
+/**
+ * @brief Configuration option validation function for integer
+ *
+ * Check if a configuration option value is valid
+ */
+typedef bool (*conf_validate_integer_option_fn)(const char *key,
+ int old_value,
+ int new_value,
+ enum conf_update_mode mode);
+
+/**
+ * @brief Configuration option validation function for boolean
+ *
+ * Check if a configuration option value is valid
+ */
+typedef bool (*conf_validate_boolean_option_fn)(const char *key,
+ bool old_value,
+ bool new_value,
+ enum conf_update_mode mode);
+
+/**
+ * @brief Initialize configuration option database
+ *
+ * This return a new configuration options context. Freeing this context will
+ * free up all the memory associated with the configuration options.
+ *
+ * @param[in] mem_ctx Talloc memory context
+ * @param[in] result The new configuration options context
+ * @return 0 on success, errno on failure
+ */
+int conf_init(TALLOC_CTX *mem_ctx, struct conf_context **result);
+
+/**
+ * @brief Define a section for organizing configuration options
+ *
+ * This functions creates a section to organize configuration option. The
+ * section names are case-insensitive and are always stored in lower case.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of the section
+ * @param[in] validate The validation function for configuration options
+ */
+void conf_define_section(struct conf_context *conf,
+ const char *section,
+ conf_validate_section_fn validate);
+
+/**
+ * @brief Define a configuration option which has a string value
+ *
+ * This functions adds a new configuration option organized under a given
+ * section. Configuration options are case-insensitive and are always stored
+ * in lower case.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of the section
+ * @param[in] key The name of the configuration option
+ * @param[in] default_value The default value for the configuration option
+ * @param[in] validate The validation function for the configuration option
+ */
+void conf_define_string(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ const char *default_value,
+ conf_validate_string_option_fn validate);
+
+/**
+ * @brief Define a configuration option which has an integer value
+ *
+ * This functions adds a new configuration option organized under a given
+ * section. Configuration options are case-insensitive and are always stored
+ * in lower case.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of the section
+ * @param[in] key The name of the configuration option
+ * @param[in] default_value The default value for the configuration option
+ * @param[in] validate The validation function for the configuration option
+ */
+void conf_define_integer(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ const int default_value,
+ conf_validate_integer_option_fn validate);
+
+/**
+ * @brief Define a configuration option which has an boolean value
+ *
+ * This functions adds a new configuration option organized under a given
+ * section. Configuration options are case-insensitive and are always stored
+ * in lower case.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of the section
+ * @param[in] key The name of the configuration option
+ * @param[in] default_value The default value for the configuration option
+ * @param[in] validate The validation function for the configuration option
+ */
+void conf_define_boolean(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ const bool default_value,
+ conf_validate_boolean_option_fn validate);
+
+/**
+ * @brief Assign user-accessible pointer for string option
+ *
+ * This pointer can be used for accessing the value of configuration option
+ * directly without requiring a function call.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of the section
+ * @param[in] key The name of the configuration option
+ * @param[in] ptr User-accessible pointer to the value
+ */
+void conf_assign_string_pointer(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ const char **ptr);
+
+/**
+ * @brief Assign user-accessible pointer for integer option
+ *
+ * This pointer can be used for accessing the value of configuration option
+ * directly without requiring a function call.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of the section
+ * @param[in] key The name of the configuration option
+ * @param[in] ptr User-accessible pointer to the value
+ */
+void conf_assign_integer_pointer(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ int *ptr);
+
+/**
+ * @brief Assign user-accessible pointer for boolean option
+ *
+ * This pointer can be used for accessing the value of configuration option
+ * directly without requiring a function call.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of the section
+ * @param[in] key The name of the configuration option
+ * @param[in] ptr User-accessible pointer to the value
+ * @return true on success, false on failure
+ */
+void conf_assign_boolean_pointer(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ bool *ptr);
+
+/**
+ * @brief Query a configuration option
+ *
+ * This function checks if a configuration option is defined or not.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of the section
+ * @param[in] key The name of the configuration option
+ * @param[out] type The type of the configuration option
+ * @return true on success, false if section/option is not defined
+ */
+bool conf_query(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ enum conf_type *type);
+
+/**
+ * @brief Check if the defined configuration options are valid
+ *
+ * This function must be called after creating configuration options
+ * to confirm that all the option definitions are valid.
+ *
+ * @param[in] conf The configuration options context
+ * @return true on success, false on failure
+ */
+bool conf_valid(struct conf_context *conf);
+
+/**
+ * @brief Set the default values for all configuration options
+ *
+ * This function resets all the configuration options to their default values.
+ *
+ * @param[in] conf The connfiguration options context
+ */
+void conf_set_defaults(struct conf_context *conf);
+
+/**
+ * @brief Load the values for configuration option values from a file
+ *
+ * This function will update the values of the configuration options from those
+ * specified in a file. This function will fail in case it encounters an
+ * undefined option. Any sections which are not defined, will be ignored.
+ *
+ * This function will call validation function (if specified) before updating
+ * the value of a configuration option. After updating all the values for a
+ * section, the validation for section (if specified) will be called. If any
+ * of the validation functions return error, then all the configuration
+ * options will be reset to their previous values.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] filename The configuration file
+ * @param[in] skip_unknown Whether unknown config options should be ignored
+ * @return 0 on success, errno on failure
+ */
+int conf_load(struct conf_context *conf,
+ const char *filename,
+ bool ignore_unknown);
+
+/**
+ * @brief Reload the values for configuration options
+ *
+ * This function will re-load the values of the configuration options. This
+ * function can be called only after succesful call to conf_load().
+ *
+ * @see conf_load
+ *
+ * @param[in] conf The configuration options context
+ * @return 0 on success, errno on failure.
+ */
+int conf_reload(struct conf_context *conf);
+
+/**
+ * @brief Set the string value of a configuration option
+ *
+ * This function can be used to update the value of a configuration option.
+ * This will call the validation function for that option (if defined) and
+ * the section validation function (if defined).
+ *
+ * If a user-defined storage pointer is provided, then the value of a
+ * configuration option should not be changed via that pointer.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of a section
+ * @param[in] key The name of a configuration option
+ * @param[in] str_val The string value
+ * @return 0 on success, errno in case of failure
+ */
+int conf_set_string(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ const char *str_val);
+
+/**
+ * @brief Set the integer value of a configuration option
+ *
+ * This function can be used to update the value of a configuration option.
+ * This will call the validation function for that option (if defined) and
+ * the section validation function (if defined).
+ *
+ * If a user-defined storage pointer is provided, then the value of a
+ * configuration option should not be changed via that pointer.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of a section
+ * @param[in] key The name of a configuration option
+ * @param[in] int_val The integer value
+ * @return 0 on success, errno in case of failure
+ */
+int conf_set_integer(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ int int_val);
+
+/**
+ * @brief Set the boolean value of a configuration option
+ *
+ * This function can be used to update the value of a configuration option.
+ * This will call the validation function for that option (if defined) and
+ * the section validation function (if defined).
+ *
+ * If a user-defined storage pointer is provided, then the value of a
+ * configuration option should not be changed via that pointer.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of a section
+ * @param[in] key The name of a configuration option
+ * @param[in] bool_val The boolean value
+ * @return 0 on success, errno in case of failure
+ */
+int conf_set_boolean(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ bool bool_val);
+
+/**
+ * @brief Get the string value of a configuration option
+ *
+ * This function can be used to fetch the current value of a configuration
+ * option.
+ *
+ * If a user-defined storage pointer is provided, then the value of a
+ * configuration option can be accessed directly via that pointer.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of a section
+ * @param[in] key The name of a configuration option
+ * @param[out] str_val The string value of the configuration option
+ * @param[out] is_default True if the value is default value
+ * @return 0 on success, errno in case of failure
+ */
+int conf_get_string(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ const char **str_val,
+ bool *is_default);
+
+/**
+ * @brief Get the integer value of a configuration option
+ *
+ * This function can be used to fetch the current value of a configuration
+ * option.
+ *
+ * If a user-defined storage pointer is provided, then the value of a
+ * configuration option can be accessed directly via that pointer.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of a section
+ * @param[in] key The name of a configuration option
+ * @param[out] int_val The integer value of the configuration option
+ * @param[out] is_default True if the value is default value
+ * @return 0 on success, errno in case of failure
+ */
+int conf_get_integer(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ int *int_val,
+ bool *is_default);
+
+/**
+ * @brief Get the boolean value of a configuration option
+ *
+ * This function can be used to fetch the current value of a configuration
+ * option.
+ *
+ * If a user-defined storage pointer is provided, then the value of a
+ * configuration option can be accessed directly via that pointer.
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] section The name of a section
+ * @param[in] key The name of a configuration option
+ * @param[out] bool_val The boolean value of the configuration option
+ * @param[out] is_default True if the value is default value
+ * @return 0 on success, errno in case of failure
+ */
+int conf_get_boolean(struct conf_context *conf,
+ const char *section,
+ const char *key,
+ bool *bool_val,
+ bool *is_default);
+
+/**
+ * @brief Dump the configuration in a file
+ *
+ * All the configuration options are dumped with their current values.
+ * If an option has a default value, then it is commented.
+ *
+ * Here is a sample output:
+ *
+ * [section1]
+ * key1 = value1
+ * key2 = value2
+ * # key3 = default_value3
+ * [section2]
+ * key4 = value4
+ *
+ * @param[in] conf The configuration options context
+ * @param[in] fp File pointer
+ */
+void conf_dump(struct conf_context *conf, FILE *fp);
+
+#endif /* __CTDB_CONF_H__ */
--- /dev/null
+#!/bin/sh
+
+. "${TEST_SCRIPTS_DIR}/unit.sh"
+
+conffile="${TEST_VAR_DIR}/config.$$"
+
+remove_files ()
+{
+ rm -f "$conffile"
+}
+
+test_cleanup remove_files
+
+ok_null
+unit_test conf_test 1
+
+ok <<EOF
+conf: unknown section [section1]
+EOF
+unit_test conf_test 2
+
+ok <<EOF
+conf: option "key1" already exists
+EOF
+unit_test conf_test 3
+
+ok <<EOF
+conf: option "key1" already exists
+EOF
+unit_test conf_test 4
+
+ok_null
+unit_test conf_test 5
+
+ok_null
+unit_test conf_test 6
+
+ok <<EOF
+conf: validation for option "key1" failed
+conf: validation for option "key2" failed
+conf: validation for option "key3" failed
+EOF
+unit_test conf_test 7
+
+cat > "$conffile" <<EOF
+[section1]
+EOF
+
+required_result 22 <<EOF
+conf: validation for section [section1] failed
+[section1]
+ # key1 = default
+EOF
+unit_test conf_test 8 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+ key1 = unknown
+EOF
+
+required_result 22 <<EOF
+conf: validation for section [section1] failed
+[section1]
+ # key1 = default
+EOF
+unit_test conf_test 8 "$conffile"
+
+cat > "$conffile" <<EOF
+
+[section1]
+ key1 = value2
+ key2 = 20 # comment
+key3 = false
+EOF
+
+ok <<EOF
+[section1]
+ key1 = value2
+ key2 = 20
+ key3 = false
+EOF
+unit_test conf_test 9 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+key1 = value2
+EOF
+
+ok <<EOF
+[section1]
+ key1 = value2
+ # key2 = 10
+ key3 = false # temporary
+EOF
+unit_test conf_test 9 "$conffile"
+
+cat > "$conffile" <<EOF
+[section2]
+ foo = bar
+EOF
+
+required_result 22 <<EOF
+conf: unknown section [section2]
+[section1]
+ # key1 = value1
+ # key2 = 10
+ key3 = false # temporary
+EOF
+unit_test conf_test 10 "$conffile"
+
+cat > "$conffile" <<EOF
+[section1]
+ key1 = value2
+ foo = bar
+ key2 = 20
+EOF
+
+required_result 2 <<EOF
+[section1]
+ # key1 = value1
+ # key2 = 10
+ key3 = false # temporary
+EOF
+unit_test conf_test 10 "$conffile"
--- /dev/null
+/*
+ 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 <assert.h>
+
+#include "common/conf.c"
+
+static void test1(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ int ret;
+ bool status;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_section(conf, "section1", NULL);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_section(conf, NULL, NULL);
+ status = conf_valid(conf);
+ assert(status == false);
+
+ talloc_free(mem_ctx);
+}
+
+static void test2(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ int ret;
+ bool status;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_string(conf, "section1", "key1", "default", NULL);
+ status = conf_valid(conf);
+ assert(status == false);
+
+ talloc_free(mem_ctx);
+}
+
+static void test3(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ int ret;
+ bool status;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_section(conf, "section1", NULL);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_string(conf, "section1", "key1", NULL, NULL);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_string(conf, "section1", "key1", "value1", NULL);
+ status = conf_valid(conf);
+ assert(status == false);
+
+ talloc_free(mem_ctx);
+}
+
+static void test4(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ int ret;
+ bool status;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_section(conf, "section1", NULL);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_string(conf, "section1", "key1", NULL, NULL);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_integer(conf, "section1", "key1", 10, NULL);
+ status = conf_valid(conf);
+ assert(status == false);
+
+ talloc_free(mem_ctx);
+}
+
+static void test5(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ enum conf_type type;
+ int ret;
+ bool status;
+ const char *s_val;
+ int i_val;
+ bool b_val;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_section(conf, "section1", NULL);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_string(conf, "section1", "key1", "value1", NULL);
+ conf_define_integer(conf, "section1", "key2", 10, NULL);
+ conf_define_boolean(conf, "section1", "key3", true, NULL);
+
+ conf_assign_string_pointer(conf, "section1", "key1", &s_val);
+ conf_assign_integer_pointer(conf, "section1", "key2", &i_val);
+ conf_assign_boolean_pointer(conf, "section1", "key3", &b_val);
+
+ status = conf_valid(conf);
+ assert(status == true);
+
+ status = conf_query(conf, "section1", "key1", &type);
+ assert(status == true);
+ assert(type == CONF_STRING);
+
+ status = conf_query(conf, "section1", "key2", &type);
+ assert(status == true);
+ assert(type == CONF_INTEGER);
+
+ status = conf_query(conf, "section1", "key3", &type);
+ assert(status == true);
+ assert(type == CONF_BOOLEAN);
+
+ assert(strcmp(s_val, "value1") == 0);
+ assert(i_val == 10);
+ assert(b_val == true);
+
+ conf_set_defaults(conf);
+
+ assert(strcmp(s_val, "value1") == 0);
+ assert(i_val == 10);
+ assert(b_val == true);
+
+ talloc_free(mem_ctx);
+}
+
+static void test6(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ int ret;
+ bool status;
+ const char *s_val, *s2_val;
+ int i_val, i2_val;
+ bool b_val, b2_val, is_default;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_section(conf, "section1", NULL);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_string(conf, "section1", "key1", "default", NULL);
+ conf_define_integer(conf, "section1", "key2", 10, NULL);
+ conf_define_boolean(conf, "section1", "key3", true, NULL);
+
+ conf_assign_string_pointer(conf, "section1", "key1", &s_val);
+ conf_assign_integer_pointer(conf, "section1", "key2", &i_val);
+ conf_assign_boolean_pointer(conf, "section1", "key3", &b_val);
+
+ status = conf_valid(conf);
+ assert(status == true);
+
+ is_default = false;
+ ret = conf_get_string(conf, "section1", "key1", &s2_val, &is_default);
+ assert(ret == 0);
+ assert(strcmp(s2_val, "default") == 0);
+ assert(is_default == true);
+
+ is_default = false;
+ ret = conf_get_integer(conf, "section1", "key2", &i2_val, &is_default);
+ assert(ret == 0);
+ assert(i2_val == 10);
+ assert(is_default == true);
+
+ is_default = false;
+ ret = conf_get_boolean(conf, "section1", "key3", &b2_val, &is_default);
+ assert(ret == 0);
+ assert(b2_val == true);
+ assert(is_default == true);
+
+ ret = conf_set_string(conf, "section1", "key1", "foobar");
+ assert(ret == 0);
+
+ ret = conf_set_integer(conf, "section1", "key2", 20);
+ assert(ret == 0);
+
+ ret = conf_set_boolean(conf, "section1", "key3", false);
+ assert(ret == 0);
+
+ assert(strcmp(s_val, "foobar") == 0);
+ assert(i_val == 20);
+ assert(b_val == false);
+
+ is_default = true;
+ ret = conf_get_string(conf, "section1", "key1", &s2_val, &is_default);
+ assert(ret == 0);
+ assert(strcmp(s2_val, "foobar") == 0);
+ assert(is_default == false);
+
+ is_default = true;
+ ret = conf_get_integer(conf, "section1", "key2", &i2_val, &is_default);
+ assert(ret == 0);
+ assert(i2_val == 20);
+ assert(is_default == false);
+
+ is_default = true;
+ ret = conf_get_boolean(conf, "section1", "key3", &b2_val, &is_default);
+ assert(ret == 0);
+ assert(b2_val == false);
+ assert(is_default == false);
+
+ conf_set_defaults(conf);
+
+ assert(strcmp(s_val, "default") == 0);
+ assert(i_val == 10);
+ assert(b_val == true);
+
+ talloc_free(mem_ctx);
+}
+
+static bool test7_validate_string(const char *key,
+ const char *old_value, const char *new_value,
+ enum conf_update_mode mode)
+{
+ return false;
+}
+
+static bool test7_validate_integer(const char *key,
+ int old_value, int new_value,
+ enum conf_update_mode mode)
+{
+ return false;
+}
+
+static bool test7_validate_boolean(const char *key,
+ bool old_value, bool new_value,
+ enum conf_update_mode mode)
+{
+ return false;
+}
+
+static void test7(void)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ int ret;
+ bool status;
+ const char *s_val, *s2_val;
+ int i_val, i2_val;
+ bool b_val, b2_val;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_section(conf, "section1", NULL);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_string(conf, "section1", "key1", "default",
+ test7_validate_string);
+ conf_define_integer(conf, "section1", "key2", 10,
+ test7_validate_integer);
+ conf_define_boolean(conf, "section1", "key3", true,
+ test7_validate_boolean);
+
+ conf_assign_string_pointer(conf, "section1", "key1", &s_val);
+ conf_assign_integer_pointer(conf, "section1", "key2", &i_val);
+ conf_assign_boolean_pointer(conf, "section1", "key3", &b_val);
+
+ status = conf_valid(conf);
+ assert(status == true);
+
+ ret = conf_set_string(conf, "section1", "key1", "foobar");
+ assert(ret == EINVAL);
+
+ ret = conf_set_integer(conf, "section1", "key2", 20);
+ assert(ret == EINVAL);
+
+ ret = conf_set_boolean(conf, "section1", "key3", false);
+ assert(ret == EINVAL);
+
+ assert(strcmp(s_val, "default") == 0);
+ assert(i_val == 10);
+ assert(b_val == true);
+
+ ret = conf_get_string(conf, "section1", "key2", &s2_val, NULL);
+ assert(ret == EINVAL);
+
+ ret = conf_get_integer(conf, "section1", "key3", &i2_val, NULL);
+ assert(ret == EINVAL);
+
+ ret = conf_get_boolean(conf, "section1", "key1", &b2_val, NULL);
+ assert(ret == EINVAL);
+
+ talloc_free(mem_ctx);
+}
+
+static bool test8_validate(struct conf_context *conf,
+ const char *section,
+ enum conf_update_mode mode)
+{
+ return false;
+}
+
+static void test8(const char *filename)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ int ret;
+ bool status;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_section(conf, "section1", test8_validate);
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_define_string(conf, "section1", "key1", "default", NULL);
+
+ status = conf_valid(conf);
+ assert(status == true);
+
+ ret = conf_load(conf, filename, true);
+ conf_dump(conf, stdout);
+
+ talloc_free(mem_ctx);
+ exit(ret);
+}
+
+static void test9(const char *filename, bool ignore_unknown)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct conf_context *conf;
+ int ret;
+ bool status;
+
+ ret = conf_init(mem_ctx, &conf);
+ assert(ret == 0);
+ assert(conf != NULL);
+
+ conf_define_section(conf, "section1", NULL);
+
+ conf_define_string(conf, "section1", "key1", "value1", NULL);
+ conf_define_integer(conf, "section1", "key2", 10, NULL);
+ conf_define_boolean(conf, "section1", "key3", true, NULL);
+
+ status = conf_valid(conf);
+ assert(status == true);
+
+ conf_set_boolean(conf, "section1", "key3", false);
+
+ ret = conf_load(conf, filename, ignore_unknown);
+ conf_dump(conf, stdout);
+
+ talloc_free(mem_ctx);
+ exit(ret);
+}
+
+int main(int argc, const char **argv)
+{
+ int num;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <testnum> [<config>]\n", argv[0]);
+ exit(1);
+ }
+
+ num = atoi(argv[1]);
+ if (num > 7 && argc != 3) {
+ fprintf(stderr, "Usage: %s <testnum> [<config>]\n", argv[0]);
+ exit(1);
+ }
+
+ switch (num) {
+ case 1:
+ test1();
+ break;
+
+ case 2:
+ test2();
+ break;
+
+ case 3:
+ test3();
+ break;
+
+ case 4:
+ test4();
+ break;
+
+ case 5:
+ test5();
+ break;
+
+ case 6:
+ test6();
+ break;
+
+ case 7:
+ test7();
+ break;
+
+ case 8:
+ test8(argv[2]);
+ break;
+
+ case 9:
+ test9(argv[2], true);
+ break;
+
+ case 10:
+ test9(argv[2], false);
+ break;
+
+ }
+
+ return 0;
+}
pidfile.c run_proc.c
hash_count.c run_event.c
sock_client.c version.c
- cmdline.c path.c
+ cmdline.c path.c conf.c
'''),
deps='''samba-util sys_rw tevent-util
replace talloc tevent tdb popt''')
'sock_io_test',
'hash_count_test',
'run_event_test',
- 'cmdline_test'
+ 'cmdline_test',
+ 'conf_test',
]
for target in ctdb_unit_tests: