diff --git a/MyApps b/MyApps new file mode 120000 index 00000000000..f3e324b931d --- /dev/null +++ b/MyApps @@ -0,0 +1 @@ +../MyApps \ No newline at end of file diff --git a/examples/settings/Kconfig b/examples/settings/Kconfig new file mode 100644 index 00000000000..81bf1e93d31 --- /dev/null +++ b/examples/settings/Kconfig @@ -0,0 +1,61 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_SETTINGS + tristate "Settings example" + default n + ---help--- + Enable the "settings" example. This uses the functions provided in + system/settings to test the storage and retrieval of various + configurable parameters + +if EXAMPLES_SETTINGS + +config EXAMPLES_SETTINGS_PROGNAME + string "Program name" + default "settings" + ---help--- + This is the name of the program that will be used when the NSH ELF + program is installed. + +config EXAMPLES_SETTINGS_PRIORITY + int "settings task priority" + default 100 + +config EXAMPLES_SETTINGS_STACKSIZE + int "settings stack size" + default DEFAULT_TASK_STACKSIZE + +choice + prompt "Select settings storage location" + default EXAMPLES_SETTINGS_USE_TMPFS + +config EXAMPLES_SETTINGS_USE_TMPFS + bool "Use TMPFS" + select FS_TMPFS + ---help--- + TMPFS will be enabled and used + +config EXAMPLES_SETTINGS_USE_OTHER + bool "Use existing/other storage media" + +endchoice # Select settings storage location + +if EXAMPLES_SETTINGS_USE_OTHER + +config EXAMPLES_SETTINGS_EXISTING_STORAGE + string "Path to existing storage media" + default "/mnt/mmcsd0" + +endif # EXAMPLES_SETTINGS_USE_OTHER + +config EXAMPLES_SETTINGS_FILENAME + string "Filename" + default "settings" + ---help--- + filename to be used. This append will append .txt and .bin + to this name to use/create two settings files. + +endif # EXAMPLES_SETTINGS diff --git a/examples/settings/Make.defs b/examples/settings/Make.defs new file mode 100644 index 00000000000..8198e28a302 --- /dev/null +++ b/examples/settings/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/examples/settings/Make.defs +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_SETTINGS),) +CONFIGURED_APPS += $(APPDIR)/examples/settings +endif diff --git a/examples/settings/Makefile b/examples/settings/Makefile new file mode 100644 index 00000000000..53bf1f22d92 --- /dev/null +++ b/examples/settings/Makefile @@ -0,0 +1,34 @@ +############################################################################ +# apps/examples/settings/Makefile +# +# 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# settings built-in application info + +PROGNAME = $(CONFIG_EXAMPLES_SETTINGS_PROGNAME) +PRIORITY = $(CONFIG_EXAMPLES_SETTINGS_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_SETTINGS_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_SETTINGS) + +# Settings Example + +MAINSRC = settings_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/settings/settings_main.c b/examples/settings/settings_main.c new file mode 100644 index 00000000000..9312608360f --- /dev/null +++ b/examples/settings/settings_main.c @@ -0,0 +1,301 @@ +/**************************************************************************** + * apps/examples/settings/settings_main.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 "system/settings.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * settings_main + ****************************************************************************/ + +int settings_main(int argc, FAR char *argv[]) +{ + int ret; + int fd; + enum settings_type_e stype; + char text_path[CONFIG_PATH_MAX]; + char bin_path[CONFIG_PATH_MAX]; + const char teststr[] = "I'm a string"; + int testval = 0x5aa5; + char readstr[CONFIG_SYSTEM_SETTINGS_VALUE_SIZE]; +#ifdef CONFIG_EXAMPLES_SETTINGS_USE_TMPFS + struct stat sbuf; + +# ifndef CONFIG_FS_TMPFS +# error TMPFS must be enabled +# endif +# ifndef CONFIG_LIBC_TMPDIR +# error LIBC_TMPDIR must be defined +# endif + if (stat(CONFIG_LIBC_TMPDIR, &sbuf)) + { + ret = nx_mount(NULL, CONFIG_LIBC_TMPDIR, "tmpfs", 0, NULL); + if (ret < 0) + { + printf("ERROR: Failed to mount tmpfs at %s: %d\n", + CONFIG_LIBC_TMPDIR, ret); + goto end; + } + } + + strcpy(bin_path, CONFIG_LIBC_TMPDIR); +#else + strcpy(bin_path, CONFIG_EXAMPLES_SETTINGS_EXISTING_STORAGE); + if (bin_path == NULL) + { + printf("Settings filepath is empty!"); + goto end; + } +#endif + + strcat(bin_path, "/"); + strcat(bin_path, CONFIG_EXAMPLES_SETTINGS_FILENAME); + strcpy(text_path, bin_path); + strcat(bin_path, ".bin"); + strcat(text_path, ".txt"); + + printf("Example of settings usage to file: %s and %s:", + bin_path, text_path); + printf("--------------------------------------------------------------\n"); + + settings_init(); + + ret = settings_setstorage(bin_path, STORAGE_BINARY); + if (ret == -ENOENT) + { + printf("No existing binary storage file found. Creating it.\n"); + fd = open(bin_path, O_CREAT); + if (fd < 0) + { + printf("Failed to create settings file\n"); + goto end; + } + + close(fd); + } + else if (ret < 0) + { + printf("settings setstorage failed: %d\n", ret); + goto end; + } + else + { + printf("existing bin settings storage file found\n"); + } + + ret = settings_setstorage(text_path, STORAGE_TEXT); + if (ret == -ENOENT) + { + printf("No existing text storage file found. Creating it.\n"); + fd = open(text_path, O_CREAT); + if (fd < 0) + { + printf("Failed to create settings file\n"); + goto end; + } + + close(fd); + } + else if (ret < 0) + { + printf("settings setstorage failed: %d\n", ret); + goto end; + } + else + { + printf("existing text settings storage file found\n"); + } + + ret = settings_create("v1", SETTING_STRING, "default value"); + if (ret < 0) + { + printf("settings create failed: %d\n", ret); + goto end; + } + + /* if this app has been run before, the setting type is likely changed */ + + ret = settings_type("v1", &stype); + if (ret < 0) + { + printf("Failed to get settings type: %d\n", ret); + goto end; + } + + if (stype == SETTING_STRING) + { + ret = settings_get("v1", SETTING_STRING, &readstr, sizeof(readstr)); + if (ret < 0) + { + printf("settings retrieve failed: %d\n", ret); + goto end; + } + + printf("Retrieved settings value (v1) with value:%s\n", readstr); + } + else + { + ret = settings_get("v1", SETTING_INT, &testval, + CONFIG_SYSTEM_SETTINGS_VALUE_SIZE); + if (ret < 0) + { + printf("settings retrieve failed: %d\n", ret); + goto end; + } + + printf("Retrieved settings value (v1) with value:%d\n", testval); + } + + printf("Trying to (re)create a setting that already exists (v1)\n"); + + testval = 0xa5a5; + ret = settings_create("v1", SETTING_INT, testval); + if (ret == -EACCES) + { + printf("Deliberate fail: setting exists! Error: %d\n", ret); + } + else if (ret < 0) + { + printf("settings create failed: %d\n", ret); + goto end; + } + + ret = settings_type("v1", &stype); + if (ret < 0) + { + printf("failed to read settings type: %d\n", ret); + goto end; + } + + printf("Retrieved setting type is: %d\n", stype); + + printf("Trying to change setting (v1) to integer type\n"); + ret = settings_set("v1", SETTING_INT, &testval); + if (ret < 0) + { + printf("Deliberate fail: settings change invalid: %d\n", ret); + } + + ret = settings_get("v2", SETTING_INT, &testval); + if (ret < 0) + { + printf("Deliberate fail: non-existent setting requested. Error:%d\n", + ret); + } + + printf("Trying to change setting (v1) from int to string: %s\n", teststr); + ret = settings_set("v1", SETTING_STRING, &teststr); + if (ret < 0) + { + printf("Deliberate fail: settings change invalid: %d\n", ret); + } + + printf("Creating a string settings value (s1):%s\n", teststr); + ret = settings_create("s1", SETTING_STRING, teststr); + if (ret < 0) + { + printf("settings changed failed: %d\n", ret); + goto end; + } + + ret = settings_get("s1", SETTING_STRING, &readstr, sizeof(readstr)); + if (ret < 0) + { + printf("settings retrieve failed: %d\n", ret); + goto end; + } + + printf("Retrieved string settings value (s1) with value:%s\n", + readstr); + + FAR struct in_addr save_ip; + FAR struct in_addr load_ip; + save_ip.s_addr = 0xc0a86401; + + printf("Changing setting to an IP value (s1) with value:%d.%d.%d.%d\n", + (int)(save_ip.s_addr >> 24) & 0xff, + (int)(save_ip.s_addr >> 16) & 0xff, + (int)(save_ip.s_addr >> 8) & 0xff, + (int)(save_ip.s_addr >> 0) & 0xff); + ret = settings_set("s1", SETTING_IP_ADDR, &save_ip); + if (ret < 0) + { + printf("IP address settings create failed: %d\n", ret); + goto end; + } + + ret = settings_get("s1", SETTING_IP_ADDR, &load_ip); + if (ret < 0) + { + printf("IP address settings retrieve failed: %d\n", ret); + goto end; + } + + printf("Retrieved IP address settings value (s1) with value:0x%08lx\n", + load_ip.s_addr); + + printf("syncing storages\n"); + ret = settings_sync(true); /* wait for cached saves to complete + * (will only be done if caching enabled). + */ + if (ret < 0) + { + printf("Failed to sync storages: %d\n", ret); + goto end; + } + +end: + printf("exiting settings example app\n"); + fflush(stdout); + + return ret; +} diff --git a/include/system/settings.h b/include/system/settings.h new file mode 100644 index 00000000000..e4f7eddb6f0 --- /dev/null +++ b/include/system/settings.h @@ -0,0 +1,306 @@ +/**************************************************************************** + * apps/include/system/settings.h + * + * 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. + * + ****************************************************************************/ + +#ifndef UTILS_SETTINGS_H_ +#define UTILS_SETTINGS_H_ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define VALID 0x600d /* "Magic" number to confirm storage is OK */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +enum settings_type_e +{ + SETTING_EMPTY = 0, + SETTING_INT, /* INT32 */ + SETTING_BOOL, + SETTING_FLOAT, + SETTING_STRING, + SETTING_IP_ADDR, +}; + +typedef struct +{ + char key[CONFIG_SYSTEM_SETTINGS_KEY_SIZE]; + enum settings_type_e type; + union + { + int i; + double f; + char s[CONFIG_SYSTEM_SETTINGS_VALUE_SIZE]; + struct in_addr ip; + } val; +} setting_t; + +typedef struct +{ + char file[CONFIG_SYSTEM_SETTINGS_MAX_FILENAME]; + + int (*load_fn)(FAR char *file); + int (*save_fn)(FAR char *file); +} storage_t; + +struct notify_s +{ + pid_t pid; + uint8_t signo; +}; + +enum storage_type_e +{ + STORAGE_BINARY = 0, + STORAGE_TEXT, +}; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: settings_init + * + * Description: + * Initializes the settings storage. + * + * Input Parameters: none + * + * Returned Value: none + * + ****************************************************************************/ + +void settings_init(void); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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, ...); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * 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. + * + * Returned Value: + * Success or negated failure code + * + ****************************************************************************/ + +int settings_get(FAR char *key, FAR enum settings_type_e type, ...); + +/**************************************************************************** + * Name: settings_set + * + * Description: + * Sets the value of a setting. + * + * 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, FAR enum settings_type_e type, ...); + +/**************************************************************************** + * 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); + +#endif /* UTILS_SETTINGS_H_ */ + diff --git a/system/settings/Kconfig b/system/settings/Kconfig new file mode 100644 index 00000000000..351897ce9e4 --- /dev/null +++ b/system/settings/Kconfig @@ -0,0 +1,85 @@ +# +# Settings Storage Configuration +# + +config SYSTEM_SETTINGS + bool "Settings Functions" + default n + ---help--- + The settings storage can be used to store and retrieve various + configurable parameters. This storage is a RAM-based map (for fast access) + but one or more files can be used too (so values can persist across system + reboots). + +if SYSTEM_SETTINGS + +config SYSTEM_SETTINGS_MAP_SIZE + int "Settings storage size" + default 20 + ---help--- + The size of the settings storage, i.e. the + maximum number of settings that can be created. + +config SYSTEM_SETTINGS_KEY_SIZE + int "Settings key size" + default 20 + ---help--- + Maximum size of a setting key. + +config SYSTEM_SETTINGS_VALUE_SIZE + int "Settings value size" + default 20 + ---help--- + Maximum size of a string setting value. + +config SYSTEM_SETTINGS_MAX_STORAGES + int "Settings files" + default 2 + ---help--- + Maximum number of supported settings files. + +config SYSTEM_SETTINGS_MAX_FILENAME + int "Settings filename size" + default 50 + ---help--- + Maximum size of settings filename. + +config SYSTEM_SETTINGS_CACHED_SAVES + bool "Cache save operations" + default y + ---help--- + If enabled saving to the storages will be cached. + + Whenever a save is triggered, the settings will only + be saved in the RAM map (and will be immediately + effective). Writes to the storages though will + happen after a small delay. + + This improves performance greatly when multiple + successive saves are to be performed, as the actual + write will happen only once, asynchronously. + +if SYSTEM_SETTINGS_CACHED_SAVES + +config SYSTEM_SETTINGS_CACHE_TIME_MS + int "Cache Delay (ms)" + default 100 + ---help--- + Sets the delay after a setting is changed before they are written +endif # SYSTEM_SETTINGS_CACHED_SAVES + +config SYSTEM_SETTINGS_MAX_SIGNALS + int "Max. settings signals" + default 2 + ---help--- + Maximum number of signals that can be registered + on the settings storage. + +config SYSTEM_SETTINGS_SIGNO + int "Signal Number to use" + default 32 + range 32 63 + ---help--- + The Signal number to use for notifies + +endif # SYSTEM_SETTINGS diff --git a/system/settings/Make.defs b/system/settings/Make.defs new file mode 100644 index 00000000000..9092af73f9e --- /dev/null +++ b/system/settings/Make.defs @@ -0,0 +1,24 @@ +############################################################################ +# apps/system/settings/Make.defs +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_SYSTEM_SETTINGS),) +CONFIGURED_APPS += $(APPDIR)/system/settings +endif + diff --git a/system/settings/Makefile b/system/settings/Makefile new file mode 100644 index 00000000000..318b2278dcf --- /dev/null +++ b/system/settings/Makefile @@ -0,0 +1,28 @@ +############################################################################ +# apps/system/settings/Makefile +# +# 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +ifneq ($CONFIG_SYSTEM_UTILS_SETTINGS,) +CSRCS += settings.c storage_bin.c storage_text.c +endif + +include $(APPDIR)/Application.mk + diff --git a/system/settings/readme.md b/system/settings/readme.md new file mode 100644 index 00000000000..4016adff69c --- /dev/null +++ b/system/settings/readme.md @@ -0,0 +1,94 @@ +# Introduction + +The settings storage can be used to store and retrieve various configurable parameters. This storage is a RAM-based map (for fast access), but one or more files can be used too (so values can persist across system reboots). + +This is a thread-safe implementation. Different threads may access the settings simultaneously. + +# Setting Definition + +Each setting is a key/value pair. + +The key is always a string, while there are many value types supported. + +## Strings + +All strings, whether they represent a key or a string value, must use a specific format. They must be ASCII, NULL-terminated strings, and they are always case-sensitive. They must obey to the following rules: + +- they cannot start with a number. +- they cannot contain the characters '=', ';'. +- they cannot contain the characters '\n', '\r'. + +## Setting Type + +Since each setting has its own type, it is the user's responsibility to access the setting using the correct type. Some "casts" are possible (e.g. between bool and int), but generally reading with the wrong type will lead to failure. + +The following types are currently supported. + +- String. +- int +- bool +- ip address +- float + +### Setting Storage Size + +Kconfig is used to determine the size of the various fields used: + +- CONFIG_SYSTEM_SETTINGS_MAP_SIZE     the total number settings allowed +- CONFIG_SYSTEM_SETTINGS_MAX_STORAGES the number of storage files that can be used +- CONFIG_SYSTEM_SETTINGS_VALUE_SIZE   the storage size of a STRING value +- CONFIG_SYSTEM_SETTINGS_KEY_SIZE     the size of the KEY field +- CONFIG_SYSTEM_SETTINGS_MAX_FILENAME the maximum filename size + +# Signal + +A POSIX signal can be chosen via Kconfig and is used to send notifications when a setting has been changed. is used to determine the maximum number of signals that can be registered for the settings storage functions. + +# Files + +There are also various types of files that can be used. Each file type is using a different data format to parse the stored data. Every type is expected to be best suited for a specific file system or medium type, or be better performing while used with external systems. In any case, all configured files are automatically synchronized whenever a value changes. + +## Cached Saves + +It is possible to enable cached saves, via Kconfig, along with the time (in ms) that should elapse after the last storage value was updated before a write to the file or files should be initiated. + +## Storage Types +The current storage types are defined in and are as follows. + +### STORAGE_BINARY + +Data is stored as "raw" binary. Bool, int and float data are stored using the maximum number of bytes needed, as determined by the size of the union used for these settings; this will be architecture dependent. + +### STORAGE_TEXT + +All data is converted to ASCII characters making the storage easily human-readable. + +# Usage + +## Most common + +This is the usual sequence of calls to use the settings utility. + +1. Call settings_init(); +2. Call settings_setstorage("path", storage_type); for every/any file storage in use + - for example settings_setstorage("/dev/eeprom", STORAGE_EPROM); +3. Call settings_create(key_name, settings_type, default value) for every setting required +4. settings_notify(). + - for example settings_create("KEY1", SETTINGS_STRING, "Hello"); +5. If a settings value needs to be read, call the variadic function settings_get(key_name, settings_type, &variable, size);. "size" is only needed for STRING types. + - for example settings_get("KEY1", SETTINGS_STRING, &read_str, sizeof(readstr)); +6. If a settings value needs to be changed, call the variadic function settings_set(key_name, settings_type, &value, size); "size" is only needed for STRING types. + - for example settings_set("KEY1", SETTINGS_STRING, &new_string); + +## Other functions +Other functions exist to assist the user program. +1. settings_sync(wait_dump). This will synchronise the settings between file storages if more than 1 are in use. if cached saves are enabled, the wait_dump parameter, if true, will force the sync function to wait until any saves are actually written out. +2. settings_iterate(idx, &setting). This gets a copy of a setting at the specified position. It can be used to iterate over the entire settings map, by using successive values of idx. +3. settings_type(key_name, &type). Gets the type of a given setting. +4. settings_clear(). Clears all settings and sata in all storages is purged. +5. settings_hash(&hash). 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. +## Error codes +The settings functions provide negated error return codes that can be used as required by the user application to deal with unexpected behaviour. + +## Example App +There is an example app available to demonstrate the usage of many of the settings features CONFIG_EXAMPLES_SETTINGS. diff --git a/system/settings/settings.c b/system/settings/settings.c new file mode 100644 index 00000000000..78eeac5ee8c --- /dev/null +++ b/system/settings/settings.c @@ -0,0 +1,1598 @@ +/**************************************************************************** + * 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; +} diff --git a/system/settings/storage.h b/system/settings/storage.h new file mode 100644 index 00000000000..dd6672baa15 --- /dev/null +++ b/system/settings/storage.h @@ -0,0 +1,63 @@ +/**************************************************************************** + * apps/system/settings/storage.h + * + * 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. + * + ****************************************************************************/ + +#ifndef SETTINGS_STORAGE_H_ +#define SETTINGS_STORAGE_H_ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "system/settings.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/* Text storage. */ + +int load_text(FAR char *file); +int save_text(FAR char *file); + +/* Binary storage. */ + +int load_bin(FAR char *file); +int save_bin(FAR char *file); + +/* EEPROM storage. */ + +int load_eeprom(FAR char *file); +int save_eeprom(FAR char *file); + +#endif /* SETTINGS_STORAGE_H_*/ + diff --git a/system/settings/storage_bin.c b/system/settings/storage_bin.c new file mode 100644 index 00000000000..6c6ed6777cf --- /dev/null +++ b/system/settings/storage_bin.c @@ -0,0 +1,285 @@ +/**************************************************************************** + * apps/system/settings/storage_bin.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 "system/settings.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BUFFER_SIZE 256 /* Note alignment for Flash writes! */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: getsetting + * + * Description: + * Gets the setting information from a given key. + * + * Input Parameters: + * key - key of the required setting + * + * Returned Value: + * The setting + * + ****************************************************************************/ + +FAR static setting_t *getsetting(char *key); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern setting_t map[CONFIG_SYSTEM_SETTINGS_MAP_SIZE]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: getsetting + * + * Description: + * Gets the setting information from a given key. + * + * Input Parameters: + * key - key of the required setting + * + * Returned Value: + * The setting + * + ****************************************************************************/ + +FAR setting_t *getsetting(FAR char *key) +{ + int i; + + for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++) + { + FAR setting_t *setting = &map[i]; + + if (strcmp(key, setting->key) == 0) + { + return setting; + } + + if (setting->type == SETTING_EMPTY) + { + return setting; + } + } + + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: load_bin + * + * Description: + * Loads binary data from a storage file. + * + * Input Parameters: + * file - the filename of the storage to use + * + * Returned Value: + * Success or negated failure code + * + ****************************************************************************/ + +int load_bin(FAR char *file) +{ + int fd; + int i; + int ret = OK; + uint16_t valid; + uint16_t count; + uint32_t calc_crc = 0; + uint32_t exp_crc = 0; + setting_t setting; + FAR setting_t *slot; + + fd = open(file, O_RDONLY); + if (fd < 0) + { + return -ENOENT; + } + + valid = 0; + read(fd, &valid, sizeof(uint16_t)); + + if (valid != VALID) + { + ret = -EBADMSG; + goto abort; /* Just exit - the settings aren't valid */ + } + + count = 0; + read(fd, &count, sizeof(uint16_t)); + + for (i = 0; i < count; i++) + { + read(fd, &setting, sizeof(setting_t)); + calc_crc = crc32part((FAR uint8_t *)&setting, sizeof(setting_t), + calc_crc); + } + + read(fd, &exp_crc, sizeof(uint32_t)); + + if (calc_crc != exp_crc) + { + ret = -EBADMSG; + goto abort; + } + + lseek(fd, (sizeof(uint16_t) * 2), SEEK_SET); /* Get after valid & size */ + + for (i = 0; i < count; i++) + { + read(fd, &setting, sizeof(setting_t)); + + slot = getsetting(setting.key); + if (slot == NULL) + { + continue; + } + + memcpy(slot, &setting, sizeof(setting_t)); + } + +abort: + close(fd); + return ret; +} + +/**************************************************************************** + * Name: save_bin + * + * Description: + * Saves binary data to a storage file. + * + * Input Parameters: + * file - the filename of the storage to use + * + * Returned Value: + * Success or negated failure code + * + ****************************************************************************/ + +int save_bin(FAR char *file) +{ + int count; + int fd; + int ret = OK; + off_t offset = sizeof(uint16_t) * 2; /* Valid & count */ + size_t data_size; + size_t rem_data; + uint32_t crc; + size_t rem_crc; + FAR uint8_t *buffer = malloc(BUFFER_SIZE); + + if (buffer == NULL) + { + return -ENOMEM; + } + + count = 0; + int i; + for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++) + { + if (map[i].type == SETTING_EMPTY) + { + break; + } + + count++; + } + + fd = open(file, (O_RDWR | O_TRUNC), 0666); + if (fd < 0) + { + ret = -ENODEV; + goto abort; + } + + memset(buffer, 0xff, BUFFER_SIZE); + *((FAR uint16_t *)buffer) = VALID; + *(((FAR uint16_t *)buffer) + 1) = count; + + data_size = count *sizeof(setting_t); + rem_data = data_size; + crc = crc32((FAR uint8_t *)map, data_size); + rem_crc = sizeof(crc); + + while ((offset + rem_data + rem_crc) > 0) + { + size_t to_write = ((offset + rem_data) > BUFFER_SIZE) ? + (size_t)(BUFFER_SIZE - offset) : rem_data; + memcpy((buffer + offset), (((FAR uint8_t *)map) + + (data_size - rem_data)), to_write); + + size_t j; + for (j = (to_write + offset); + (j < BUFFER_SIZE) && (rem_crc > 0); j++, rem_crc--) + { + off_t crc_byte = (off_t)(sizeof(crc) - rem_crc); + buffer[j] = (crc >> (8 * crc_byte)) & 0xff; + } + + write(fd, buffer, BUFFER_SIZE); + rem_data -= to_write; + offset = 0; + } + + close(fd); + +abort: + free(buffer); + + return ret; +} + diff --git a/system/settings/storage_text.c b/system/settings/storage_text.c new file mode 100644 index 00000000000..d1d4f85fe4e --- /dev/null +++ b/system/settings/storage_text.c @@ -0,0 +1,425 @@ +/**************************************************************************** + * apps/system/settings/storage_text.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 "system/settings.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define BUFFER_SIZE 256 /* Note alignment for Flash writes! */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: getsetting + * + * Description: + * Gets the setting information from a given key. + * + * Input Parameters: + * key - key of the required setting + * + * Returned Value: + * The setting + * + ****************************************************************************/ + +FAR static setting_t *getsetting(char *key); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +extern setting_t map[CONFIG_SYSTEM_SETTINGS_MAP_SIZE]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: getsetting + * + * Description: + * Gets the setting information from a given key. + * + * Input Parameters: + * key - key of the required setting + * + * Returned Value: + * The setting + * + ****************************************************************************/ + +FAR setting_t *getsetting(char *key) +{ + int i; + FAR setting_t *setting; + + if (strlen(key) >= CONFIG_SYSTEM_SETTINGS_KEY_SIZE) + { + return NULL; + } + + for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++) + { + setting = &map[i]; + + if (strcmp(key, setting->key) == 0) + { + return setting; + } + + if (setting->type == SETTING_EMPTY) + { + strncpy(setting->key, key, CONFIG_SYSTEM_SETTINGS_KEY_SIZE); + setting->key[CONFIG_SYSTEM_SETTINGS_KEY_SIZE - 1] = '\0'; + return setting; + } + } + + return NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: load_text + * + * Description: + * Loads text from a storage file. + * + * Input Parameters: + * file - the filename of the storage to use + * + * Returned Value: + * Success or negated failure code + * + ****************************************************************************/ + +int load_text(FAR char *file) +{ + int ret = OK; + FAR FILE *f; + FAR char *eq; + FAR char *key; + FAR char *val; + FAR char *backup_file; + FAR char *buffer; + FAR setting_t *setting; + + /* Check that the file exists */ + + if (access(file, F_OK) != 0) + { + /* If not, try the backup file */ + + backup_file = malloc(strlen(file) + 2); + if (backup_file == NULL) + { + return -ENODEV; + } + + strcpy(backup_file, file); + strcat(backup_file, "~"); + + if (access(backup_file, F_OK) == 0) + { + /* There is a backup file + * Restore this as the new settings file + */ + + rename(backup_file, file); + } + + free(backup_file); + } + + f = fopen(file, "r"); + if (f == NULL) + { + return -ENOENT; + } + + buffer = malloc(BUFFER_SIZE); + if (buffer == NULL) + { + ret = -ENOMEM; + goto abort; + } + + while (fgets(buffer, BUFFER_SIZE, f)) + { + int i; + + /* Remove any line terminators */ + + for (i = ((int)strlen(buffer) - 1); i > 0; i--) + { + if (buffer[i] == ';' || buffer[i] == '\n' || buffer[i] == '\r') + { + buffer[i] = '\0'; + } + else + { + break; + } + } + + /* Separate the key / value pair */ + + eq = strchr(buffer, '='); + if (eq == NULL) + { + continue; + } + + key = buffer; + val = eq + 1; + *eq = '\0'; + + /* Check if the key is valid */ + + if (!isalpha(key[0])) + { + continue; + } + + /* Get the setting slot */ + + setting = getsetting(key); + if (setting == NULL) + { + continue; + } + + /* Parse the value */ + + if (isalpha(val[0])) + { + if (strcasecmp(val, "true") == 0) + { + /* It's a boolean */ + + setting->type = SETTING_BOOL; + setting->val.i = 1; + } + else if (strcasecmp(val, "false") == 0) + { + /* It's a boolean */ + + setting->type = SETTING_BOOL; + setting->val.i = 0; + } + else + { + /* It's a string */ + + if (strlen(val) >= CONFIG_SYSTEM_SETTINGS_VALUE_SIZE) + { + continue; + } + + setting->type = SETTING_STRING; + strncpy(setting->val.s, val, + CONFIG_SYSTEM_SETTINGS_VALUE_SIZE); + setting->val.s[CONFIG_SYSTEM_SETTINGS_VALUE_SIZE - 1] = '\0'; + } + } + else + { + if (strchr(val, '.') != NULL) + { + FAR char *s = val; + i = 0; + while (s[i]) s[i] == '.' ? i++ : *s++; + if (i == 1) + { + /* It's a float */ + + double d = 0; + if (sscanf(val, "%lf", &d) == 1) + { + setting->type = SETTING_FLOAT; + setting->val.f = d; + } + } + else if (i == 3) + { + /* It's an IP address */ + + setting->type = SETTING_IP_ADDR; + inet_pton(AF_INET, val, &setting->val.ip); + } + } + else + { + /* It's an integer */ + + i = 0; + if (sscanf(val, "%d", &i) == 1) + { + setting->type = SETTING_INT; + setting->val.i = i; + } + } + } + + /* Handle parse errors */ + + if (setting->type == SETTING_EMPTY) + { + memset(setting->key, 0, CONFIG_SYSTEM_SETTINGS_KEY_SIZE); + } + } + + free(buffer); + +abort: + fclose(f); + + return ret; +} + +/**************************************************************************** + * Name: save_text + * + * Description: + * Saves text to a storage file. + * + * Input Parameters: + * file - the filename of the storage to use + * + * Returned Value: + * Success or negated failure code + * + ****************************************************************************/ + +int save_text(FAR char *file) +{ + int ret = OK; + FAR char *backup_file = malloc(strlen(file) + 2); + FAR FILE *f; + int i; + + if (backup_file == NULL) + { + return -ENODEV; + } + + strcpy(backup_file, file); + strcat(backup_file, "~"); + + f = fopen(backup_file, "w"); + if (f == NULL) + { + ret = -ENODEV; + goto abort; + } + + for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++) + { + if (map[i].type == SETTING_EMPTY) + { + break; + } + + switch (map[i].type) + { + case SETTING_STRING: + { + fprintf(f, "%s=%s\n", map[i].key, + map[i].val.s); + } + break; + + case SETTING_INT: + { + fprintf(f, "%s=%d\n", map[i].key, + map[i].val.i); + } + break; + case SETTING_BOOL: + { + fprintf(f, "%s=%s\n", map[i].key, + map[i].val.i ? + "true" : "false"); + } + break; + + case SETTING_FLOAT: + { + fprintf(f, "%s=%.06f\n", map[i].key, + map[i].val.f); + } + break; + + case SETTING_IP_ADDR: + { + char buffer[20]; + inet_ntop(AF_INET, &map[i].val.ip, buffer, 20); + fprintf(f, "%s=%s\n", map[i].key, buffer); + } + break; + + default: + { + return -EINVAL; + } + break; + } + } + + fclose(f); + + remove(file); + rename(backup_file, file); + +abort: + free(backup_file); + + return ret; +}