1599 lines
36 KiB
C
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;
|
|
}
|