From 24d2b85782f8b8491b5b6d9a4e662905bd563f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20Battrel?= Date: Mon, 26 Jun 2023 07:50:39 +0200 Subject: [PATCH] Bluetooth: Host: Add API to clean BT settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new function `bt_settings_cleanup` that allow users to clean potential leftover present in the Bluetooth settings. The function will look for every settings key under the 'bt/' namespace and remove the one not listed in the `bt_settings_keys` list. The settings that are linked to an ID that no longer exist will also be deleted. Signed-off-by: Théo Battrel --- include/zephyr/bluetooth/settings.h | 31 +++++++ subsys/bluetooth/host/settings.c | 121 ++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 include/zephyr/bluetooth/settings.h diff --git a/include/zephyr/bluetooth/settings.h b/include/zephyr/bluetooth/settings.h new file mode 100644 index 000000000000000..f35c160d50406af --- /dev/null +++ b/include/zephyr/bluetooth/settings.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief This function act as a garbage collector for the Bluetooth settings. + * It will: + * - Remove not used settings key under 'bt/' namespace; + * - Remove possible leftover from unexpected behavior. + * + * The settings are removed directly from the storage, the user need to run @ref + * settings_load (or @ref settings_load_subtree to only load 'bt' namespace) to + * apply the changes. + * + * Example of possible leftover: An identity has been deleted, the corresponding + * address as been removed from 'bt/id' but before removing the corresponding + * key in 'bt/keys', the device has been powered off. Calling + * `bt_settings_cleanup` will ensure that the key is removed. + * + * @warning User must not call this function if they use the 'bt/' namespace to + * store settings. All settings stored under 'bt/' namespace that are not + * defined by the Bluetooth stack will be deleted. + * + * @note This function will ignore Bluetooth Mesh settings ('bt/mesh/' + * namespace). + * + * @return 0 on success, non-zero on failure + */ +int bt_settings_cleanup(void); diff --git a/subsys/bluetooth/host/settings.c b/subsys/bluetooth/host/settings.c index 1623645ae212303..2d35e178e6538c3 100644 --- a/subsys/bluetooth/host/settings.c +++ b/subsys/bluetooth/host/settings.c @@ -5,6 +5,8 @@ */ #include +#include +#include #include #include @@ -12,6 +14,7 @@ #include #include #include +#include #include "common/bt_str.h" @@ -298,6 +301,124 @@ int bt_settings_init(void) return 0; } +static const char * const bt_settings_keys[] = { + "sc", + "cf", + "ccc", + "hash", + "name", + "id", + "irk", + "link_key", + "keys", + "mesh", +}; + +static int bt_settings_load_direct_cleanup(const char *key, size_t len, settings_read_cb read_cb, + void *cb_arg, void *param) +{ + int err; + int next_len; + int subsys_len; + unsigned long id; + const char *next; + const char *id_str; + bool key_is_supported; + char full_key[BT_SETTINGS_KEY_MAX]; + + bt_addr_le_t *id_addr = (bt_addr_le_t *)param; + + subsys_len = settings_name_next(key, &next); + + LOG_DBG("subsys: %.*s", subsys_len, key); + + key_is_supported = false; + + for (size_t i = 0; i < ARRAY_SIZE(bt_settings_keys); i++) { + if (!strncmp(key, bt_settings_keys[i], strlen(bt_settings_keys[i]))) { + key_is_supported = true; + } + } + + if (key_is_supported) { + if (!next || !strncmp(key, "mesh", subsys_len)) { + /* key exist but does not need to be checked for + * leftover because it is unique and not linked to ID. + * We return and continue to look for next keys. + */ + return 0; + } + + next_len = settings_name_next(next, &id_str); + + if (!id_str) { + id = BT_ID_DEFAULT; + } else { + id = strtoul(id_str, NULL, 10); + + if (id >= CONFIG_BT_ID_MAX) { + LOG_ERR("Invalid local identity (%lu)", id); + + return 0; + } + } + + if (!bt_addr_le_eq(&id_addr[(uint8_t)id], BT_ADDR_LE_ANY)) { + /* ID has not been deleted */ + return 0; + } + } + + /* At this point, we know that the key need to be cleared */ + snprintk(full_key, sizeof(full_key), "bt/%s", key); + LOG_DBG("Key to delete: %s", full_key); + + err = settings_delete(full_key); + if (err) { + LOG_ERR("Failed to delete key '%s' (err %d)", full_key, err); + } + + return 0; +} + +static int bt_settings_load_direct_id(const char *key, size_t len, settings_read_cb read_cb, + void *cb_arg, void *param) +{ + ssize_t read_len; + int id_count; + bt_addr_le_t *id_addr = (bt_addr_le_t *)param; + + read_len = read_cb(cb_arg, id_addr, CONFIG_BT_ID_MAX * sizeof(id_addr[0])); + if (read_len <= 0) { + LOG_ERR("Failed to read data (err %d)", len); + } + + id_count = read_len / sizeof(id_addr[0]); + + /* there is only one 'bt/id' key, no need for further subtree searching */ + return 1; +} + +int bt_settings_cleanup(void) +{ + int err; + bt_addr_le_t id_addr[CONFIG_BT_ID_MAX] = {0}; + + err = settings_load_subtree_direct("bt/id", bt_settings_load_direct_id, (void *)id_addr); + if (err) { + LOG_DBG("Failed to load 'bt/id' subtree (err %d)", err); + return err; + } + + err = settings_load_subtree_direct("bt", bt_settings_load_direct_cleanup, &id_addr); + if (err) { + LOG_DBG("Failed to load 'bt' subtree (err %d)", err); + return err; + } + + return 0; +} + int bt_settings_store(const char *key, uint8_t id, const bt_addr_le_t *addr, const void *value, size_t val_len) {