Skip to content

Commit

Permalink
Bluetooth: Host: Add API to clean BT settings
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
theob-pro committed Jul 4, 2023
1 parent abc5f4c commit 24d2b85
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 0 deletions.
31 changes: 31 additions & 0 deletions include/zephyr/bluetooth/settings.h
Original file line number Diff line number Diff line change
@@ -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);
121 changes: 121 additions & 0 deletions subsys/bluetooth/host/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
*/

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <zephyr/kernel.h>
#include <zephyr/settings/settings.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/settings.h>

#include "common/bt_str.h"

Expand Down Expand Up @@ -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)
{
Expand Down

0 comments on commit 24d2b85

Please sign in to comment.