/**************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 ret = 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; }