subsys/settings: stream-style encoding and decoding to/from storage
This patch reworks routines used to store and read the settings data. Provide stream-style encoding and decoding to/from flash, so the the API only requires a pointer to binary data, and the settings implementation takes care of encoding/decoding to/from base64 and writing/reading to/from flash on the fly. This would eliminate the need of a separate base64 value buffer on the application-side, thereby further contributing to the stack footprint reduction. Above changes allows to remove: 256-byte value length limitation. removing enum settings_type usage so all settings data are treated now as a byte array (i.e. what's previously SETTINGS_BYTES) Introduced routine settings_val_read_cb for read and decode the settings data from storage inside h_set handler implementations. h_set settings handler now provide persistent value's context used along with read routine instead of immediately value. Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
This commit is contained in:
parent
c012a288c3
commit
76f8b97871
11 changed files with 929 additions and 541 deletions
|
@ -8,6 +8,7 @@
|
|||
#ifndef ZEPHYR_INCLUDE_SETTINGS_SETTINGS_H_
|
||||
#define ZEPHYR_INCLUDE_SETTINGS_SETTINGS_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <misc/util.h>
|
||||
#include <misc/slist.h>
|
||||
#include <stdint.h>
|
||||
|
@ -34,76 +35,58 @@ extern "C" {
|
|||
|
||||
#define SETTINGS_NMGR_OP 0
|
||||
|
||||
/**
|
||||
* Type of settings value.
|
||||
*/
|
||||
enum settings_type {
|
||||
SETTINGS_NONE = 0,
|
||||
SETTINGS_INT8,
|
||||
SETTINGS_INT16,
|
||||
SETTINGS_INT32,
|
||||
SETTINGS_INT64,
|
||||
SETTINGS_STRING,
|
||||
SETTINGS_BYTES,
|
||||
SETTINGS_FLOAT,
|
||||
SETTINGS_DOUBLE,
|
||||
SETTINGS_BOOL,
|
||||
} __attribute__((__packed__));
|
||||
|
||||
/**
|
||||
* Parameter to commit handler describing where data is going to.
|
||||
*/
|
||||
enum settings_export_tgt {
|
||||
SETTINGS_EXPORT_PERSIST, /* Value is to be persisted. */
|
||||
SETTINGS_EXPORT_SHOW /* Value is to be displayed. */
|
||||
};
|
||||
|
||||
/**
|
||||
* @struct settings_handler
|
||||
* Config handlers for subtree implement a set of handler functions.
|
||||
* These are registered using a call to settings_register().
|
||||
* These are registered using a call to @ref settings_register.
|
||||
*/
|
||||
struct settings_handler {
|
||||
sys_snode_t node;
|
||||
/**< Linked list node info for module internal usage. */
|
||||
|
||||
char *name;
|
||||
/**< Name of subtree. */
|
||||
|
||||
int (*h_get)(int argc, char **argv, char *val, int val_len_max);
|
||||
/**< Get values handler of settings items identified by keyword names.
|
||||
*
|
||||
* @param settings_handler::node Linked list node info for module internal usage.
|
||||
*
|
||||
* @param settings_handler::name Name of subtree.
|
||||
*
|
||||
* @param settings_handler::h_get Get values handler of settings items
|
||||
* identified by keyword names.Parameters:
|
||||
* Parameters:
|
||||
* - argc - count of item in argv.
|
||||
* - argv - array of pointers to keyword names.
|
||||
* - val - buffer for a value.
|
||||
* - val_len_max - size of that buffer.
|
||||
*
|
||||
* @param settings_handler::h_set Set value handler of settings items
|
||||
* identified by keyword names. Parameters:
|
||||
* - argc - count of item in argv, argv - array of pointers to keyword names.
|
||||
* - val- pointer to value to be set.
|
||||
*
|
||||
* @param settings_handler::h_commit This handler gets called after settings
|
||||
* has been loaded in full. User might use it to apply setting to
|
||||
* the application.
|
||||
*
|
||||
* @param settings_handler::h_export This gets called to dump all current
|
||||
* settings items.
|
||||
* This happens when settings_save() tries to save the settings. Parameters:
|
||||
* - tgt: indicates where data is going to.
|
||||
* - Export_function: the pointer to the internal function which appends
|
||||
* a single key-value pair to persisted settings. Don't store duplicated
|
||||
* value. The name is subtree/key string, val is the string with
|
||||
* value.
|
||||
*
|
||||
* @remarks The User might limit a implementations of handler to serving only
|
||||
* one keyword at one call - what will impose limit to get/set values using full
|
||||
* subtree/key name.
|
||||
*/
|
||||
struct settings_handler {
|
||||
sys_snode_t node;
|
||||
char *name;
|
||||
char *(*h_get)(int argc, char **argv, char *val, int val_len_max);
|
||||
int (*h_set)(int argc, char **argv, char *val);
|
||||
|
||||
int (*h_set)(int argc, char **argv, void *value_ctx);
|
||||
/**< Set value handler of settings items identified by keyword names.
|
||||
*
|
||||
* Parameters:
|
||||
* - argc - count of item in argv, argv - array of pointers to keyword
|
||||
* names.
|
||||
* - value_ctx - pointer to the value contex which is used paramiter
|
||||
* for data extracting routine (@ref settings_val_read_cb).
|
||||
*/
|
||||
|
||||
int (*h_commit)(void);
|
||||
int (*h_export)(int (*export_func)(const char *name, char *val),
|
||||
enum settings_export_tgt tgt);
|
||||
/**< This handler gets called after settings has been loaded in full.
|
||||
* User might use it to apply setting to the application.
|
||||
*/
|
||||
|
||||
int (*h_export)(int (*export_func)(const char *name, void *val,
|
||||
size_t val_len));
|
||||
/**< This gets called to dump all current settings items.
|
||||
*
|
||||
* This happens when @ref settings_save tries to save the settings.
|
||||
* Parameters:
|
||||
* - export_func: the pointer to the internal function which appends
|
||||
* a single key-value pair to persisted settings. Don't store
|
||||
* duplicated value. The name is subtree/key string, val is the string
|
||||
* with value.
|
||||
*
|
||||
* @remarks The User might limit a implementations of handler to serving
|
||||
* only one keyword at one call - what will impose limit to get/set
|
||||
* values using full subtree/key name.
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -148,29 +131,33 @@ int settings_save(void);
|
|||
* changed value).
|
||||
*
|
||||
* @param name Name/key of the settings item.
|
||||
* @param var Value of the settings item.
|
||||
* @param value Pointer to the value of the settings item. This value will
|
||||
* be transferred to the @ref settings_handler::h_export handler implementation.
|
||||
* @param val_len Length of the value.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int settings_save_one(const char *name, char *var);
|
||||
int settings_save_one(const char *name, void *value, size_t val_len);
|
||||
|
||||
/**
|
||||
* Set settings item identified by @p name to be value @p val_str.
|
||||
* Set settings item identified by @p name to be value @p value.
|
||||
* This finds the settings handler for this subtree and calls it's
|
||||
* set handler.
|
||||
*
|
||||
* @param name Name/key of the settings item.
|
||||
* @param val_str Value of the settings item.
|
||||
* @param value Pointer to the value of the settings item. This value will
|
||||
* be transferred to the @ref settings_handler::h_set handler implementation.
|
||||
* @param len Length of value string.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int settings_set_value(char *name, char *val_str);
|
||||
int settings_set_value(char *name, void *value, size_t len);
|
||||
|
||||
/**
|
||||
* Get value of settings item identified by @p name.
|
||||
* This calls the settings handler h_get for the subtree.
|
||||
*
|
||||
* Configuration handler can copy the string to @p buf, the maximum
|
||||
* Configuration handler should copy the string to @p buf, the maximum
|
||||
* number of bytes it will copy is limited by @p buf_len.
|
||||
*
|
||||
* @param name Name/key of the settings item.
|
||||
|
@ -180,10 +167,9 @@ int settings_set_value(char *name, char *val_str);
|
|||
*
|
||||
* @param buf_len size of buf.
|
||||
*
|
||||
* @return value will be pointer to beginning of the buf,
|
||||
* except for string it will pointer to beginning of string source.
|
||||
* @return Positive: Length of copied dat. Negative: -ERCODE
|
||||
*/
|
||||
char *settings_get_value(char *name, char *buf, int buf_len);
|
||||
int settings_get_value(char *name, char *buf, int buf_len);
|
||||
|
||||
/**
|
||||
* Call commit for all settings handler. This should apply all
|
||||
|
@ -196,60 +182,20 @@ char *settings_get_value(char *name, char *buf, int buf_len);
|
|||
int settings_commit(char *name);
|
||||
|
||||
/**
|
||||
* Convenience routine for converting value passed as a string to native
|
||||
* data type.
|
||||
* Persistent data extracting routine.
|
||||
*
|
||||
* @param val_str Value of the settings item as string.
|
||||
* @param type Type of the value to convert to.
|
||||
* @param vp Pointer to variable to fill with the decoded value.
|
||||
* @param maxlen the vp buffer size.
|
||||
* This function read and decode data from non volatile storage to user buffer
|
||||
* This function should be used inside set handler in order to read the settings
|
||||
* data from backend storage.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
* @param[in] value_ctx Data contex provided by the <p>h_set</p> handler.
|
||||
* @param[out] buf Buffer for data read.
|
||||
* @param[in] len Length of <p>buf</p>.
|
||||
*
|
||||
* @retval Negative value on failure. 0 and positive: Length of data loaded to
|
||||
* the <p>buf</p>.
|
||||
*/
|
||||
int settings_value_from_str(char *val_str, enum settings_type type, void *vp,
|
||||
int maxlen);
|
||||
|
||||
/**
|
||||
* Convenience routine for converting byte array passed as a base64
|
||||
* encoded string.
|
||||
*
|
||||
* @param val_str Value of the settings item as string.
|
||||
* @param vp Pointer to variable to fill with the decoded value.
|
||||
* @param len Size of that variable. On return the number of bytes in the array.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int settings_bytes_from_str(char *val_str, void *vp, int *len);
|
||||
|
||||
/**
|
||||
* Convenience routine for converting native data type to a string.
|
||||
*
|
||||
* @param type Type of the value to convert from.
|
||||
* @param vp Pointer to variable to convert.
|
||||
* @param buf Buffer where string value will be stored.
|
||||
* @param buf_len Size of the buffer.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
char *settings_str_from_value(enum settings_type type, void *vp, char *buf,
|
||||
int buf_len);
|
||||
#define SETTINGS_STR_FROM_BYTES_LEN(len) (((len) * 4 / 3) + 4)
|
||||
|
||||
/**
|
||||
* Convenience routine for converting byte array into a base64
|
||||
* encoded string.
|
||||
*
|
||||
* @param vp Pointer to variable to convert.
|
||||
* @param vp_len Number of bytes to convert.
|
||||
* @param buf Buffer where string value will be stored.
|
||||
* @param buf_len Size of the buffer.
|
||||
*
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len);
|
||||
|
||||
#define SETTINGS_VALUE_SET(str, type, val) \
|
||||
settings_value_from_str((str), (type), &(val), sizeof(val))
|
||||
int settings_val_read_cb(void *value_ctx, void *buf, size_t len);
|
||||
|
||||
/**
|
||||
* @} settings
|
||||
|
|
|
@ -16,6 +16,11 @@ menuconfig SETTINGS
|
|||
It supports several back-ends to store and load serialized data from
|
||||
and it can do so atomically for all involved modules.
|
||||
|
||||
# Hidden option to enable encoding length into settings entry
|
||||
config SETTINGS_ENCODE_LEN
|
||||
depends on SETTINGS
|
||||
bool
|
||||
|
||||
choice
|
||||
prompt "Storage back-end"
|
||||
default SETTINGS_FCB if FCB
|
||||
|
@ -32,6 +37,7 @@ config SETTINGS_FCB
|
|||
config SETTINGS_FS
|
||||
bool "File System"
|
||||
depends on FILE_SYSTEM
|
||||
select SETTINGS_ENCODE_LEN
|
||||
help
|
||||
Use a file system as a settings storage back-end.
|
||||
endchoice
|
||||
|
|
|
@ -22,6 +22,7 @@ struct settings_fcb {
|
|||
|
||||
extern int settings_fcb_src(struct settings_fcb *cf);
|
||||
extern int settings_fcb_dst(struct settings_fcb *cf);
|
||||
void settings_mount_fcb_backend(struct settings_fcb *cf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ int settings_file_src(struct settings_file *cf);
|
|||
/* settings saves go to a file */
|
||||
int settings_file_dst(struct settings_file *cf);
|
||||
|
||||
void settings_mount_fs_backend(struct settings_file *cf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -8,25 +8,17 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "base64.h"
|
||||
|
||||
#include "settings/settings.h"
|
||||
#include "settings_priv.h"
|
||||
#include <zephyr/types.h>
|
||||
|
||||
/* mbedtls-base64 lib encodes data to null-terminated string */
|
||||
#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
|
||||
|
||||
sys_slist_t settings_handlers;
|
||||
|
||||
static u8_t settings_cmd_inited;
|
||||
|
||||
void settings_store_init(void);
|
||||
static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base);
|
||||
static s64_t dec_to_s64(char *p_str, char **e_ptr);
|
||||
|
||||
void settings_init(void)
|
||||
{
|
||||
|
@ -102,234 +94,61 @@ static struct settings_handler *settings_parse_and_lookup(char *name,
|
|||
return settings_handler_lookup(name_argv[0]);
|
||||
}
|
||||
|
||||
int settings_value_from_str(char *val_str, enum settings_type type, void *vp,
|
||||
int maxlen)
|
||||
{
|
||||
s32_t val;
|
||||
s64_t val64;
|
||||
char *eptr;
|
||||
|
||||
if (!val_str) {
|
||||
goto err;
|
||||
}
|
||||
switch (type) {
|
||||
case SETTINGS_INT8:
|
||||
case SETTINGS_INT16:
|
||||
case SETTINGS_INT32:
|
||||
case SETTINGS_BOOL:
|
||||
val = strtol(val_str, &eptr, 0);
|
||||
if (*eptr != '\0') {
|
||||
goto err;
|
||||
}
|
||||
if (type == SETTINGS_BOOL) {
|
||||
if (val < 0 || val > 1) {
|
||||
goto err;
|
||||
}
|
||||
*(bool *)vp = val;
|
||||
} else if (type == SETTINGS_INT8) {
|
||||
if (val < INT8_MIN || val > UINT8_MAX) {
|
||||
goto err;
|
||||
}
|
||||
*(int8_t *)vp = val;
|
||||
} else if (type == SETTINGS_INT16) {
|
||||
if (val < INT16_MIN || val > UINT16_MAX) {
|
||||
goto err;
|
||||
}
|
||||
*(int16_t *)vp = val;
|
||||
} else if (type == SETTINGS_INT32) {
|
||||
*(s32_t *)vp = val;
|
||||
}
|
||||
break;
|
||||
case SETTINGS_INT64:
|
||||
val64 = dec_to_s64(val_str, &eptr);
|
||||
if (*eptr != '\0') {
|
||||
goto err;
|
||||
}
|
||||
*(s64_t *)vp = val64;
|
||||
break;
|
||||
case SETTINGS_STRING:
|
||||
val = strlen(val_str);
|
||||
if (val + 1 > maxlen) {
|
||||
goto err;
|
||||
}
|
||||
strcpy(vp, val_str);
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int settings_bytes_from_str(char *val_str, void *vp, int *len)
|
||||
{
|
||||
int err;
|
||||
int rc;
|
||||
|
||||
err = base64_decode(vp, *len, &rc, val_str, strlen(val_str));
|
||||
|
||||
if (err) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*len = rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *settings_str_from_value(enum settings_type type, void *vp, char *buf,
|
||||
int buf_len)
|
||||
{
|
||||
s32_t val;
|
||||
|
||||
if (type == SETTINGS_STRING) {
|
||||
return vp;
|
||||
}
|
||||
switch (type) {
|
||||
case SETTINGS_INT8:
|
||||
case SETTINGS_INT16:
|
||||
case SETTINGS_INT32:
|
||||
case SETTINGS_BOOL:
|
||||
if (type == SETTINGS_BOOL) {
|
||||
val = *(bool *)vp;
|
||||
} else if (type == SETTINGS_INT8) {
|
||||
val = *(int8_t *)vp;
|
||||
} else if (type == SETTINGS_INT16) {
|
||||
val = *(int16_t *)vp;
|
||||
} else {
|
||||
val = *(s32_t *)vp;
|
||||
}
|
||||
snprintf(buf, buf_len, "%ld", (long)val);
|
||||
return buf;
|
||||
case SETTINGS_INT64:
|
||||
s64_to_dec(buf, buf_len, *(s64_t *)vp, 10);
|
||||
return buf;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void u64_to_dec(char *ptr, int buf_len, u64_t value, int base)
|
||||
{
|
||||
u64_t t = 0U, res = 0U;
|
||||
u64_t tmp = value;
|
||||
int count = 0;
|
||||
|
||||
if (ptr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tmp == 0) {
|
||||
count++;
|
||||
}
|
||||
|
||||
while (tmp > 0) {
|
||||
tmp = tmp/base;
|
||||
count++;
|
||||
}
|
||||
|
||||
ptr += count;
|
||||
|
||||
*ptr = '\0';
|
||||
|
||||
do {
|
||||
res = value - base * (t = value / base);
|
||||
if (res < 10) {
|
||||
*--ptr = '0' + res;
|
||||
} else if ((res >= 10) && (res < 16)) {
|
||||
*--ptr = 'A' - 10 + res;
|
||||
}
|
||||
value = t;
|
||||
} while (value != 0);
|
||||
}
|
||||
|
||||
static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base)
|
||||
{
|
||||
u64_t val64;
|
||||
|
||||
if (ptr == NULL || buf_len < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
*ptr = '-';
|
||||
ptr++;
|
||||
buf_len--;
|
||||
val64 = value * (-1);
|
||||
} else {
|
||||
val64 = value;
|
||||
}
|
||||
|
||||
u64_to_dec(ptr, buf_len, val64, base);
|
||||
}
|
||||
|
||||
static s64_t dec_to_s64(char *p_str, char **e_ptr)
|
||||
{
|
||||
u64_t val = 0U, prev_val = 0U;
|
||||
bool neg = false;
|
||||
int digit;
|
||||
|
||||
if (*p_str == '-') {
|
||||
neg = true;
|
||||
p_str++;
|
||||
} else if (*p_str == '+') {
|
||||
p_str++;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (*p_str >= '0' && *p_str <= '9') {
|
||||
digit = *p_str - '0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
val *= 10;
|
||||
val += digit;
|
||||
|
||||
/* this is only a fuse */
|
||||
if (val < prev_val) {
|
||||
break;
|
||||
}
|
||||
|
||||
prev_val = val;
|
||||
p_str++;
|
||||
}
|
||||
|
||||
if (e_ptr != 0)
|
||||
*e_ptr = p_str;
|
||||
|
||||
if (neg) {
|
||||
return -val;
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len)
|
||||
{
|
||||
if (BASE64_ENCODE_SIZE(vp_len) > buf_len) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t enc_len;
|
||||
|
||||
base64_encode(buf, buf_len, &enc_len, vp, vp_len);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
int settings_set_value(char *name, char *val_str)
|
||||
int settings_set_value_priv(char *name, void *val_read_cb_ctx, off_t off,
|
||||
u8_t is_runtime)
|
||||
{
|
||||
int name_argc;
|
||||
char *name_argv[SETTINGS_MAX_DIR_DEPTH];
|
||||
struct settings_handler *ch;
|
||||
struct read_value_cb_ctx value_ctx;
|
||||
|
||||
ch = settings_parse_and_lookup(name, &name_argc, name_argv);
|
||||
if (!ch) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ch->h_set(name_argc - 1, &name_argv[1], val_str);
|
||||
value_ctx.read_cb_ctx = val_read_cb_ctx;
|
||||
value_ctx.off = off;
|
||||
value_ctx.runtime = is_runtime;
|
||||
|
||||
return ch->h_set(name_argc - 1, &name_argv[1], (void *)&value_ctx);
|
||||
}
|
||||
|
||||
/* API */
|
||||
int settings_set_value(char *name, void *val_str, size_t len)
|
||||
{
|
||||
struct runtime_value_ctx rt_ctx;
|
||||
|
||||
rt_ctx.p_value = val_str;
|
||||
rt_ctx.size = len;
|
||||
|
||||
return settings_set_value_priv(name, &rt_ctx, 0, 1);
|
||||
}
|
||||
|
||||
/* API */
|
||||
int settings_val_read_cb(void *value_ctx, void *buf, size_t len)
|
||||
{
|
||||
struct read_value_cb_ctx *value_context = value_ctx;
|
||||
size_t len_read;
|
||||
int rc;
|
||||
struct runtime_value_ctx *rt_ctx;
|
||||
|
||||
if (value_context->runtime) {
|
||||
rt_ctx = value_context->read_cb_ctx;
|
||||
len_read = min(len, rt_ctx->size);
|
||||
memcpy(buf, rt_ctx->p_value, len_read);
|
||||
return len_read;
|
||||
} else {
|
||||
rc = settings_line_val_read(value_context->off, 0, buf, len,
|
||||
&len_read,
|
||||
value_context->read_cb_ctx);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
return len_read;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -338,7 +157,7 @@ int settings_set_value(char *name, char *val_str)
|
|||
* Return value will be pointer to beginning of that buffer,
|
||||
* except for string it will pointer to beginning of string.
|
||||
*/
|
||||
char *settings_get_value(char *name, char *buf, int buf_len)
|
||||
int settings_get_value(char *name, char *buf, int buf_len)
|
||||
{
|
||||
int name_argc;
|
||||
char *name_argv[SETTINGS_MAX_DIR_DEPTH];
|
||||
|
@ -346,11 +165,11 @@ char *settings_get_value(char *name, char *buf, int buf_len)
|
|||
|
||||
ch = settings_parse_and_lookup(name, &name_argc, name_argv);
|
||||
if (!ch) {
|
||||
return NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ch->h_get) {
|
||||
return NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
return ch->h_get(name_argc - 1, &name_argv[1], buf, buf_len);
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ struct settings_fcb_load_cb_arg {
|
|||
static int settings_fcb_load(struct settings_store *cs, load_cb cb,
|
||||
void *cb_arg);
|
||||
static int settings_fcb_save(struct settings_store *cs, const char *name,
|
||||
const char *value);
|
||||
const char *value, size_t val_len);
|
||||
|
||||
static struct settings_store_itf settings_fcb_itf = {
|
||||
.csi_load = settings_fcb_load,
|
||||
|
@ -79,33 +79,23 @@ int settings_fcb_dst(struct settings_fcb *cf)
|
|||
static int settings_fcb_load_cb(struct fcb_entry_ctx *entry_ctx, void *arg)
|
||||
{
|
||||
struct settings_fcb_load_cb_arg *argp;
|
||||
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
|
||||
SETTINGS_EXTRA_LEN];
|
||||
char *name_str;
|
||||
char *val_str;
|
||||
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
|
||||
int rc;
|
||||
int len;
|
||||
|
||||
argp = (struct settings_fcb_load_cb_arg *)arg;
|
||||
|
||||
len = entry_ctx->loc.fe_data_len;
|
||||
if (len >= sizeof(buf)) {
|
||||
len = sizeof(buf) - 1;
|
||||
}
|
||||
|
||||
rc = flash_area_read(entry_ctx->fap,
|
||||
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf, len);
|
||||
size_t len_read;
|
||||
|
||||
rc = settings_line_name_read(buf, sizeof(buf), &len_read,
|
||||
(void *)&entry_ctx->loc);
|
||||
if (rc) {
|
||||
return 0;
|
||||
}
|
||||
buf[len] = '\0';
|
||||
buf[len_read] = '\0';
|
||||
|
||||
rc = settings_line_parse(buf, &name_str, &val_str);
|
||||
if (rc) {
|
||||
return 0;
|
||||
}
|
||||
argp->cb(name_str, val_str, argp->cb_arg);
|
||||
/*name, val-read_cb-ctx, val-off*/
|
||||
/* take into account '=' separator after the name */
|
||||
argp->cb(buf, (void *)&entry_ctx->loc, len_read + 1, argp->cb_arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -125,64 +115,79 @@ static int settings_fcb_load(struct settings_store *cs, load_cb cb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int settings_fcb_var_read(struct fcb_entry_ctx *entry_ctx, char *buf,
|
||||
char **name, char **val)
|
||||
static int read_handler(void *ctx, off_t off, char *buf, size_t *len)
|
||||
{
|
||||
int rc;
|
||||
struct fcb_entry_ctx *entry_ctx = ctx;
|
||||
|
||||
rc = flash_area_read(entry_ctx->fap,
|
||||
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf,
|
||||
entry_ctx->loc.fe_data_len);
|
||||
if (rc) {
|
||||
return rc;
|
||||
if (off >= entry_ctx->loc.fe_data_len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
buf[entry_ctx->loc.fe_data_len] = '\0';
|
||||
rc = settings_line_parse(buf, name, val);
|
||||
return rc;
|
||||
|
||||
if ((off + *len) > entry_ctx->loc.fe_data_len) {
|
||||
*len = entry_ctx->loc.fe_data_len - off;
|
||||
}
|
||||
|
||||
return flash_area_read(entry_ctx->fap,
|
||||
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc) + off, buf,
|
||||
*len);
|
||||
}
|
||||
|
||||
static void settings_fcb_compress(struct settings_fcb *cf)
|
||||
{
|
||||
int rc;
|
||||
char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
|
||||
SETTINGS_EXTRA_LEN];
|
||||
char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
|
||||
SETTINGS_EXTRA_LEN];
|
||||
struct fcb_entry_ctx loc1;
|
||||
struct fcb_entry_ctx loc2;
|
||||
char *name1, *val1;
|
||||
char *name2, *val2;
|
||||
char name1[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
|
||||
char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
|
||||
int copy;
|
||||
u8_t rbs;
|
||||
|
||||
rc = fcb_append_to_scratch(&cf->cf_fcb);
|
||||
if (rc) {
|
||||
return; /* XXX */
|
||||
}
|
||||
|
||||
rbs = flash_area_align(cf->cf_fcb.fap);
|
||||
|
||||
loc1.fap = cf->cf_fcb.fap;
|
||||
|
||||
loc1.loc.fe_sector = NULL;
|
||||
loc1.loc.fe_elem_off = 0;
|
||||
|
||||
while (fcb_getnext(&cf->cf_fcb, &loc1.loc) == 0) {
|
||||
if (loc1.loc.fe_sector != cf->cf_fcb.f_oldest) {
|
||||
break;
|
||||
}
|
||||
rc = settings_fcb_var_read(&loc1, buf1, &name1, &val1);
|
||||
|
||||
off_t val1_off;
|
||||
|
||||
rc = settings_line_name_read(name1, sizeof(name1), &val1_off,
|
||||
&loc1);
|
||||
if (rc) {
|
||||
continue;
|
||||
}
|
||||
if (!val1) {
|
||||
/* No sense to copy empty entry from the oldest sector*/
|
||||
|
||||
if (val1_off + 1 == loc1.loc.fe_data_len) {
|
||||
/* Lack of a value so the record is a deletion-record */
|
||||
/* No sense to copy empty entry from */
|
||||
/* the oldest sector */
|
||||
continue;
|
||||
}
|
||||
|
||||
loc2 = loc1;
|
||||
copy = 1;
|
||||
|
||||
while (fcb_getnext(&cf->cf_fcb, &loc2.loc) == 0) {
|
||||
rc = settings_fcb_var_read(&loc2, buf2, &name2, &val2);
|
||||
off_t val2_off;
|
||||
|
||||
rc = settings_line_name_read(name2, sizeof(name2),
|
||||
&val2_off, &loc2);
|
||||
if (rc) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name1, name2)) {
|
||||
|
||||
if ((val1_off == val2_off) &&
|
||||
!memcmp(name1, name2, val1_off)) {
|
||||
copy = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -194,17 +199,13 @@ static void settings_fcb_compress(struct settings_fcb *cf)
|
|||
/*
|
||||
* Can't find one. Must copy.
|
||||
*/
|
||||
rc = flash_area_read(loc1.fap, FCB_ENTRY_FA_DATA_OFF(loc1.loc),
|
||||
buf1, loc1.loc.fe_data_len);
|
||||
if (rc) {
|
||||
continue;
|
||||
}
|
||||
rc = fcb_append(&cf->cf_fcb, loc1.loc.fe_data_len, &loc2.loc);
|
||||
if (rc) {
|
||||
continue;
|
||||
}
|
||||
rc = flash_area_write(loc2.fap, FCB_ENTRY_FA_DATA_OFF(loc2.loc),
|
||||
buf1, loc1.loc.fe_data_len);
|
||||
|
||||
rc = settings_entry_copy(&loc2, 0, &loc1, 0,
|
||||
loc1.loc.fe_data_len);
|
||||
if (rc) {
|
||||
continue;
|
||||
}
|
||||
|
@ -216,14 +217,35 @@ static void settings_fcb_compress(struct settings_fcb *cf)
|
|||
__ASSERT(rc == 0, "Failed to fcb rotate.\n");
|
||||
}
|
||||
|
||||
static int settings_fcb_append(struct settings_fcb *cf, char *buf, int len)
|
||||
static int write_handler(void *ctx, off_t off, char const *buf, size_t len)
|
||||
{
|
||||
struct fcb_entry_ctx *entry_ctx = ctx;
|
||||
|
||||
return flash_area_write(entry_ctx->fap,
|
||||
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc) + off,
|
||||
buf, len);
|
||||
}
|
||||
|
||||
/* ::csi_save implementation */
|
||||
static int settings_fcb_save(struct settings_store *cs, const char *name,
|
||||
const char *value, size_t val_len)
|
||||
{
|
||||
struct settings_fcb *cf = (struct settings_fcb *)cs;
|
||||
struct fcb_entry_ctx loc;
|
||||
int len;
|
||||
int rc;
|
||||
int i;
|
||||
struct fcb_entry loc;
|
||||
u8_t wbs;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
rc = fcb_append(&cf->cf_fcb, len, &loc);
|
||||
if (!name) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wbs = flash_area_align(cf->cf_fcb.fap);
|
||||
len = settings_line_len_calc(name, val_len);
|
||||
|
||||
for (i = 0; i < CONFIG_SETTINGS_FCB_NUM_AREAS; i++) {
|
||||
rc = fcb_append(&cf->cf_fcb, len, &loc.loc);
|
||||
if (rc != FCB_ERR_NOSPACE) {
|
||||
break;
|
||||
}
|
||||
|
@ -233,29 +255,24 @@ static int settings_fcb_append(struct settings_fcb *cf, char *buf, int len)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = flash_area_write(cf->cf_fcb.fap, FCB_ENTRY_FA_DATA_OFF(loc),
|
||||
buf, len);
|
||||
if (rc) {
|
||||
return -EINVAL;
|
||||
loc.fap = cf->cf_fcb.fap;
|
||||
|
||||
rc = settings_line_write(name, value, val_len, 0, (void *)&loc);
|
||||
|
||||
if (rc != -EIO) {
|
||||
i = fcb_append_finish(&cf->cf_fcb, &loc.loc);
|
||||
if (!rc) {
|
||||
rc = i;
|
||||
}
|
||||
return fcb_append_finish(&cf->cf_fcb, &loc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int settings_fcb_save(struct settings_store *cs, const char *name,
|
||||
const char *value)
|
||||
void settings_mount_fcb_backend(struct settings_fcb *cf)
|
||||
{
|
||||
struct settings_fcb *cf = (struct settings_fcb *)cs;
|
||||
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
|
||||
SETTINGS_EXTRA_LEN];
|
||||
int len;
|
||||
u8_t rbs;
|
||||
|
||||
if (!name) {
|
||||
return -EINVAL;
|
||||
}
|
||||
rbs = cf->cf_fcb.f_align;
|
||||
|
||||
len = settings_line_make(buf, sizeof(buf), name, value);
|
||||
if (len < 0 || len + 2 > sizeof(buf)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
return settings_fcb_append(cf, buf, len);
|
||||
settings_line_io_init(read_handler, write_handler, rbs);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
static int settings_file_load(struct settings_store *cs, load_cb cb,
|
||||
void *cb_arg);
|
||||
static int settings_file_save(struct settings_store *cs, const char *name,
|
||||
const char *value);
|
||||
const char *value, size_t val_len);
|
||||
|
||||
static struct settings_store_itf settings_file_itf = {
|
||||
.csi_load = settings_file_load,
|
||||
|
@ -50,40 +50,6 @@ int settings_file_dst(struct settings_file *cf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int settings_getnext_line(struct fs_file_t *file, char *buf, int blen,
|
||||
off_t *loc)
|
||||
{
|
||||
int rc;
|
||||
char *end;
|
||||
|
||||
rc = fs_seek(file, *loc, FS_SEEK_SET);
|
||||
if (rc < 0) {
|
||||
*loc = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = fs_read(file, buf, blen);
|
||||
if (rc <= 0) {
|
||||
*loc = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rc == blen) {
|
||||
rc--;
|
||||
}
|
||||
buf[rc] = '\0';
|
||||
|
||||
end = strchr(buf, '\n');
|
||||
if (end) {
|
||||
*end = '\0';
|
||||
} else {
|
||||
end = strchr(buf, '\0');
|
||||
}
|
||||
blen = end - buf;
|
||||
*loc += (blen + 1);
|
||||
return blen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to load configuration items. cb must be called for every configuration
|
||||
* item found.
|
||||
|
@ -92,37 +58,53 @@ static int settings_file_load(struct settings_store *cs, load_cb cb,
|
|||
void *cb_arg)
|
||||
{
|
||||
struct settings_file *cf = (struct settings_file *)cs;
|
||||
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
|
||||
struct fs_dirent file_info;
|
||||
struct fs_file_t file;
|
||||
off_t loc;
|
||||
char tmpbuf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
|
||||
SETTINGS_EXTRA_LEN];
|
||||
char *name_str;
|
||||
char *val_str;
|
||||
int rc;
|
||||
size_t len_read;
|
||||
int lines;
|
||||
int rc;
|
||||
|
||||
|
||||
struct line_entry_ctx entry_ctx = {
|
||||
.stor_ctx = (void *)&file,
|
||||
.seek = 0,
|
||||
.len = 0 /* unknown length */
|
||||
};
|
||||
|
||||
lines = 0;
|
||||
|
||||
rc = fs_stat(cf->cf_name, &file_info);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = fs_open(&file, cf->cf_name);
|
||||
if (rc != 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
loc = 0;
|
||||
lines = 0;
|
||||
while (1) {
|
||||
rc = settings_getnext_line(&file, tmpbuf, sizeof(tmpbuf), &loc);
|
||||
if (loc == 0) {
|
||||
rc = settings_next_line_ctx(&entry_ctx);
|
||||
|
||||
if (rc || entry_ctx.len == 0) {
|
||||
break;
|
||||
}
|
||||
if (rc < 0) {
|
||||
continue;
|
||||
}
|
||||
rc = settings_line_parse(tmpbuf, &name_str, &val_str);
|
||||
if (rc != 0) {
|
||||
continue;
|
||||
|
||||
rc = settings_line_name_read(buf, sizeof(buf), &len_read,
|
||||
(void *)&entry_ctx);
|
||||
|
||||
if (rc || len_read == 0) {
|
||||
break;
|
||||
}
|
||||
buf[len_read] = '\0';
|
||||
|
||||
/*name, val-read_cb-ctx, val-off*/
|
||||
/* take into account '=' separator after the name */
|
||||
cb(buf, (void *)&entry_ctx, len_read + 1, cb_arg);
|
||||
lines++;
|
||||
cb(name_str, val_str, cb_arg);
|
||||
}
|
||||
|
||||
rc = fs_close(&file);
|
||||
cf->cf_lines = lines;
|
||||
|
||||
|
@ -161,58 +143,101 @@ static int settings_file_create_or_replace(struct fs_file_t *zfp,
|
|||
|
||||
return fs_open(zfp, file_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to compress configuration file by keeping unique names only.
|
||||
*/
|
||||
void settings_file_compress(struct settings_file *cf)
|
||||
int settings_file_save_and_compress(struct settings_file *cf, const char *name,
|
||||
const char *value, size_t val_len)
|
||||
{
|
||||
int rc;
|
||||
int rc, rc2;
|
||||
struct fs_file_t rf;
|
||||
struct fs_file_t wf;
|
||||
char tmp_file[SETTINGS_FILE_NAME_MAX];
|
||||
char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
|
||||
SETTINGS_EXTRA_LEN];
|
||||
char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
|
||||
SETTINGS_EXTRA_LEN];
|
||||
off_t loc1, loc2;
|
||||
char *name1, *val1;
|
||||
char *name2, *val2;
|
||||
char name1[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
|
||||
char name2[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN];
|
||||
struct line_entry_ctx loc1 = {
|
||||
.stor_ctx = &rf,
|
||||
.seek = 0,
|
||||
.len = 0 /* unknown length */
|
||||
};
|
||||
|
||||
struct line_entry_ctx loc2;
|
||||
|
||||
struct line_entry_ctx loc3 = {
|
||||
.stor_ctx = &wf
|
||||
};
|
||||
|
||||
int copy;
|
||||
int len, len2;
|
||||
int lines;
|
||||
size_t new_name_len;
|
||||
off_t val1_off;
|
||||
|
||||
if (fs_open(&rf, cf->cf_name) != 0) {
|
||||
return;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
settings_tmpfile(tmp_file, cf->cf_name, ".cmp");
|
||||
|
||||
if (settings_file_create_or_replace(&wf, tmp_file)) {
|
||||
fs_close(&rf);
|
||||
return;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
loc1 = 0;
|
||||
lines = 0;
|
||||
new_name_len = strlen(name);
|
||||
|
||||
while (1) {
|
||||
len = settings_getnext_line(&rf, buf1, sizeof(buf1), &loc1);
|
||||
if (loc1 == 0 || len < 0) {
|
||||
rc = settings_next_line_ctx(&loc1);
|
||||
|
||||
if (rc || loc1.len == 0) {
|
||||
/* try to amend new value to the commpresed file */
|
||||
break;
|
||||
}
|
||||
rc = settings_line_parse(buf1, &name1, &val1);
|
||||
|
||||
rc = settings_line_name_read(name1, sizeof(name1), &val1_off,
|
||||
&loc1);
|
||||
if (rc) {
|
||||
/* try to process next line */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (val1_off + 1 == loc1.len) {
|
||||
/* Lack of a value so the record is a deletion-record */
|
||||
/* No sense to copy empty entry from */
|
||||
/* the oldest sector */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* avoid copping value which will be overwritten by new value*/
|
||||
if ((val1_off == new_name_len) &&
|
||||
!memcmp(name1, name, val1_off)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
loc2 = loc1;
|
||||
|
||||
copy = 1;
|
||||
while ((len2 = settings_getnext_line(&rf, buf2, sizeof(buf2),
|
||||
&loc2)) > 0) {
|
||||
rc = settings_line_parse(buf2, &name2, &val2);
|
||||
while (1) {
|
||||
off_t val2_off;
|
||||
|
||||
rc = settings_next_line_ctx(&loc2);
|
||||
|
||||
if (rc || loc2.len == 0) {
|
||||
/* try to amend new value to */
|
||||
/* the commpresed file */
|
||||
break;
|
||||
}
|
||||
|
||||
rc = settings_line_name_read(name2, sizeof(name2),
|
||||
&val2_off, &loc2);
|
||||
if (rc) {
|
||||
/* try to process next line */
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(name1, name2)) {
|
||||
copy = 0;
|
||||
if ((val1_off == val2_off) &&
|
||||
!memcmp(name1, name2, val1_off)) {
|
||||
copy = 0; /* newer version doesn't exist */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -220,45 +245,58 @@ void settings_file_compress(struct settings_file *cf)
|
|||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Can't find one. Must copy.
|
||||
*/
|
||||
len = settings_line_make(buf2, sizeof(buf2), name1, val1);
|
||||
if (len < 0 || len + 2 > sizeof(buf2)) {
|
||||
continue;
|
||||
}
|
||||
buf2[len++] = '\n';
|
||||
if (fs_write(&wf, buf2, len) != len) {
|
||||
ARG_UNUSED(fs_close(&rf));
|
||||
ARG_UNUSED(fs_close(&wf));
|
||||
return;
|
||||
loc2 = loc1;
|
||||
loc2.len += 2;
|
||||
loc2.seek -= 2;
|
||||
rc = settings_entry_copy(&loc3, 0, &loc2, 0, loc2.len);
|
||||
if (rc) {
|
||||
/* compressed file might be corrupted */
|
||||
goto end_rolback;
|
||||
}
|
||||
|
||||
lines++;
|
||||
}
|
||||
|
||||
len = fs_close(&wf);
|
||||
len2 = fs_close(&rf);
|
||||
if (len == 0 && len2 == 0 && fs_unlink(cf->cf_name) == 0) {
|
||||
ARG_UNUSED(fs_rename(tmp_file, cf->cf_name));
|
||||
cf->cf_lines = lines;
|
||||
/* at last store the new value */
|
||||
rc = settings_line_write(name, value, val_len, 0, &loc3);
|
||||
if (rc) {
|
||||
/* compressed file might be corrupted */
|
||||
goto end_rolback;
|
||||
}
|
||||
|
||||
rc = fs_close(&wf);
|
||||
rc2 = fs_close(&rf);
|
||||
if (rc == 0 && rc2 == 0 && fs_unlink(cf->cf_name) == 0) {
|
||||
if (fs_rename(tmp_file, cf->cf_name)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
cf->cf_lines = lines + 1;
|
||||
} else {
|
||||
rc = -EIO;
|
||||
}
|
||||
/*
|
||||
* XXX at settings_file_load(), look for .cmp if actual file does not
|
||||
* exist.
|
||||
*/
|
||||
return 0;
|
||||
end_rolback:
|
||||
(void)fs_close(&wf);
|
||||
if (fs_close(&rf) == 0) {
|
||||
(void)fs_unlink(tmp_file);
|
||||
}
|
||||
return -EIO;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Called to save configuration.
|
||||
*/
|
||||
static int settings_file_save(struct settings_store *cs, const char *name,
|
||||
const char *value)
|
||||
const char *value, size_t val_len)
|
||||
{
|
||||
struct settings_file *cf = (struct settings_file *)cs;
|
||||
struct line_entry_ctx entry_ctx;
|
||||
struct fs_file_t file;
|
||||
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
|
||||
SETTINGS_EXTRA_LEN];
|
||||
int len;
|
||||
int rc2;
|
||||
int rc;
|
||||
|
||||
|
@ -271,13 +309,9 @@ static int settings_file_save(struct settings_store *cs, const char *name,
|
|||
* Compress before config file size exceeds
|
||||
* the max number of lines.
|
||||
*/
|
||||
settings_file_compress(cf);
|
||||
return settings_file_save_and_compress(cf, name, value,
|
||||
val_len);
|
||||
}
|
||||
len = settings_line_make(buf, sizeof(buf), name, value);
|
||||
if (len < 0 || len + 2 > sizeof(buf)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
buf[len++] = '\n';
|
||||
|
||||
/*
|
||||
* Open the file to add this one value.
|
||||
|
@ -286,8 +320,10 @@ static int settings_file_save(struct settings_store *cs, const char *name,
|
|||
if (rc == 0) {
|
||||
rc = fs_seek(&file, 0, FS_SEEK_END);
|
||||
if (rc == 0) {
|
||||
rc2 = fs_write(&file, buf, len);
|
||||
if (rc2 == len) {
|
||||
entry_ctx.stor_ctx = &file;
|
||||
rc2 = settings_line_write(name, value, val_len, 0,
|
||||
(void *)&entry_ctx);
|
||||
if (rc2 == 0) {
|
||||
cf->cf_lines++;
|
||||
}
|
||||
}
|
||||
|
@ -300,3 +336,63 @@ static int settings_file_save(struct settings_store *cs, const char *name,
|
|||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int read_handler(void *ctx, off_t off, char *buf, size_t *len)
|
||||
{
|
||||
struct line_entry_ctx *entry_ctx = ctx;
|
||||
struct fs_file_t *file = entry_ctx->stor_ctx;
|
||||
ssize_t r_len;
|
||||
int rc;
|
||||
|
||||
/* 0 is reserved for reding the length-field only */
|
||||
if (entry_ctx->len != 0) {
|
||||
if (off >= entry_ctx->len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((off + *len) > entry_ctx->len) {
|
||||
*len = entry_ctx->len - off;
|
||||
}
|
||||
}
|
||||
|
||||
rc = fs_seek(file, entry_ctx->seek + off, FS_SEEK_SET);
|
||||
if (rc) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r_len = fs_read(file, buf, *len);
|
||||
|
||||
if (r_len >= 0) {
|
||||
*len = r_len;
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = r_len;
|
||||
}
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int write_handler(void *ctx, off_t off, char const *buf, size_t len)
|
||||
{
|
||||
struct line_entry_ctx *entry_ctx = ctx;
|
||||
struct fs_file_t *file = entry_ctx->stor_ctx;
|
||||
int rc;
|
||||
|
||||
/* append to file only */
|
||||
rc = fs_seek(file, 0, FS_SEEK_END);
|
||||
|
||||
if (rc == 0) {
|
||||
rc = fs_write(file, buf, len);
|
||||
|
||||
if (rc > 0) {
|
||||
rc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void settings_mount_fs_backend(struct settings_file *cf)
|
||||
{
|
||||
settings_line_io_init(read_handler, write_handler, 1);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,8 @@ static void settings_init_fs(void)
|
|||
if (rc) {
|
||||
k_panic();
|
||||
}
|
||||
|
||||
settings_mount_fs_backend(&config_init_settings_file);
|
||||
}
|
||||
|
||||
#elif defined(CONFIG_SETTINGS_FCB)
|
||||
|
@ -93,6 +95,8 @@ static void settings_init_fcb(void)
|
|||
if (rc != 0) {
|
||||
k_panic();
|
||||
}
|
||||
|
||||
settings_mount_fcb_backend(&config_init_settings_fcb);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include "settings/settings.h"
|
||||
#include "settings_priv.h"
|
||||
#include "base64.h"
|
||||
#include "misc/printk.h"
|
||||
|
||||
int settings_line_parse(char *buf, char **namep, char **valp)
|
||||
{
|
||||
|
@ -87,3 +89,360 @@ int settings_line_make(char *dst, int dlen, const char *name, const char *value)
|
|||
|
||||
return off;
|
||||
}
|
||||
|
||||
struct settings_io_cb_s {
|
||||
int (*read_cb)(void *ctx, off_t off, char *buf, size_t *len);
|
||||
int (*write_cb)(void *ctx, off_t off, char const *buf, size_t len);
|
||||
u8_t rwbs;
|
||||
} settings_io_cb;
|
||||
|
||||
#define MAX_ENC_BLOCK_SIZE 4
|
||||
|
||||
int settings_line_write(const char *name, const char *value, size_t val_len,
|
||||
off_t w_loc, void *cb_arg)
|
||||
{
|
||||
size_t w_size, rem, add, enc_len;
|
||||
|
||||
/* minimal buffer for encoding base64 + EOL*/
|
||||
char enc_buf[MAX_ENC_BLOCK_SIZE + 1];
|
||||
|
||||
char *p_enc = enc_buf;
|
||||
bool done;
|
||||
char w_buf[16]; /* write buff, must be aligned either to minimal */
|
||||
/* base64 encoding size and write-block-size */
|
||||
int rc;
|
||||
u8_t wbs = settings_io_cb.rwbs;
|
||||
#ifdef CONFIG_SETTINGS_ENCODE_LEN
|
||||
u16_t len_field;
|
||||
#endif
|
||||
|
||||
rem = strlen(name);
|
||||
|
||||
#ifdef CONFIG_SETTINGS_ENCODE_LEN
|
||||
len_field = settings_line_len_calc(name, val_len);
|
||||
memcpy(w_buf, &len_field, sizeof(len_field));
|
||||
w_size = 0;
|
||||
|
||||
|
||||
add = sizeof(len_field) % wbs;
|
||||
if (add) {
|
||||
w_size = wbs - add;
|
||||
if (rem < w_size) {
|
||||
w_size = rem;
|
||||
}
|
||||
|
||||
memcpy(w_buf + sizeof(len_field), name, w_size);
|
||||
name += w_size;
|
||||
rem -= w_size;
|
||||
}
|
||||
|
||||
w_size += sizeof(len_field);
|
||||
if (w_size % wbs == 0) {
|
||||
rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
|
||||
if (rc) {
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
/* The Alternative to condition above mean that `rem == 0` as `name` */
|
||||
/* must have been consumed */
|
||||
#endif
|
||||
w_size = rem - rem % wbs;
|
||||
rem %= wbs;
|
||||
|
||||
rc = settings_io_cb.write_cb(cb_arg, w_loc, name, w_size);
|
||||
w_loc += w_size;
|
||||
name += w_size;
|
||||
w_size = rem;
|
||||
|
||||
if (rem) {
|
||||
memcpy(w_buf, name, rem);
|
||||
}
|
||||
|
||||
w_buf[rem] = '=';
|
||||
w_size++;
|
||||
|
||||
rem = val_len;
|
||||
enc_len = 0;
|
||||
done = false;
|
||||
|
||||
while (1) {
|
||||
while (w_size < sizeof(w_buf)) {
|
||||
if (enc_len) {
|
||||
add = min(enc_len, sizeof(w_buf) - w_size);
|
||||
memcpy(&w_buf[w_size], p_enc, add);
|
||||
enc_len -= add;
|
||||
w_size += add;
|
||||
p_enc += add;
|
||||
} else {
|
||||
if (rem) {
|
||||
add = min(rem, MAX_ENC_BLOCK_SIZE/4*3);
|
||||
rc = base64_encode(enc_buf, sizeof(enc_buf), &enc_len, value, add);
|
||||
if (rc) {
|
||||
return -EINVAL;
|
||||
}
|
||||
value += add;
|
||||
rem -= add;
|
||||
p_enc = enc_buf;
|
||||
} else {
|
||||
add = (w_size) % wbs;
|
||||
if (add) {
|
||||
add = wbs - add;
|
||||
memset(&w_buf[w_size], '\0',
|
||||
add);
|
||||
w_size += add;
|
||||
}
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = settings_io_cb.write_cb(cb_arg, w_loc, w_buf, w_size);
|
||||
if (rc) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
w_loc += w_size;
|
||||
w_size = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SETTINGS_ENCODE_LEN
|
||||
int settings_next_line_ctx(struct line_entry_ctx *entry_ctx)
|
||||
{
|
||||
size_t len_read;
|
||||
u16_t readout;
|
||||
int rc;
|
||||
|
||||
entry_ctx->seek += entry_ctx->len; /* to begin of nex line */
|
||||
|
||||
entry_ctx->len = 0; /* ask read handler to ignore len */
|
||||
|
||||
rc = settings_line_raw_read(0, (char *)&readout, sizeof(readout),
|
||||
&len_read, entry_ctx);
|
||||
if (rc == 0) {
|
||||
if (len_read != sizeof(readout)) {
|
||||
if (len_read != 0) {
|
||||
rc = -ESPIPE;
|
||||
}
|
||||
} else {
|
||||
entry_ctx->seek += sizeof(readout);
|
||||
entry_ctx->len = readout;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
int settings_line_len_calc(const char *name, size_t val_len)
|
||||
{
|
||||
int len, mod;
|
||||
|
||||
/* <enc(value)> */
|
||||
len = val_len/3*4 + ((val_len%3) ? 4 : 0);
|
||||
/* <name>=<enc(value)> */
|
||||
len += strlen(name) + 1;
|
||||
mod = len % settings_io_cb.rwbs;
|
||||
if (mod) {
|
||||
/*additional \0 for meet flash alignment */
|
||||
len += settings_io_cb.rwbs - mod;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read RAW settings line entry data until a char from the storage.
|
||||
*
|
||||
* @param seek offset form the line beginning.
|
||||
* @param[out] out buffer for name
|
||||
* @param[in] len_req size of <p>out</p> buffer
|
||||
* @param[out] len_read length of read name
|
||||
* @param[in] until_char pointer on char value until which all line data will
|
||||
* be read. If Null entire data will be read.
|
||||
* @param[in] cb_arg settings line storage context expected by the
|
||||
* <p>read_cb</p> implementation
|
||||
*
|
||||
* @retval 0 on success,
|
||||
* -ERCODE on storage errors
|
||||
*/
|
||||
static int settings_line_raw_read_until(off_t seek, char *out, size_t len_req,
|
||||
size_t *len_read, char const *until_char,
|
||||
void *cb_arg)
|
||||
{
|
||||
size_t rem_size, len;
|
||||
char temp_buf[16]; /* buffer for fit read-block-size requirements */
|
||||
size_t exp_size, read_size;
|
||||
u8_t rbs = settings_io_cb.rwbs;
|
||||
off_t off;
|
||||
int rc;
|
||||
|
||||
rem_size = len_req;
|
||||
|
||||
while (rem_size) {
|
||||
off = seek / rbs * rbs;
|
||||
|
||||
read_size = sizeof(temp_buf);
|
||||
exp_size = read_size;
|
||||
|
||||
rc = settings_io_cb.read_cb(cb_arg, off, temp_buf, &read_size);
|
||||
if (rc) {
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
off = seek - off;
|
||||
len = read_size - off;
|
||||
len = min(rem_size, len);
|
||||
|
||||
if (until_char != NULL) {
|
||||
char *pend;
|
||||
pend = memchr(&temp_buf[off], *until_char, len);
|
||||
if (pend != NULL) {
|
||||
len = pend - &temp_buf[off];
|
||||
rc = 1; /* will cause loop expiration */
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(out, &temp_buf[off], len);
|
||||
|
||||
rem_size -= len;
|
||||
|
||||
if (exp_size > read_size || rc) {
|
||||
break;
|
||||
}
|
||||
|
||||
out += len;
|
||||
seek += len;
|
||||
}
|
||||
|
||||
*len_read = len_req - rem_size;
|
||||
|
||||
if (until_char != NULL) {
|
||||
return (rc) ? 0 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int settings_line_raw_read(off_t seek, char *out, size_t len_req,
|
||||
size_t *len_read, void *cb_arg)
|
||||
{
|
||||
return settings_line_raw_read_until(seek, out, len_req, len_read,
|
||||
NULL, cb_arg);
|
||||
}
|
||||
|
||||
/* off from value begin */
|
||||
int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
|
||||
size_t *len_read, void *cb_arg)
|
||||
{
|
||||
char enc_buf[16 + 1];
|
||||
char dec_buf[sizeof(enc_buf)/4 * 3 + 1];
|
||||
size_t rem_size, read_size, exp_size, clen, olen;
|
||||
off_t seek_begin, off_begin;
|
||||
int rc;
|
||||
|
||||
|
||||
rem_size = len_req;
|
||||
|
||||
while (rem_size) {
|
||||
seek_begin = off / 3 * 4;
|
||||
off_begin = seek_begin / 4 * 3;
|
||||
|
||||
read_size = rem_size / 3 * 4;
|
||||
read_size += (rem_size % 3 != 0 || off_begin != off) ? 4 : 0;
|
||||
|
||||
read_size = min(read_size, sizeof(enc_buf) - 1);
|
||||
exp_size = read_size;
|
||||
|
||||
rc = settings_line_raw_read(val_off + seek_begin, enc_buf,
|
||||
read_size, &read_size, cb_arg);
|
||||
if (rc) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
enc_buf[read_size] = 0; /* breaking guaranted */
|
||||
read_size = strlen(enc_buf);
|
||||
|
||||
if (read_size == 0 || read_size % 4) {
|
||||
/* unexpected use case - a NULL value or an encoding */
|
||||
/* problem */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = base64_decode(dec_buf, sizeof(dec_buf), &olen, enc_buf,
|
||||
read_size);
|
||||
dec_buf[olen] = 0;
|
||||
|
||||
clen = min(olen + off_begin - off, rem_size);
|
||||
|
||||
memcpy(out, &dec_buf[off - off_begin], clen);
|
||||
rem_size -= clen;
|
||||
|
||||
if (exp_size > read_size || olen < read_size/4*3) {
|
||||
break;
|
||||
}
|
||||
|
||||
out += clen;
|
||||
off += clen;
|
||||
}
|
||||
|
||||
*len_read = len_req - rem_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int settings_line_name_read(char *out, size_t len_req, size_t *len_read,
|
||||
void *cb_arg)
|
||||
{
|
||||
char const until_char = '=';
|
||||
|
||||
return settings_line_raw_read_until(0, out, len_req, len_read,
|
||||
&until_char, cb_arg);
|
||||
}
|
||||
|
||||
|
||||
int settings_entry_copy(void *dst_ctx, off_t dst_off, void *src_ctx,
|
||||
off_t src_off, size_t len)
|
||||
{
|
||||
int rc;
|
||||
char buf[16];
|
||||
size_t chunk_size;
|
||||
|
||||
while (len) {
|
||||
chunk_size = min(len, sizeof(buf));
|
||||
|
||||
rc = settings_io_cb.read_cb(src_ctx, src_off, buf, &chunk_size);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
|
||||
rc = settings_io_cb.write_cb(dst_ctx, dst_off, buf, chunk_size);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
|
||||
src_off += chunk_size;
|
||||
dst_off += chunk_size;
|
||||
len -= chunk_size;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
|
||||
size_t *len),
|
||||
int (*write_cb)(void *ctx, off_t off, char const *buf,
|
||||
size_t len),
|
||||
u8_t io_rwbs)
|
||||
{
|
||||
settings_io_cb.read_cb = read_cb;
|
||||
settings_io_cb.write_cb = write_cb;
|
||||
settings_io_cb.rwbs = io_rwbs;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
#ifndef __SETTINGS_PRIV_H_
|
||||
#define __SETTINGS_PRIV_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -24,18 +27,107 @@ int settings_line_make(char *dst, int dlen, const char *name, const char *val);
|
|||
int settings_line_make2(char *dst, int dlen, const char *name,
|
||||
const char *value);
|
||||
|
||||
|
||||
|
||||
void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
|
||||
size_t *len),
|
||||
int (*write_cb)(void *ctx, off_t off,
|
||||
char const *buf, size_t len),
|
||||
u8_t io_rwbs);
|
||||
|
||||
int settings_line_write(const char *name, const char *value, size_t val_len,
|
||||
off_t w_loc, void *cb_arg);
|
||||
|
||||
int settings_line_len_calc(const char *name, size_t val_len);
|
||||
|
||||
#ifdef CONFIG_SETTINGS_ENCODE_LEN
|
||||
/* in storage line contex */
|
||||
struct line_entry_ctx {
|
||||
void *stor_ctx;
|
||||
off_t seek; /* offset of id-value pair within the file */
|
||||
size_t len; /* len of line without len value */
|
||||
};
|
||||
|
||||
int settings_next_line_ctx(struct line_entry_ctx *entry_ctx);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Read RAW settings line entry data from the storage.
|
||||
*
|
||||
* @param seek offset form the line beginning.
|
||||
* @param[out] out buffer for name
|
||||
* @param[in] len_req size of <p>out</p> buffer
|
||||
* @param[out] len_read length of read name
|
||||
* @param[in] cb_arg settings line storage context expected by the
|
||||
* <p>read_cb</p> implementatio
|
||||
*
|
||||
* @retval 0 on success,
|
||||
* -ERCODE on storage errors
|
||||
*/
|
||||
int settings_line_raw_read(off_t seek, char *out, size_t len_req,
|
||||
size_t *len_read, void *cb_arg);
|
||||
|
||||
/*
|
||||
* @param val_off offset of the value-string.
|
||||
* @param off from val_off (so within the value-string)
|
||||
*/
|
||||
int settings_line_val_read(off_t val_off, off_t off, char *out, size_t len_req,
|
||||
size_t *len_read, void *cb_arg);
|
||||
|
||||
/**
|
||||
* Read the settings line entry name from the storage.
|
||||
*
|
||||
* @param[out] out buffer for name
|
||||
* @param[in] len_req size of <p>out</p> buffer
|
||||
* @param[out] len_read length of read name
|
||||
* @param[in] cb_arg settings line storage context expected by the
|
||||
* <p>read_cb</p> implementation
|
||||
*
|
||||
* @retval 0 on read proper name,
|
||||
* 1 on when read improper name,
|
||||
* -ERCODE on storage errors
|
||||
*/
|
||||
int settings_line_name_read(char *out, size_t len_req, size_t *len_read,
|
||||
void *cb_arg);
|
||||
|
||||
int settings_entry_copy(void *dst_ctx, off_t dst_off, void *src_ctx,
|
||||
off_t src_off, size_t len);
|
||||
|
||||
void settings_line_io_init(int (*read_cb)(void *ctx, off_t off, char *buf,
|
||||
size_t *len),
|
||||
int (*write_cb)(void *ctx, off_t off, char const *buf,
|
||||
size_t len),
|
||||
u8_t io_rwbs);
|
||||
|
||||
int settings_set_value_priv(char *name, void *val_read_cb_ctx, off_t off,
|
||||
u8_t is_runtime);
|
||||
|
||||
/*
|
||||
* API for config storage.
|
||||
*/
|
||||
typedef void (*load_cb)(char *name, char *val, void *cb_arg);
|
||||
typedef void (*load_cb)(char *name, void *val_read_cb_ctx, off_t off,
|
||||
void *cb_arg);
|
||||
|
||||
struct settings_store_itf {
|
||||
int (*csi_load)(struct settings_store *cs, load_cb cb, void *cb_arg);
|
||||
int (*csi_save_start)(struct settings_store *cs);
|
||||
int (*csi_save)(struct settings_store *cs, const char *name,
|
||||
const char *value);
|
||||
const char *value, size_t val_len);
|
||||
int (*csi_save_end)(struct settings_store *cs);
|
||||
};
|
||||
|
||||
struct read_value_cb_ctx {
|
||||
void *read_cb_ctx;
|
||||
off_t off;
|
||||
u8_t runtime;
|
||||
};
|
||||
|
||||
/* suports runtime settings_set_value operation */
|
||||
struct runtime_value_ctx {
|
||||
void *p_value;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void settings_src_register(struct settings_store *cs);
|
||||
void settings_dst_register(struct settings_store *cs);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
struct settings_dup_check_arg {
|
||||
const char *name;
|
||||
const char *val;
|
||||
size_t val_len;
|
||||
int is_dup;
|
||||
};
|
||||
|
||||
|
@ -44,9 +45,10 @@ void settings_dst_register(struct settings_store *cs)
|
|||
settings_save_dst = cs;
|
||||
}
|
||||
|
||||
static void settings_load_cb(char *name, char *val, void *cb_arg)
|
||||
static void settings_load_cb(char *name, void *val_read_cb_ctx, off_t off,
|
||||
void *cb_arg)
|
||||
{
|
||||
int rc = settings_set_value(name, val);
|
||||
int rc = settings_set_value_priv(name, val_read_cb_ctx, off, 0);
|
||||
__ASSERT(rc == 0, "set-value operation failure\n");
|
||||
(void)rc;
|
||||
}
|
||||
|
@ -68,22 +70,66 @@ int settings_load(void)
|
|||
return settings_commit(NULL);
|
||||
}
|
||||
|
||||
static void settings_dup_check_cb(char *name, char *val, void *cb_arg)
|
||||
/* val_off - offset of value-string within line entries */
|
||||
static int settings_cmp(char const *val, size_t val_len, void *val_read_cb_ctx,
|
||||
off_t val_off)
|
||||
{
|
||||
size_t len_read, exp_len;
|
||||
size_t rem;
|
||||
char buf[16];
|
||||
int rc;
|
||||
off_t off = 0;
|
||||
|
||||
for (rem = val_len; rem > 0; rem -= len_read) {
|
||||
len_read = exp_len = min(sizeof(buf), rem);
|
||||
rc = settings_line_val_read(val_off, off, buf, len_read,
|
||||
&len_read, val_read_cb_ctx);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len_read != exp_len) {
|
||||
rc = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = memcmp(val, buf, len_read);
|
||||
if (rc) {
|
||||
break;
|
||||
}
|
||||
val += len_read;
|
||||
off += len_read;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void settings_dup_check_cb(char *name, void *val_read_cb_ctx, off_t off,
|
||||
void *cb_arg)
|
||||
{
|
||||
struct settings_dup_check_arg *cdca = (struct settings_dup_check_arg *)
|
||||
cb_arg;
|
||||
size_t len_read;
|
||||
char temp;
|
||||
int rc;
|
||||
|
||||
|
||||
if (strcmp(name, cdca->name)) {
|
||||
return;
|
||||
}
|
||||
if (!val) {
|
||||
|
||||
rc = settings_line_raw_read(off, &temp, 1, &len_read,
|
||||
val_read_cb_ctx);
|
||||
if (rc || len_read == 0 || temp == '\0') {
|
||||
/* treat as an empty entry */
|
||||
if (!cdca->val || cdca->val[0] == '\0') {
|
||||
cdca->is_dup = 1;
|
||||
} else {
|
||||
cdca->is_dup = 0;
|
||||
}
|
||||
} else {
|
||||
if (cdca->val && !strcmp(val, cdca->val)) {
|
||||
if (cdca->val && !settings_cmp(cdca->val, cdca->val_len,
|
||||
val_read_cb_ctx, off)) {
|
||||
cdca->is_dup = 1;
|
||||
} else {
|
||||
cdca->is_dup = 0;
|
||||
|
@ -94,7 +140,7 @@ static void settings_dup_check_cb(char *name, char *val, void *cb_arg)
|
|||
/*
|
||||
* Append a single value to persisted config. Don't store duplicate value.
|
||||
*/
|
||||
int settings_save_one(const char *name, char *value)
|
||||
int settings_save_one(const char *name, void *value, size_t val_len)
|
||||
{
|
||||
struct settings_store *cs;
|
||||
struct settings_dup_check_arg cdca;
|
||||
|
@ -108,13 +154,14 @@ int settings_save_one(const char *name, char *value)
|
|||
* Check if we're writing the same value again.
|
||||
*/
|
||||
cdca.name = name;
|
||||
cdca.val = value;
|
||||
cdca.val = (char *)value;
|
||||
cdca.is_dup = 0;
|
||||
cdca.val_len = val_len;
|
||||
cs->cs_itf->csi_load(cs, settings_dup_check_cb, &cdca);
|
||||
if (cdca.is_dup == 1) {
|
||||
return 0;
|
||||
}
|
||||
return cs->cs_itf->csi_save(cs, name, value);
|
||||
return cs->cs_itf->csi_save(cs, name, (char *)value, val_len);
|
||||
}
|
||||
|
||||
int settings_save(void)
|
||||
|
@ -136,8 +183,7 @@ int settings_save(void)
|
|||
|
||||
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
|
||||
if (ch->h_export) {
|
||||
rc2 = ch->h_export(settings_save_one,
|
||||
SETTINGS_EXPORT_PERSIST);
|
||||
rc2 = ch->h_export(settings_save_one);
|
||||
if (!rc) {
|
||||
rc = rc2;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue