1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/ctdb/common/conf.c
Amitay Isaacs efd0c45a8b ctdb-common: Fix empty value handling in conf
In tini, allow_empty_value=false causes the parser to ignore the lines
without '=' sign, but lines with nothing after '=' sign are allowed and
cause empty string ("") to be passed as a value.

This is counter-intuitive, so conf requires special handling for empty
values (which are treated as invalid).

Signed-off-by: Amitay Isaacs <amitay@gmail.com>
Reviewed-by: Martin Schwenke <martin@meltin.net>
2019-07-26 03:34:16 +00:00

1392 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 "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);
}
}
}