1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/ctdb/conf/conf.c
John Mulligan ac926a506d ctdb-conf: add boolean arg for verbosity when loading config
In a future commit we will add support for loading the config file from
the `ctdb` command line tool. Prior to this change the config file load
func always called D_NOTICE that causes the command to emit new text and
thus break all the tests that rely on the specific test output (not to
mention something users could notice). This change plumbs a new
`verbose` argument into some of the config file loading functions.
Generally, all existing functions will have verbose set to true to match
the existing behavior. Future callers of this function can set it to
false in order to avoid emitting the extra text.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
Reviewed-by: Martin Schwenke <martin@meltin.net>
2024-08-06 00:43:36 +00:00

1395 lines
26 KiB
C

/*
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 "conf/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 gets 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,
bool verbose)
{
conf->filename = talloc_strdup(conf, filename);
if (conf->filename == NULL) {
return ENOMEM;
}
conf->ignore_unknown = ignore_unknown;
if (verbose) {
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);
}
}
}