blob: 9d93028591165ebc67f454b5eaf4de55e8ef5a5a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include "os/mynewt.h"
#include "base64/base64.h"
#include "config/config.h"
#include "config_priv.h"
struct conf_handler_head conf_handlers;
static struct os_mutex conf_mtx;
#if MYNEWT_VAL(OS_SCHEDULING)
static os_event_fn conf_ev_fn_load;
/* OS event - causes persisted config values to be loaded at startup. */
static struct os_event conf_ev_load = {
.ev_cb = conf_ev_fn_load,
};
#endif
void
conf_init(void)
{
int rc;
os_mutex_init(&conf_mtx);
SLIST_INIT(&conf_handlers);
conf_store_init();
(void)rc;
#if MYNEWT_VAL(CONFIG_CLI)
rc = conf_cli_register();
SYSINIT_PANIC_ASSERT(rc == 0);
#endif
#if MYNEWT_VAL(CONFIG_MGMT)
rc = conf_mgmt_register();
SYSINIT_PANIC_ASSERT(rc == 0);
#endif
/* Delay loading the configuration until the default event queue is
* processed. This gives main() a chance to configure the underlying
* storage first.
*/
#if MYNEWT_VAL(OS_SCHEDULING)
os_eventq_put(os_eventq_dflt_get(), &conf_ev_load);
#endif
}
void
conf_lock(void)
{
os_mutex_pend(&conf_mtx, 0xFFFFFFFF);
}
void
conf_unlock(void)
{
os_mutex_release(&conf_mtx);
}
int
conf_register(struct conf_handler *handler)
{
conf_lock();
SLIST_INSERT_HEAD(&conf_handlers, handler, ch_list);
conf_unlock();
return 0;
}
#if MYNEWT_VAL(OS_SCHEDULING)
static void
conf_ev_fn_load(struct os_event *ev)
{
conf_ensure_loaded();
}
#endif
/*
* Find conf_handler based on name.
*/
struct conf_handler *
conf_handler_lookup(char *name)
{
struct conf_handler *ch;
SLIST_FOREACH(ch, &conf_handlers, ch_list) {
if (!strcmp(name, ch->ch_name)) {
return ch;
}
}
return NULL;
}
/*
* Separate string into argv array.
*/
int
conf_parse_name(char *name, int *name_argc, char *name_argv[])
{
char *tok;
char *tok_ptr;
char *sep = CONF_NAME_SEPARATOR;
int i;
tok = strtok_r(name, sep, &tok_ptr);
i = 0;
while (tok) {
name_argv[i++] = tok;
tok = strtok_r(NULL, sep, &tok_ptr);
}
*name_argc = i;
return 0;
}
struct conf_handler *
conf_parse_and_lookup(char *name, int *name_argc, char *name_argv[])
{
int rc;
rc = conf_parse_name(name, name_argc, name_argv);
if (rc) {
return NULL;
}
return conf_handler_lookup(name_argv[0]);
}
int
conf_value_from_str(char *val_str, enum conf_type type, void *vp, int maxlen)
{
int64_t val;
uint64_t uval;
#if MYNEWT_VAL(CONFIG_FLOAT_SUPPORT)
float fval;
#endif
char *eptr;
if (!val_str) {
goto err;
}
switch (type) {
case CONF_BOOL:
case CONF_INT8:
case CONF_INT16:
case CONF_INT32:
case CONF_INT64:
val = strtoll(val_str, &eptr, 0);
if (*eptr != '\0') {
goto err;
}
if (type == CONF_BOOL) {
if (val < 0 || val > 1) {
goto err;
}
*(bool *)vp = val;
} else if (type == CONF_INT8) {
if (val < INT8_MIN || val > INT8_MAX) {
goto err;
}
*(int8_t *)vp = val;
} else if (type == CONF_INT16) {
if (val < INT16_MIN || val > INT16_MAX) {
goto err;
}
*(int16_t *)vp = val;
} else if (type == CONF_INT32) {
if (val < INT32_MIN || val > INT32_MAX) {
goto err;
}
*(int32_t *)vp = val;
} else {
*(int64_t *)vp = val;
}
break;
case CONF_UINT8:
case CONF_UINT16:
case CONF_UINT32:
case CONF_UINT64:
uval = strtoull(val_str, &eptr, 0);
if (*eptr != '\0') {
goto err;
}
if (type == CONF_UINT8) {
if (uval > UINT8_MAX) {
goto err;
}
*(uint8_t *)vp = uval;
} else if (type == CONF_UINT16) {
if (uval > UINT16_MAX) {
goto err;
}
*(uint16_t *)vp = uval;
} else if (type == CONF_UINT32) {
if (uval > UINT32_MAX) {
goto err;
}
*(uint32_t *)vp = uval;
} else {
*(uint64_t *)vp = uval;
}
break;
#if MYNEWT_VAL(CONFIG_FLOAT_SUPPORT)
case CONF_FLOAT:
fval = strtof(val_str, &eptr);
if (*eptr != '\0') {
goto err;
}
*(float *)vp = fval;
break;
#endif
case CONF_STRING:
val = strlen(val_str);
if (val + 1 > maxlen) {
goto err;
}
strcpy(vp, val_str);
break;
default:
goto err;
}
return 0;
err:
return OS_INVALID_PARM;
}
int
conf_bytes_from_str(char *val_str, void *vp, int *len)
{
int tmp;
if (base64_decode_len(val_str) > *len) {
return OS_INVALID_PARM;
}
tmp = base64_decode(val_str, vp);
if (tmp < 0) {
return OS_INVALID_PARM;
}
*len = tmp;
return 0;
}
char *
conf_str_from_value(enum conf_type type, void *vp, char *buf, int buf_len)
{
int64_t val;
uint64_t uval;
#if MYNEWT_VAL(CONFIG_FLOAT_SUPPORT)
float fval;
#endif
if (type == CONF_STRING) {
return vp;
}
switch (type) {
case CONF_BOOL:
case CONF_INT8:
case CONF_INT16:
case CONF_INT32:
case CONF_INT64:
if (type == CONF_BOOL) {
val = *(bool *)vp;
} else if (type == CONF_INT8) {
val = *(int8_t *)vp;
} else if (type == CONF_INT16) {
val = *(int16_t *)vp;
} else if (type == CONF_INT32) {
val = *(int32_t *)vp;
} else {
val = *(int64_t *)vp;
}
snprintf(buf, buf_len, "%" PRId64, val);
return buf;
case CONF_UINT8:
case CONF_UINT16:
case CONF_UINT32:
case CONF_UINT64:
if (type == CONF_UINT8) {
uval = *(uint8_t *)vp;
} else if (type == CONF_UINT16) {
uval = *(uint16_t *)vp;
} else if (type == CONF_UINT32) {
uval = *(uint32_t *)vp;
} else {
uval = *(uint64_t *)vp;
}
snprintf(buf, buf_len, "%" PRIu64, uval);
return buf;
#if MYNEWT_VAL(CONFIG_FLOAT_SUPPORT)
case CONF_FLOAT:
fval = *(float *)vp;
snprintf(buf, buf_len, "%f", fval);
return buf;
#endif
default:
return NULL;
}
}
char *
conf_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len)
{
if (BASE64_ENCODE_SIZE(vp_len) > buf_len) {
return NULL;
}
base64_encode(vp, vp_len, buf, 1);
return buf;
}
/**
* Executes a conf_handler's "get" callback and returns the result.
*/
static char *
conf_get_cb(struct conf_handler *ch, int argc, char **argv, char *val,
int val_len_max)
{
if (ch->ch_ext) {
if (ch->ch_get_ext != NULL) {
return ch->ch_get_ext(argc, argv, val, val_len_max, ch->ch_arg);
}
} else {
if (ch->ch_get != NULL) {
return ch->ch_get(argc, argv, val, val_len_max);
}
}
return NULL;
}
/**
* Executes a conf_handler's "set" callback and returns the result.
*/
static int
conf_set_cb(struct conf_handler *ch, int argc, char **argv, char *val)
{
if (ch->ch_ext) {
if (ch->ch_set_ext != NULL) {
return ch->ch_set_ext(argc, argv, val, ch->ch_arg);
}
} else {
if (ch->ch_set != NULL) {
return ch->ch_set(argc, argv, val);
}
}
return OS_ERROR;
}
/**
* Executes a conf_handler's "commit" callback and returns the result.
*/
static int
conf_commit_cb(struct conf_handler *ch)
{
if (ch->ch_ext) {
if (ch->ch_commit_ext != NULL) {
return ch->ch_commit_ext(ch->ch_arg);
}
} else {
if (ch->ch_commit != NULL) {
return ch->ch_commit();
}
}
return 0;
}
/**
* Executes a conf_handler's "export" callback and returns the result.
*/
int
conf_export_cb(struct conf_handler *ch, conf_export_func_t export_func,
conf_export_tgt_t tgt)
{
if (ch->ch_ext) {
if (ch->ch_export_ext != NULL) {
return ch->ch_export_ext(export_func, tgt, ch->ch_arg);
}
} else {
if (ch->ch_export != NULL) {
return ch->ch_export(export_func, tgt);
}
}
return 0;
}
void
conf_export(conf_export_func_t export_func, enum conf_export_tgt tgt)
{
struct conf_handler *ch;
conf_lock();
SLIST_FOREACH(ch, &conf_handlers, ch_list) {
conf_export_cb(ch, export_func, tgt);
}
conf_unlock();
}
int
conf_set_value(char *name, char *val_str)
{
int name_argc;
char *name_argv[CONF_MAX_DIR_DEPTH];
struct conf_handler *ch;
int rc;
conf_lock();
ch = conf_parse_and_lookup(name, &name_argc, name_argv);
if (!ch) {
rc = OS_INVALID_PARM;
goto out;
}
rc = conf_set_cb(ch, name_argc - 1, &name_argv[1], val_str);
out:
conf_unlock();
return rc;
}
/*
* Get value in printable string form. If value is not string, the value
* will be filled in *buf.
* Return value will be pointer to beginning of that buffer,
* except for string it will pointer to beginning of string.
*/
char *
conf_get_value(char *name, char *buf, int buf_len)
{
int name_argc;
char *name_argv[CONF_MAX_DIR_DEPTH];
struct conf_handler *ch;
char *rval = NULL;
conf_lock();
ch = conf_parse_and_lookup(name, &name_argc, name_argv);
if (!ch) {
goto out;
}
rval = conf_get_cb(ch, name_argc - 1, &name_argv[1], buf, buf_len);
out:
conf_unlock();
return rval;
}
int
conf_commit(char *name)
{
int name_argc;
char *name_argv[CONF_MAX_DIR_DEPTH];
struct conf_handler *ch;
int rc;
int rc2;
conf_lock();
if (name) {
ch = conf_parse_and_lookup(name, &name_argc, name_argv);
if (!ch) {
rc = OS_INVALID_PARM;
goto out;
}
rc = conf_commit_cb(ch);
} else {
rc = 0;
SLIST_FOREACH(ch, &conf_handlers, ch_list) {
if (ch->ch_commit) {
rc2 = conf_commit_cb(ch);
if (!rc) {
rc = rc2;
}
}
}
}
out:
conf_unlock();
return rc;
}