nuttx-apps/system/settings/settings.c
2024-08-30 21:29:50 +08:00

1599 lines
36 KiB
C

/****************************************************************************
* apps/system/settings/settings.c
*
* 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <nuttx/crc32.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <nuttx/config.h>
#include "storage.h"
#include "system/settings.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_SYSTEM_SETTINGS_CACHED_SAVES
# define TIMER_SIGNAL SIGRTMIN
#endif
#ifndef CONFIG_SYSTEM_SETTINGS_CACHE_TIME_MS
# define CONFIG_SYSTEM_SETTINGS_CACHE_TIME_MS 100
#endif
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
static int sanity_check(FAR char *str);
static uint32_t hash_calc(void);
static int get_setting(FAR char *key, FAR setting_t **setting);
static size_t get_string(FAR setting_t *setting, FAR char *buffer,
size_t size);
static int set_string(FAR setting_t *setting, FAR char *str);
static int get_int(FAR setting_t *setting, FAR int *i);
static int set_int(FAR setting_t *setting, int i);
static int get_bool(FAR setting_t *setting, FAR int *i);
static int set_bool(FAR setting_t *setting, int i);
static int get_float(FAR setting_t *setting, FAR double *f);
static int set_float(FAR setting_t *setting, FAR double f);
static int get_ip(FAR setting_t *setting, FAR struct in_addr *ip);
static int set_ip(FAR setting_t *setting, FAR struct in_addr *ip);
static int load(void);
static void save(void);
static void signotify(void);
static void dump_cache(union sigval ptr);
/****************************************************************************
* Private Data
****************************************************************************/
static struct
{
pthread_mutex_t mtx;
uint32_t hash;
bool wrpend;
bool initialized;
storage_t store[CONFIG_SYSTEM_SETTINGS_MAX_STORAGES];
struct notify_s notify[CONFIG_SYSTEM_SETTINGS_MAX_SIGNALS];
#if defined(CONFIG_SYSTEM_SETTINGS_CACHED_SAVES)
struct sigevent sev;
struct itimerspec trigger;
timer_t timerid;
#endif
} g_settings;
setting_t map[CONFIG_SYSTEM_SETTINGS_MAP_SIZE];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: hash_calc
*
* Description:
* Gets a setting for a string value
*
* Input Parameters:
* none
* Returned Value:
* crc32 hash of all the settings
*
****************************************************************************/
static uint32_t hash_calc(void)
{
return crc32((FAR uint8_t *)map, sizeof(map));
}
/****************************************************************************
* Name: get_setting
*
* Description:
* Gets a setting for a given key
*
* Input Parameters:
* key - key of the required setting
* setting - pointer to pointer for the setting
*
* Returned Value:
* The value of the setting for the given key
*
****************************************************************************/
static int get_setting(FAR char *key, FAR setting_t **setting)
{
int i;
int ret = OK;
assert(*setting == NULL);
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++)
{
if (map[i].type == SETTING_EMPTY)
{
ret = -ENOENT;
goto exit;
}
if (strcmp(map[i].key, key) == 0)
{
*setting = &map[i];
goto exit;
}
}
*setting = NULL;
ret = -ENOENT;
exit:
return ret;
}
/****************************************************************************
* Name: get_string
*
* Description:
* Gets a setting for a string value
*
* Input Parameters:
* setting - pointer to the setting type
* str - pointer to return the string value
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static size_t get_string(FAR setting_t *setting, FAR char *buffer,
size_t size)
{
assert(setting);
if ((setting->type != SETTING_STRING) &&
(setting->type != SETTING_IP_ADDR))
{
return -EACCES;
}
if (setting->type == SETTING_STRING)
{
FAR const char *s = setting->val.s;
size_t len = strlen(s);
assert(len < size);
if (len >= size)
{
return 0;
}
strncpy(buffer, s, size);
buffer[size - 1] = '\0';
return len;
}
else if (setting->type == SETTING_IP_ADDR)
{
assert(INET_ADDRSTRLEN < size);
inet_ntop(AF_INET, &setting->val.ip, buffer, size);
buffer[size - 1] = '\0';
return strlen(buffer);
}
return 0;
}
/****************************************************************************
* Name: set_string
*
* Description:
* Creates a setting for a string value setting->type != set_
*
* Input Parameters:
* setting - pointer to the setting type
* str - the string value
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int set_string(FAR setting_t *setting, FAR char *str)
{
assert(setting);
if ((setting->type != SETTING_STRING) &&
(setting->type != SETTING_IP_ADDR))
{
return -EACCES;
}
ASSERT(strlen(str) < CONFIG_SYSTEM_SETTINGS_VALUE_SIZE);
if (strlen(str) >= CONFIG_SYSTEM_SETTINGS_VALUE_SIZE)
{
return -EINVAL;
}
if (strlen(str) && (sanity_check(str) < 0))
{
return -EINVAL;
}
setting->type = SETTING_STRING;
strncpy(setting->val.s, str, CONFIG_SYSTEM_SETTINGS_VALUE_SIZE);
setting->val.s[CONFIG_SYSTEM_SETTINGS_VALUE_SIZE - 1] = '\0';
return OK;
}
/****************************************************************************
* Name: get_int
*
* Description:
* Gets a setting for an integer value
*
* Input Parameters:
* setting - pointer to the setting type
* i - pointer to return the integer value
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int get_int(FAR setting_t *setting, FAR int *i)
{
assert(setting);
if ((setting->type != SETTING_INT) &&
(setting->type != SETTING_BOOL) &&
(setting->type != SETTING_FLOAT))
{
return -EACCES;
}
if (setting->type == SETTING_INT)
{
*i = setting->val.i;
}
else if (setting->type == SETTING_BOOL)
{
*i = !!setting->val.i;
}
else if (setting->type == SETTING_FLOAT)
{
*i = (int)setting->val.f;
}
else
{
return -EINVAL;
}
return OK;
}
/****************************************************************************
* Name: set_int
*
* Description:
* Creates a setting for an integer value
*
* Input Parameters:
* setting - pointer to the setting type
* i - the integer value
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int set_int(FAR setting_t *setting, int i)
{
assert(setting);
if ((setting->type != SETTING_INT) &&
(setting->type != SETTING_BOOL) &&
(setting->type != SETTING_FLOAT))
{
return -EACCES;
}
setting->type = SETTING_INT;
setting->val.i = i;
return OK;
}
/****************************************************************************
* Name: get_bool
*
* Description:
* Gets a setting for a boolean value
*
* Input Parameters:
* setting - pointer to the setting type
* f - pointer to return the boolean value
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int get_bool(FAR setting_t *setting, FAR int *i)
{
assert(setting);
if ((setting->type != SETTING_BOOL) &&
(setting->type != SETTING_INT))
{
return -EACCES;
}
if ((setting->type == SETTING_INT) || (setting->type == SETTING_BOOL))
{
*i = !!setting->val.i;
}
else
{
return -EINVAL;
}
return OK;
}
/****************************************************************************
* Name: set_bool
*
* Description:
* Creates a setting for a boolean value
*
* Input Parameters:
* setting - pointer to the setting type
* i - the boolean value
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int set_bool(FAR setting_t *setting, int i)
{
assert(setting);
if ((setting->type != SETTING_BOOL) && (setting->type != SETTING_INT))
{
return -EACCES;
}
setting->type = SETTING_BOOL;
setting->val.i = !!i;
return OK;
}
/****************************************************************************
* Name: get_float
*
* Description:
* Gets a setting for a float value
*
* Input Parameters:
* setting - pointer to the setting type
* f - pointer to return the floating point value
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int get_float(FAR setting_t *setting, FAR double *f)
{
assert(setting);
if ((setting->type != SETTING_FLOAT) &&
(setting->type != SETTING_INT))
{
return -EACCES;
}
if (setting->type == SETTING_FLOAT)
{
*f = setting->val.f;
}
else if (setting->type == SETTING_INT)
{
*f = (double)setting->val.i;
}
else
{
return -EINVAL;
}
return OK;
}
/****************************************************************************
* Name: set_float
*
* Description:
* Creates a setting for a float value
*
* Input Parameters:
* setting - pointer to the setting type
* f - the floating point value
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int set_float(FAR setting_t *setting, double f)
{
assert(setting);
if ((setting->type != SETTING_FLOAT) &&
(setting->type != SETTING_INT))
{
return -EACCES;
}
setting->type = SETTING_FLOAT;
setting->val.f = f;
return OK;
}
/****************************************************************************
* Name: get_ip
*
* Description:
* Creates a setting for an IP address
*
* Input Parameters:
* setting - pointer to the setting type
* ip - pointer to return the IP address
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int get_ip(FAR setting_t *setting, FAR struct in_addr *ip)
{
int ret;
assert(setting);
if ((setting->type != SETTING_IP_ADDR) &&
(setting->type != SETTING_STRING))
{
return -EACCES;
}
if (setting->type == SETTING_IP_ADDR)
{
memcpy(ip, &setting->val.ip, sizeof(struct in_addr));
ret = OK;
}
else if (setting->type == SETTING_STRING)
{
ret = inet_pton(AF_INET, setting->val.s, ip);
}
else
{
ret = -EINVAL;
}
return ret;
}
/****************************************************************************
* Name: set_ip
*
* Description:
* Creates a setting for an IP address
*
* Input Parameters:
* setting - pointer to the setting type
* ip - IP address (in network byte order)
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int set_ip(FAR setting_t *setting, FAR struct in_addr *ip)
{
assert(setting);
if ((setting->type != SETTING_IP_ADDR) &&
(setting->type != SETTING_STRING))
{
return -EACCES;
}
setting->type = SETTING_IP_ADDR;
memcpy(&setting->val.ip, ip, sizeof(struct in_addr));
return OK;
}
/****************************************************************************
* Name: load
*
* Description:
* Loads all values
*
* Input Parameters:
* none
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int load(void)
{
int i;
int ret;
int loadfailed = 0;
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAX_STORAGES; i++)
{
if ((g_settings.store[i].file[0] != '\0') &&
g_settings.store[i].load_fn)
{
ret = g_settings.store[i].load_fn(g_settings.store[i].file);
if (ret < 0)
{
loadfailed++;
}
}
}
if (loadfailed >= CONFIG_SYSTEM_SETTINGS_MAX_STORAGES)
{
/* ALL storages failed to load. We have a problem. */
return -ENODATA;
}
else
{
return OK;
}
}
/****************************************************************************
* Name: save
*
* Description:
* Saves cached values, either immediately or on a timer
*
* Input Parameters:
* none
*
* Returned Value:
* None
*
****************************************************************************/
static void save(void)
{
g_settings.wrpend = true;
#ifdef CONFIG_SYSTEM_SETTINGS_CACHED_SAVES
timer_settime(g_settings.timerid, 0, &g_settings.trigger, NULL);
#else
union sigval value =
{
.sival_ptr = &g_settings.wrpend,
};
dump_cache(value);
#endif
}
/****************************************************************************
* Name: signotify
*
* Description:
* Notify anything waiting for a signal
*
* Input Parameters:
* none
*
* Returned Value:
* none
*
****************************************************************************/
static void signotify(void)
{
int i;
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAX_SIGNALS; i++)
{
if (g_settings.notify[i].pid == 0)
{
break;
}
kill(g_settings.notify[i].pid, g_settings.notify[i].signo);
}
}
/****************************************************************************
* Name: dump_cache
*
* Description:
* Writes out the cached data to the appropriate storage
*
* Input Parameters:
* value - parameter passed if called from a timer signal
*
* Returned Value:
* None
*
****************************************************************************/
static void dump_cache(union sigval ptr)
{
int ret = OK;
FAR bool *wrpend = (bool *)ptr.sival_ptr;
int i;
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
assert(0);
}
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAX_STORAGES; i++)
{
if ((g_settings.store[i].file[0] != '\0') &&
g_settings.store[i].save_fn)
{
ret = g_settings.store[i].save_fn(g_settings.store[i].file);
#if 0
if (ret < 0)
{
/* What to do? We can't return anything from a void function.
*
* MIGHT BE A FUTURE REVISIT NEEDED
*/
}
#endif
}
}
*wrpend = false;
pthread_mutex_unlock(&g_settings.mtx);
}
/****************************************************************************
* Name: sanity_check
*
* Description:
* Checks that the string does not contain "illegal" characters
*
* Input Parameters:
* str - the string to check
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
static int sanity_check(FAR char *str)
{
#ifdef CONFIG_DEBUG_ASSERTIONS
assert(strchr(str, '=') == NULL);
assert(strchr(str, ';') == NULL);
assert(strchr(str, '\n') == NULL);
assert(strchr(str, '\r') == NULL);
#else
if ((strchr(str, '=') != NULL) || (strchr(str, ';') != NULL) ||
(strchr(str, '\n') != NULL) || (strchr(str, '\r') != NULL))
{
return -EINVAL;
}
#endif
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: settings_init
*
* Description:
* Initializes the settings storage.
*
* Input Parameters: none
*
* Returned Value: none
*
****************************************************************************/
void settings_init(void)
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
pthread_mutex_init(&g_settings.mtx, &attr);
memset(map, 0, sizeof(map));
memset(g_settings.store, 0, sizeof(g_settings.store));
memset(g_settings.notify, 0, sizeof(g_settings.notify));
#if defined(CONFIG_SYSTEM_SETTINGS_CACHED_SAVES)
memset(&g_settings.sev, 0, sizeof(struct sigevent));
g_settings.sev.sigev_notify = SIGEV_THREAD;
g_settings.sev.sigev_signo = TIMER_SIGNAL;
g_settings.sev.sigev_value.sival_ptr = &g_settings.wrpend;
g_settings.sev.sigev_notify_function = dump_cache;
memset(&g_settings.trigger, 0, sizeof(struct itimerspec));
g_settings.trigger.it_value.tv_sec =
CONFIG_SYSTEM_SETTINGS_CACHE_TIME_MS / 1000;
g_settings.trigger.it_value.tv_nsec =
(CONFIG_SYSTEM_SETTINGS_CACHE_TIME_MS % 1000) *
1000 * 1000;
timer_create(CLOCK_REALTIME, &g_settings.sev, &g_settings.timerid);
#endif
g_settings.initialized = true;
g_settings.hash = 0;
g_settings.wrpend = false;
}
/****************************************************************************
* Name: settings_setstorage
*
* Description:
* Sets a file to be used as a settings storage.
* Except from the first file, if loading the file causes any changes
* to the settings, then the new map will be dumped to all files
* (effectively it syncs all storages).
*
* Input Parameters:
* file - the filename of the storage to use
* type - the type of the storage (BINARY or TEXT)
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_setstorage(FAR char *file, enum storage_type_e type)
{
FAR storage_t *storage = NULL;
int ret = OK;
int idx = 0;
uint32_t h;
if (!g_settings.initialized)
{
assert(0);
}
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
while ((idx < CONFIG_SYSTEM_SETTINGS_MAX_STORAGES) &&
(g_settings.store[idx].file[0] != '\0'))
{
idx++;
}
assert(idx < CONFIG_SYSTEM_SETTINGS_MAX_STORAGES);
if (idx >= CONFIG_SYSTEM_SETTINGS_MAX_STORAGES)
{
ret = -ENOSPC;
goto errout;
}
assert(strlen(file) < CONFIG_SYSTEM_SETTINGS_MAX_FILENAME);
if (strlen(file) >= CONFIG_SYSTEM_SETTINGS_MAX_FILENAME)
{
ret = -EINVAL;
goto errout;
}
storage = &g_settings.store[idx];
strncpy(storage->file, file, sizeof(storage->file));
storage->file[sizeof(storage->file) - 1] = '\0';
switch (type)
{
case STORAGE_BINARY:
{
storage->load_fn = load_bin;
storage->save_fn = save_bin;
}
break;
case STORAGE_TEXT:
{
storage->load_fn = load_text;
storage->save_fn = save_text;
}
break;
default:
{
assert(0);
}
break;
}
ret = storage->load_fn(storage->file);
h = hash_calc();
/* Only save if there are more than 1 storages. */
if ((storage != &g_settings.store[0]) && ((h != g_settings.hash) ||
(access(file, F_OK) != 0)))
{
signotify();
save();
}
g_settings.hash = h;
errout:
pthread_mutex_unlock(&g_settings.mtx);
return ret;
}
/****************************************************************************
* Name: settings_sync
*
* Description:
* Synchronizes the storage.
*
* wait_dump - if cached saves are enabled, this determines whether
* the function will wait until the save is actually
* completed or just schedule a new save
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_sync(bool wait_dump)
{
int ret = OK;
uint32_t h;
if (!g_settings.initialized)
{
assert(0);
}
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
ret = load();
if (ret < 0) /* All storages failed to load */
{
goto done;
}
h = hash_calc();
if (h != g_settings.hash)
{
g_settings.hash = h;
signotify();
save();
}
done:
pthread_mutex_unlock(&g_settings.mtx);
#ifdef CONFIG_SYSTEM_SETTINGS_CACHED_SAVES
if ((wait_dump && (ret >= 0)))
{
while (g_settings.wrpend)
{
usleep(10 * 1000); /* Sleep for 10ms */
}
}
#endif
return ret;
}
/****************************************************************************
* Name: settings_notify
*
* Description:
* Registers a task to be notified on any change of the settings.
* Whenever any value is changed, a signal will be sent to all
* registered threads. Signals are NOT sent when new settings are
* created or when the whole storage is cleared.
*
* Input Parameters:
* none
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_notify(void)
{
int ret;
int idx = 0;
if (!g_settings.initialized)
{
assert(0);
}
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
while ((idx < CONFIG_SYSTEM_SETTINGS_MAX_SIGNALS) &&
g_settings.notify[idx].pid)
{
idx++;
}
assert(idx < CONFIG_SYSTEM_SETTINGS_MAX_SIGNALS);
if (idx >= CONFIG_SYSTEM_SETTINGS_MAX_SIGNALS)
{
ret = -EINVAL;
goto errout;
}
g_settings.notify[idx].pid = getpid();
g_settings.notify[idx].signo = CONFIG_SYSTEM_SETTINGS_SIGNO;
errout:
pthread_mutex_unlock(&g_settings.mtx);
return OK;
}
/****************************************************************************
* Name: settings_hash
*
* Description:
* Gets the hash of the settings storage.
*
* This hash represents the internal state of the settings map. A
* unique number is calculated based on the contents of the whole map.
*
* This hash can be used to check the settings for any alterations, i.e.
* any setting that may had its value changed since last check.
*
* Input Parameters:
* none
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_hash(FAR uint32_t *h)
{
if (!g_settings.initialized)
{
assert(0);
}
*h = g_settings.hash;
return OK;
}
/****************************************************************************
* Name: settings_clear
*
* Description:
* Clears all settings.
* Data in all storages are purged.
*
* Note that if the settings are cleared during the application run-time
* (i.e. not during initialization), every access to the settings storage
* will fail.
*
* All settings must be created again. This can be done either by calling
* Settings_create() again, or by restarting the application.
*
* Input Parameters:
* none
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_clear(void)
{
int ret;
if (!g_settings.initialized)
{
assert(0);
}
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
memset(map, 0, sizeof(map));
g_settings.hash = 0;
save();
pthread_mutex_unlock(&g_settings.mtx);
while (g_settings.wrpend)
{
usleep(10 * 1000); /* Sleep for 10ms */
}
return OK;
}
/****************************************************************************
* Name: settings_create
*
* Description:
* Creates a new setting.
*
* If the setting is found to exist in any of the storages, it will
* be loaded. Else, it will be created and the default value will be
* assigned.
*
* Input Parameters:
* key - the key of the setting.
* type - the type of the setting.
* ... - the default value of the setting.
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_create(FAR char *key, enum settings_type_e type, ...)
{
int ret = OK;
FAR setting_t *setting = NULL;
int j;
if (!g_settings.initialized)
{
assert(0);
}
assert(type != SETTING_EMPTY);
assert(strlen(key));
if (strlen(key) == 0)
{
return -EINVAL;
}
assert(strlen(key) < CONFIG_SYSTEM_SETTINGS_KEY_SIZE);
if (strlen(key) >= CONFIG_SYSTEM_SETTINGS_KEY_SIZE)
{
return -EINVAL;
}
assert(isalpha(key[0]) && (sanity_check(key) == OK));
if (!isalpha(key[0]) || (sanity_check(key) < 0))
{
return -EINVAL;
}
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
for (j = 0; j < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; j++)
{
if (strcmp(key, map[j].key) == 0)
{
setting = &map[j];
/* We found a setting with this key name */
goto errout;
}
if (map[j].type == SETTING_EMPTY)
{
setting = &map[j];
strncpy(setting->key, key, CONFIG_SYSTEM_SETTINGS_KEY_SIZE);
setting->key[CONFIG_SYSTEM_SETTINGS_KEY_SIZE - 1] = '\0';
/* This setting is empty/unused - we can use it */
break;
}
}
assert(setting);
if (setting == NULL)
{
goto errout;
}
if ((setting->type == SETTING_EMPTY) ||
(setting->type != type))
{
bool set_val = false;
va_list ap;
va_start(ap, type);
switch (type)
{
case SETTING_STRING:
{
FAR char *str = va_arg(ap, FAR char *);
if ((setting->type == SETTING_STRING) ||
(setting->type == SETTING_IP_ADDR))
{
break;
}
set_val = true;
setting->type = SETTING_STRING;
ret = set_string(setting, str);
}
break;
case SETTING_INT:
{
int i = va_arg(ap, int);
if ((setting->type == SETTING_INT) ||
(setting->type == SETTING_BOOL) ||
(setting->type == SETTING_FLOAT))
{
break;
}
set_val = true;
setting->type = SETTING_INT;
ret = set_int(setting, i);
}
break;
case SETTING_BOOL:
{
int i = va_arg(ap, int);
if ((setting->type == SETTING_BOOL) ||
(setting->type == SETTING_INT))
{
break;
}
set_val = true;
setting->type = SETTING_BOOL;
ret = set_bool(setting, i);
}
break;
case SETTING_FLOAT:
{
double f = va_arg(ap, double);
if ((setting->type == SETTING_FLOAT) ||
(setting->type == SETTING_INT))
{
break;
}
set_val = true;
setting->type = SETTING_FLOAT;
ret = set_float(setting, f);
}
break;
case SETTING_IP_ADDR:
{
FAR struct in_addr *ip = va_arg(ap, FAR struct in_addr *);
if ((setting->type == SETTING_IP_ADDR) ||
(setting->type == SETTING_STRING))
{
break;
}
set_val = true;
setting->type = SETTING_IP_ADDR;
ret = set_ip(setting, ip);
}
break;
default:
case SETTING_EMPTY:
{
assert(0);
ret = -EINVAL;
}
break;
}
va_end(ap);
if ((ret < 0) || !set_val)
{
memset(setting, 0, sizeof(setting_t));
setting = NULL;
}
else
{
g_settings.hash = hash_calc();
save();
}
}
errout:
pthread_mutex_unlock(&g_settings.mtx);
return ret;
}
/****************************************************************************
* Name: settings_type
*
* Description:
* Gets the type of a setting.
*
* Input Parameters:
* key - the key of the setting.
* type - pointer to int for the returned setting type
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_type(FAR char *key, FAR enum settings_type_e *stype)
{
int ret;
FAR setting_t *setting;
if (!g_settings.initialized)
{
assert(0);
}
assert(stype != NULL);
assert(key != NULL);
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
ret = get_setting(key, &setting);
if (setting)
{
*stype = setting->type;
}
pthread_mutex_unlock(&g_settings.mtx);
return ret;
}
/****************************************************************************
* Name: settings_get
*
* Description:
* Gets the value of a setting.
*
* Input Parameters:
* key - the key of the setting.
* type - the type of the setting
* ... - pointer to store the setting value plus, if a string
* setting, the length of the string to get
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_get(FAR char *key, enum settings_type_e type, ...)
{
int ret;
FAR setting_t *setting;
if (!g_settings.initialized)
{
assert(0);
}
assert(type != SETTING_EMPTY);
assert(key[0] != '\0');
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
ret = get_setting(key, &setting);
if (ret < 0)
{
goto errout;
}
va_list ap;
va_start(ap, type);
switch (type)
{
case SETTING_STRING:
{
FAR char *buf = va_arg(ap, FAR char *);
size_t len = va_arg(ap, size_t);
ret = (int)get_string(setting, buf, len);
}
break;
case SETTING_INT:
{
FAR int *i = va_arg(ap, FAR int *);
ret = get_int(setting, i);
}
break;
case SETTING_BOOL:
{
FAR int *i = va_arg(ap, FAR int *);
ret = get_bool(setting, i);
}
break;
case SETTING_FLOAT:
{
FAR double *f = va_arg(ap, FAR double *);
ret = get_float(setting, f);
}
break;
case SETTING_IP_ADDR:
{
FAR struct in_addr *ip = va_arg(ap, FAR struct in_addr *);
ret = get_ip(setting, ip);
}
break;
default:
{
assert(0);
}
break;
}
va_end(ap);
errout:
pthread_mutex_unlock(&g_settings.mtx);
return ret;
}
/****************************************************************************
* Name: settings_set
*
* Description:
* Sets the value of a setting. The type can be changed, within limits,
* here too.
*
* Input Parameters:
* key - the key of the setting.
* type - the type of the setting
* ... - the new value of the setting.
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_set(FAR char *key, enum settings_type_e type, ...)
{
int ret;
FAR setting_t *setting;
uint32_t h;
if (!g_settings.initialized)
{
assert(0);
}
assert(type != SETTING_EMPTY);
assert(key[0] != '\0');
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
ret = get_setting(key, &setting);
if (ret < 0)
{
goto errout;
}
va_list ap;
va_start(ap, type);
switch (type)
{
case SETTING_STRING:
{
FAR char *str = va_arg(ap, FAR char *);
ret = set_string(setting, str);
}
break;
case SETTING_INT:
{
int i = va_arg(ap, int);
ret = set_int(setting, i);
}
break;
case SETTING_BOOL:
{
int i = va_arg(ap, int);
ret = set_bool(setting, i);
}
break;
case SETTING_FLOAT:
{
double f = va_arg(ap, double);
ret = set_float(setting, f);
}
break;
case SETTING_IP_ADDR:
{
FAR struct in_addr *ip = va_arg(ap, FAR struct in_addr *);
ret = set_ip(setting, ip);
}
break;
default:
{
assert(0);
}
break;
}
va_end(ap);
if (ret >= 0)
{
h = hash_calc();
if (h != g_settings.hash)
{
g_settings.hash = h;
signotify();
save();
}
}
errout:
pthread_mutex_unlock(&g_settings.mtx);
return ret;
}
/****************************************************************************
* Name: settings_iterate
*
* Description:
* Gets a copy of a setting at the specified position. It can be used to
* iterate over the settings map, by using successive values of idx.
*
* Input Parameters:
* idx - the iteration index for the setting.
* setting - pointer to return the setting value
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_iterate(int idx, FAR setting_t *setting)
{
int ret;
if (!g_settings.initialized)
{
assert(0);
}
assert(setting);
if ((idx < 0) || (idx >= CONFIG_SYSTEM_SETTINGS_MAP_SIZE))
{
memset(setting, 0, sizeof(setting_t));
return -EINVAL;
}
ret = pthread_mutex_lock(&g_settings.mtx);
if (ret < 0)
{
return ret;
}
memcpy(setting, &map[idx], sizeof(setting_t));
if (map[idx].type == SETTING_EMPTY)
{
ret = -ENOENT;
}
pthread_mutex_unlock(&g_settings.mtx);
return ret;
}