blob: 9fcd0fac5387d4786c31629d84644ba90226a8cf [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test_backend.h"
#include <stdio.h>
#include <stddef.h>
#include <zephyr/kernel.h>
#include "zephyr/types.h"
#include "errno.h"
#include <zephyr/bluetooth/mesh.h>
#include "argparse.h"
#define LOG_MODULE_NAME settings_test_backend
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#define SETTINGS_FILE setting_file
#define SETTINGS_FILE_TMP setting_file_tmp
#define ENTRY_LEN_SIZE (4)
#define ENTRY_NAME_MAX_LEN (SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN)
#define ENTRY_VAL_MAX_LEN (SETTINGS_MAX_VAL_LEN * 2)
#define READ_LEN_MAX (ENTRY_VAL_MAX_LEN + ENTRY_NAME_MAX_LEN + 2)
struct line_read_ctx {
int len;
const uint8_t *val;
};
static char setting_file[50];
static char setting_file_tmp[sizeof(setting_file) + 1];
static int entry_check_and_copy(FILE *fin, FILE *fout, const char *name)
{
char line[READ_LEN_MAX + 1];
while (fgets(line, sizeof(line), fin) == line) {
if (strstr(line, name) != NULL) {
return 0;
}
if (fputs(line, fout) < 0) {
return -1;
}
};
return 0;
}
static ssize_t settings_line_read_cb(void *cb_arg, void *data, size_t len)
{
struct line_read_ctx *valctx = (struct line_read_ctx *)cb_arg;
if ((valctx->len / 2) > len) {
return -ENOBUFS;
}
uint8_t *n = (uint8_t *) data;
len = valctx->len / 2;
for (int i = 0; i < len; i++, n++) {
if (sscanf(&valctx->val[i * 2], "%2hhx", n) != 1) {
return 0;
};
}
return len;
}
static int settings_custom_load(struct settings_store *cs, const struct settings_load_arg *arg)
{
FILE *fp = fopen(SETTINGS_FILE, "r+");
if (fp == NULL) {
LOG_WRN("Settings file is missing");
return -1;
}
if (fseek(fp, 0, SEEK_SET) < 0) {
return -1;
}
int vallen;
char line[READ_LEN_MAX + 1];
while (fgets(line, sizeof(line), fp) == line) {
/* check for matching subtree */
if (arg->subtree != NULL && !strstr(line, arg->subtree)) {
continue;
}
char *pos = strchr(line, '=');
if (pos <= line) {
return -1;
}
vallen = strlen(line) - (pos - line) - 2;
LOG_INF("loading entry: %s", line);
struct line_read_ctx valctx;
valctx.len = vallen;
valctx.val = pos + 1;
int err = settings_call_set_handler(line, vallen / 2, settings_line_read_cb,
&valctx, arg);
if (err < 0) {
return err;
}
};
return fclose(fp);
}
/* Entries are saved to optimize readability of the settings file for test development and
* debugging purposes. Format:
* <entry-key>=<entry-value-hex-str>\n
*/
static int settings_custom_save(struct settings_store *cs, const char *name,
const char *value, size_t val_len)
{
FILE *fcur = fopen(SETTINGS_FILE, "r+");
FILE *fnew = NULL;
if (fcur == NULL) {
fcur = fopen(SETTINGS_FILE, "w");
} else {
fnew = fopen(SETTINGS_FILE_TMP, "w");
if (fnew == NULL) {
LOG_ERR("Failed to create temporary file %s", SETTINGS_FILE_TMP);
return -1;
}
}
if (fcur == NULL) {
LOG_ERR("Failed to create settings file: %s", SETTINGS_FILE);
return -1;
}
if (strlen(name) > ENTRY_NAME_MAX_LEN || val_len > SETTINGS_MAX_VAL_LEN) {
return -1;
}
if (fnew != NULL) {
if (entry_check_and_copy(fcur, fnew, name) < 0) {
return -1;
}
}
if (val_len) {
char bufvname[ENTRY_NAME_MAX_LEN + ENTRY_LEN_SIZE + 3];
snprintk(bufvname, sizeof(bufvname), "%s=", name);
if (fputs(bufvname, fnew != NULL ? fnew : fcur) < 0) {
return -1;
}
char bufval[ENTRY_VAL_MAX_LEN + 2] = {};
size_t valcnt = 0;
while (valcnt < (val_len * 2)) {
valcnt += snprintk(&bufval[valcnt], 3, "%02x",
(uint8_t)value[valcnt / 2]);
};
/* helps in making settings file readable */
bufval[valcnt++] = '\n';
bufval[valcnt] = 0;
if (fputs(bufval, fnew != NULL ? fnew : fcur) < 0) {
return -1;
}
}
if (fnew != NULL) {
entry_check_and_copy(fcur, fnew, name);
}
fclose(fcur);
if (fnew != NULL) {
fclose(fnew);
remove(SETTINGS_FILE);
rename(SETTINGS_FILE_TMP, SETTINGS_FILE);
}
return 0;
}
/* custom backend interface */
static struct settings_store_itf settings_custom_itf = {
.csi_load = settings_custom_load,
.csi_save = settings_custom_save,
};
/* custom backend node */
static struct settings_store settings_custom_store = {
.cs_itf = &settings_custom_itf
};
int settings_backend_init(void)
{
snprintf(setting_file, sizeof(setting_file), "%s_%d.log", get_simid(), get_device_nbr());
snprintf(setting_file_tmp, sizeof(setting_file_tmp), "~%s", setting_file);
LOG_INF("file path: %s", SETTINGS_FILE);
/* register custom backend */
settings_dst_register(&settings_custom_store);
settings_src_register(&settings_custom_store);
return 0;
}
void settings_test_backend_clear(void)
{
snprintf(setting_file, sizeof(setting_file), "%s_%d.log", get_simid(), get_device_nbr());
if (remove(setting_file)) {
LOG_INF("error deleting file: %s", setting_file);
}
}