bluez/tools/mesh-cfgclient.c
Luiz Augusto von Dentz ee6f3a837e shared/shell: Fix not handling prompt with color properly
Colors use escape sequence that needs to be enveloped with
RL_PROMPT_START_IGNORE (\001) and RL_PROMPT_END_IGNORE (\002) in order
for readline to properly calculate the prompt length.

Fixes: https://github.com/bluez/bluez/issues/965
2024-10-17 10:00:02 -04:00

2671 lines
64 KiB
C

// SPDX-License-Identifier: LGPL-2.1-or-later
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
*
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <dbus/dbus.h>
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
#include <ell/ell.h>
#include "src/shared/shell.h"
#include "src/shared/util.h"
#include "mesh/mesh.h"
#include "mesh/mesh-defs.h"
#include "tools/mesh/agent.h"
#include "tools/mesh/cfgcli.h"
#include "tools/mesh/keys.h"
#include "tools/mesh/mesh-db.h"
#include "tools/mesh/model.h"
#include "tools/mesh/remote.h"
#define PROMPT_ON "[mesh-cfgclient]# "
#define PROMPT_OFF "Waiting to connect to bluetooth-meshd..."
#define CFG_SRV_MODEL 0x0000
#define CFG_CLI_MODEL 0x0001
#define RPR_SVR_MODEL 0x0004
#define RPR_CLI_MODEL 0x0005
#define PRV_BEACON_SVR 0x0008
#define PRV_BEACON_CLI 0x0009
#define UNPROV_SCAN_MAX_SECS 300
#define DEFAULT_START_ADDRESS 0x00aa
#define DEFAULT_MAX_ADDRESS (VIRTUAL_ADDRESS_LOW - 1)
#define DEFAULT_NET_INDEX 0x0000
#define MAX_CRPL_SIZE 0x7fff
#define DEFAULT_CFG_FILE "config_db.json"
#define DEFAULT_EXPORT_FILE "export_db.json"
struct meshcfg_el {
const char *path;
uint8_t index;
uint16_t mods[4];
};
struct meshcfg_app {
const char *path;
const char *agent_path;
struct meshcfg_el ele;
uint16_t cid;
uint16_t pid;
uint16_t vid;
uint16_t crpl;
uint8_t uuid[16];
};
struct meshcfg_node {
const char *path;
struct l_dbus_proxy *proxy;
struct l_dbus_proxy *mgmt_proxy;
union {
uint64_t u64;
uint8_t u8[8];
} token;
};
struct unprov_device {
time_t last_seen;
int id;
uint32_t uri_hash;
uint8_t uuid[16];
int16_t rssi;
uint16_t server;
uint16_t oob_info;
};
struct generic_request {
uint32_t arg1;
uint32_t arg2;
uint32_t arg3;
uint8_t *data1;
uint8_t *data2;
const char *str;
};
struct scan_data {
uint16_t dst;
uint16_t secs;
};
static void *finalized = L_UINT_TO_PTR(-1);
static struct l_dbus *dbus;
static struct l_timeout *scan_timeout;
static struct l_queue *node_proxies;
static struct l_dbus_proxy *net_proxy;
static struct meshcfg_node *local;
static struct model_info *cfgcli;
static struct l_queue *devices;
static bool prov_in_progress;
static const char * const caps[] = {"static-oob",
"push",
"twist",
"blink",
"beep",
"vibrate",
"public-oob",
"out-alpha",
"in-alpha",
"out-numeric",
"in-numeric"};
static bool have_config;
static struct meshcfg_app app = {
.path = "/mesh/cfgclient",
.agent_path = "/mesh/cfgclient/agent",
.cid = 0x05f1,
.pid = 0x0002,
.vid = 0x0001,
.crpl = MAX_CRPL_SIZE,
.ele = {
.path = "/mesh/cfgclient/ele0",
.index = 0,
.mods = {CFG_SRV_MODEL, CFG_CLI_MODEL,
PRV_BEACON_SVR, PRV_BEACON_CLI}
}
};
static const struct option options[] = {
{ "config", required_argument, 0, 'c' },
{ "address-start", required_argument, 0, 'a' },
{ "address-range", required_argument, 0, 'r' },
{ "net-index", required_argument, 0, 'n' },
{ 0, 0, 0, 0 }
};
static const char *address_opt;
static const char *range_opt;
static const char *net_idx_opt;
static const char *config_opt;
static uint32_t iv_index;
static uint16_t low_addr;
static uint16_t high_addr;
static uint16_t prov_net_idx;
static const char *cfg_fname;
static const char **optargs[] = {
&config_opt,
&address_opt,
&range_opt,
&net_idx_opt,
};
static const char *help[] = {
"Configuration file",
"Starting unicast address for remote nodes",
"Net index for provisioning subnet"
};
static const struct bt_shell_opt opt = {
.options = options,
.optno = sizeof(options) / sizeof(struct option),
.optstr = "c:a:n:",
.optarg = optargs,
.help = help,
};
static const char *dbus_err_args = "org.freedesktop.DBus.Error.InvalidArgs";
static const char *dbus_err_fail = "org.freedesktop.DBus.Error.Failed";
static const char *dbus_err_support = "org.freedesktop.DBus.Error.NotSupported";
static bool parse_argument_on_off(int argc, char *argv[], bool *value)
{
if (!strcmp(argv[1], "on") || !strcmp(argv[1], "yes")) {
*value = TRUE;
return TRUE;
}
if (!strcmp(argv[1], "off") || !strcmp(argv[1], "no")) {
*value = FALSE;
return TRUE;
}
bt_shell_printf("Invalid argument %s\n", argv[1]);
return FALSE;
}
static bool match_device_uuid(const void *a, const void *b)
{
const struct unprov_device *dev = a;
if (a == finalized)
return false;
return memcmp(dev->uuid, b, 16) == 0;
}
static bool match_by_id(const void *a, const void *b)
{
const struct unprov_device *dev = a;
int id = L_PTR_TO_UINT(b);
if (a == finalized)
return false;
l_info("test %d %d", dev->id, id);
return dev->id == id;
}
static bool match_by_srv_uuid(const void *a, const void *b)
{
const struct unprov_device *dev = a;
const struct unprov_device *new_dev = b;
if (a == finalized)
return false;
return (dev->server == new_dev->server) &&
(memcmp(dev->uuid, new_dev->uuid, 16) == 0);
}
static void print_device(void *a, void *b)
{
struct unprov_device *dev = a;
int *cnt = b;
struct tm *tm;
char buf[80];
char *str;
if (a == finalized)
return;
tm = localtime(&dev->last_seen);
assert(strftime(buf, sizeof(buf), "%c", tm));
(*cnt)++;
dev->id = *cnt;
str = l_util_hexstring_upper(dev->uuid, sizeof(dev->uuid));
bt_shell_printf(COLOR_YELLOW "#%d" COLOR_OFF
" UUID: %s, RSSI %d, Server: %4.4x\n Seen: %s\n",
*cnt, str, dev->rssi, dev->server, buf);
l_free(str);
}
struct send_data {
const char *ele_path;
bool rmt;
bool is_dev_key;
uint16_t dst;
uint16_t idx;
uint8_t *data;
uint16_t len;
};
struct key_data {
const char *ele_path;
uint16_t dst;
uint16_t idx;
uint16_t net_idx;
bool update;
};
static void append_dict_entry_basic(struct l_dbus_message_builder *builder,
const char *key, const char *signature,
const void *data)
{
if (!builder)
return;
l_dbus_message_builder_enter_dict(builder, "sv");
l_dbus_message_builder_append_basic(builder, 's', key);
l_dbus_message_builder_enter_variant(builder, signature);
l_dbus_message_builder_append_basic(builder, signature[0], data);
l_dbus_message_builder_leave_variant(builder);
l_dbus_message_builder_leave_dict(builder);
}
static void append_byte_array(struct l_dbus_message_builder *builder,
unsigned char *data, unsigned int len)
{
unsigned int i;
l_dbus_message_builder_enter_array(builder, "y");
for (i = 0; i < len; i++)
l_dbus_message_builder_append_basic(builder, 'y', &(data[i]));
l_dbus_message_builder_leave_array(builder);
}
static void send_msg_setup(struct l_dbus_message *msg, void *user_data)
{
struct send_data *req = user_data;
struct l_dbus_message_builder *builder;
builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_append_basic(builder, 'o', req->ele_path);
l_dbus_message_builder_append_basic(builder, 'q', &req->dst);
if (req->is_dev_key)
l_dbus_message_builder_append_basic(builder, 'b', &req->rmt);
l_dbus_message_builder_append_basic(builder, 'q', &req->idx);
/* Options */
l_dbus_message_builder_enter_array(builder, "{sv}");
l_dbus_message_builder_enter_dict(builder, "sv");
l_dbus_message_builder_leave_dict(builder);
l_dbus_message_builder_leave_array(builder);
/* Data */
append_byte_array(builder, req->data, req->len);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
}
static bool send_msg(void *user_data, uint16_t dst, uint16_t idx,
uint8_t *data, uint16_t len)
{
struct send_data *req;
uint16_t net_idx_tx = idx;
bool is_dev_key;
const char *method_name;
is_dev_key = (idx == APP_IDX_DEV_REMOTE || idx == APP_IDX_DEV_LOCAL);
method_name = is_dev_key ? "DevKeySend" : "Send";
if (is_dev_key) {
net_idx_tx = remote_get_subnet_idx(dst);
if (net_idx_tx == NET_IDX_INVALID)
return false;
}
req = l_new(struct send_data, 1);
req->ele_path = user_data;
req->dst = dst;
req->idx = net_idx_tx;
req->data = data;
req->len = len;
req->rmt = (idx == APP_IDX_DEV_REMOTE);
req->is_dev_key = is_dev_key;
return l_dbus_proxy_method_call(local->proxy, method_name,
send_msg_setup, NULL, req, l_free) != 0;
}
static void send_key_setup(struct l_dbus_message *msg, void *user_data)
{
struct key_data *req = user_data;
struct l_dbus_message_builder *builder;
builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_append_basic(builder, 'o', req->ele_path);
l_dbus_message_builder_append_basic(builder, 'q', &req->dst);
l_dbus_message_builder_append_basic(builder, 'q', &req->idx);
l_dbus_message_builder_append_basic(builder, 'q', &req->net_idx);
l_dbus_message_builder_append_basic(builder, 'b', &req->update);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
}
static bool send_key(void *user_data, uint16_t dst, uint16_t key_idx,
bool is_appkey, bool update)
{
struct key_data *req;
uint16_t net_idx;
const char *method_name = (!is_appkey) ? "AddNetKey" : "AddAppKey";
net_idx = remote_get_subnet_idx(dst);
if (net_idx == NET_IDX_INVALID) {
bt_shell_printf("Node %4.4x not found\n", dst);
return false;
}
if (!is_appkey && !keys_subnet_exists(key_idx)) {
bt_shell_printf("Local NetKey %u (0x%3.3x) not found\n",
key_idx, key_idx);
return false;
}
if (is_appkey && (keys_get_bound_key(key_idx) == NET_IDX_INVALID)) {
bt_shell_printf("Local AppKey %u (0x%3.3x) not found\n",
key_idx, key_idx);
return false;
}
req = l_new(struct key_data, 1);
req->ele_path = user_data;
req->dst = dst;
req->idx = key_idx;
req->net_idx = net_idx;
req->update = update;
return l_dbus_proxy_method_call(local->proxy, method_name,
send_key_setup, NULL, req, l_free) != 0;
}
static void delete_node_setup(struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t primary;
uint8_t ele_cnt;
primary = (uint16_t) req->arg1;
ele_cnt = (uint8_t) req->arg2;
l_dbus_message_set_arguments(msg, "qy", primary, ele_cnt);
}
static void delete_node(uint16_t primary, uint8_t ele_cnt)
{
struct generic_request *req;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
req = l_new(struct generic_request, 1);
req->arg1 = primary;
req->arg2 = ele_cnt;
l_dbus_proxy_method_call(local->mgmt_proxy, "DeleteRemoteNode",
delete_node_setup, NULL, req, l_free);
}
static void client_init(void)
{
cfgcli = cfgcli_init(send_key, delete_node, (void *) app.ele.path);
cfgcli->ops.set_send_func(send_msg, (void *) app.ele.path);
}
static bool caps_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
uint32_t i;
if (!l_dbus_message_builder_enter_array(builder, "s"))
return false;
for (i = 0; i < L_ARRAY_SIZE(caps); i++)
l_dbus_message_builder_append_basic(builder, 's', caps[i]);
l_dbus_message_builder_leave_array(builder);
return true;
}
static void agent_input_done(oob_type_t type, void *buf, uint16_t len,
void *user_data)
{
struct l_dbus_message *msg = user_data;
struct l_dbus_message *reply = NULL;
struct l_dbus_message_builder *builder;
uint32_t val_u32;
uint8_t oob_data[64];
switch (type) {
case NONE:
case OUTPUT:
default:
break;
case ASCII:
if (len > 8) {
bt_shell_printf("Bad input length\n");
break;
}
/* Fall Through */
case HEXADECIMAL:
if (len > sizeof(oob_data)) {
bt_shell_printf("Bad input length\n");
break;
}
memset(oob_data, 0, sizeof(oob_data));
memcpy(oob_data, buf, len);
reply = l_dbus_message_new_method_return(msg);
builder = l_dbus_message_builder_new(reply);
append_byte_array(builder, oob_data, len);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
break;
case DECIMAL:
if (len > 8) {
bt_shell_printf("Bad input length\n");
break;
}
val_u32 = l_get_be32(buf);
reply = l_dbus_message_new_method_return(msg);
l_dbus_message_set_arguments(reply, "u", val_u32);
break;
}
if (!reply)
reply = l_dbus_message_new_error(msg, dbus_err_fail, NULL);
l_dbus_send(dbus, reply);
}
struct requested_action {
const char *action;
const char *description;
};
static struct requested_action display_numeric_table[] = {
{ "push", "Push remote button %d times"},
{ "twist", "Twist remote nob %d times"},
{ "in-numeric", "Enter %d on remote device"},
{ "out-numeric", "Enter %d on remote device"}
};
static struct requested_action prompt_numeric_table[] = {
{ "blink", "Enter the number of times remote LED blinked"},
{ "beep", "Enter the number of times remote device beeped"},
{ "vibrate", "Enter the number of times remote device vibrated"},
{ "in-numeric", "Enter the number displayed on remote device"},
{ "out-numeric", "Enter the number displayed on remote device"}
};
static int get_action(char *str, bool prompt)
{
struct requested_action *action_table;
size_t len;
int i, sz;
if (!str)
return -1;
if (prompt) {
len = strlen(str);
sz = L_ARRAY_SIZE(prompt_numeric_table);
action_table = prompt_numeric_table;
} else {
len = strlen(str);
sz = L_ARRAY_SIZE(display_numeric_table);
action_table = display_numeric_table;
}
for (i = 0; i < sz; ++i)
if (len == strlen(action_table[i].action) &&
!strcmp(str, action_table[i].action))
return i;
return -1;
}
static struct l_dbus_message *disp_numeric_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
char *str;
uint32_t n;
int action_index;
if (!l_dbus_message_get_arguments(msg, "su", &str, &n)) {
l_error("Cannot parse \"DisplayNumeric\" arguments");
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
}
action_index = get_action(str, false);
if (action_index < 0)
return l_dbus_message_new_error(msg, dbus_err_support, NULL);
str = l_strdup_printf(display_numeric_table[action_index].description,
n);
bt_shell_printf(COLOR_YELLOW "%s\n" COLOR_OFF, str);
l_free(str);
return l_dbus_message_new_method_return(msg);
}
static struct l_dbus_message *disp_string_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
const char *prompt = "Enter AlphaNumeric code on remote device:";
char *str;
if (!l_dbus_message_get_arguments(msg, "s", &str)) {
l_error("Cannot parse \"DisplayString\" arguments");
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
}
bt_shell_printf(COLOR_YELLOW "%s %s\n" COLOR_OFF, prompt, str);
return l_dbus_message_new_method_return(msg);
}
static struct l_dbus_message *prompt_numeric_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
char *str;
int action_index;
const char *desc;
if (!l_dbus_message_get_arguments(msg, "s", &str)) {
l_error("Cannot parse \"PromptNumeric\" arguments");
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
}
action_index = get_action(str, true);
if (action_index < 0)
return l_dbus_message_new_error(msg, dbus_err_support, NULL);
desc = prompt_numeric_table[action_index].description;
l_dbus_message_ref(msg);
agent_input_request(DECIMAL, 8, desc, agent_input_done, msg);
return NULL;
}
static struct l_dbus_message *prompt_public_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
l_dbus_message_ref(msg);
agent_input_request(HEXADECIMAL, 64, "Enter 512 bit Public Key",
agent_input_done, msg);
return NULL;
}
static struct l_dbus_message *prompt_static_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
char *str;
if (!l_dbus_message_get_arguments(msg, "s", &str) || !str) {
l_error("Cannot parse \"PromptStatic\" arguments");
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
}
if (!strcmp(str, "in-alpha") || !strcmp(str, "out-alpha")) {
l_dbus_message_ref(msg);
agent_input_request(ASCII, 8, "Enter displayed Ascii code",
agent_input_done, msg);
} else if (!strcmp(str, "static-oob")) {
l_dbus_message_ref(msg);
agent_input_request(HEXADECIMAL, 16, "Enter Static Key",
agent_input_done, msg);
} else
return l_dbus_message_new_error(msg, dbus_err_support, NULL);
return NULL;
}
static void setup_agent_iface(struct l_dbus_interface *iface)
{
l_dbus_interface_property(iface, "Capabilities", 0, "as", caps_getter,
NULL);
/* TODO: Other properties */
l_dbus_interface_method(iface, "DisplayString", 0, disp_string_call,
"", "s", "value");
l_dbus_interface_method(iface, "DisplayNumeric", 0, disp_numeric_call,
"", "su", "type", "number");
l_dbus_interface_method(iface, "PromptNumeric", 0, prompt_numeric_call,
"u", "s", "number", "type");
l_dbus_interface_method(iface, "PromptStatic", 0, prompt_static_call,
"ay", "s", "data", "type");
l_dbus_interface_method(iface, "PublicKey", 0, prompt_public_call,
"ay", "", "data");
}
static bool register_agent(void)
{
if (!l_dbus_register_interface(dbus, MESH_PROVISION_AGENT_INTERFACE,
setup_agent_iface, NULL, false)) {
l_error("Unable to register agent interface");
return false;
}
if (!l_dbus_register_object(dbus, app.agent_path, NULL, NULL,
MESH_PROVISION_AGENT_INTERFACE, NULL, NULL)) {
l_error("Failed to register object %s", app.agent_path);
return false;
}
if (!l_dbus_object_add_interface(dbus, app.agent_path,
L_DBUS_INTERFACE_PROPERTIES, NULL)) {
l_error("Failed to add interface %s",
L_DBUS_INTERFACE_PROPERTIES);
return false;
}
return true;
}
static void try_set_node_proxy(void *a, void *b)
{
struct l_dbus_proxy *proxy = a;
const char *interface = l_dbus_proxy_get_interface(proxy);
const char *path = l_dbus_proxy_get_path(proxy);
if (strcmp(local->path, path))
return;
if (!strcmp(interface, MESH_MANAGEMENT_INTERFACE))
local->mgmt_proxy = proxy;
else if (!strcmp(interface, MESH_NODE_INTERFACE))
local->proxy = proxy;
}
static void attach_node_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
struct meshcfg_node *node = user_data;
struct l_dbus_message_iter iter_cfg;
uint32_t ivi;
if (l_dbus_message_is_error(msg)) {
const char *name;
l_dbus_message_get_error(msg, &name, NULL);
l_error("Failed to attach node: %s", name);
goto fail;
}
if (!l_dbus_message_get_arguments(msg, "oa(ya(qa{sv}))",
&local->path, &iter_cfg))
goto fail;
bt_shell_printf("Attached with path %s\n", local->path);
/* Populate node's proxies */
l_queue_foreach(node_proxies, try_set_node_proxy, node);
/* Remove from orphaned proxies list */
if (local->proxy)
l_queue_remove(node_proxies, local->proxy);
if (local->mgmt_proxy)
l_queue_remove(node_proxies, local->mgmt_proxy);
/* Inititalize config client model */
client_init();
if (l_dbus_proxy_get_property(local->proxy, "IvIndex", "u", &ivi) &&
ivi != iv_index) {
iv_index = ivi;
mesh_db_set_iv_index(ivi);
remote_clear_rejected_addresses(ivi);
}
/* Read own node composition */
if (!cfgcli_get_comp(0x0001, 128))
l_error("Failed to read own composition");
return;
fail:
l_free(node);
node = NULL;
}
static void attach_node_setup(struct l_dbus_message *msg, void *user_data)
{
l_dbus_message_set_arguments(msg, "ot", app.path,
l_get_be64(local->token.u8));
}
static void create_net_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
if (l_dbus_message_is_error(msg)) {
const char *name;
l_dbus_message_get_error(msg, &name, NULL);
l_error("Failed to create network: %s", name);
return;
}
}
static void create_net_setup(struct l_dbus_message *msg, void *user_data)
{
struct l_dbus_message_builder *builder;
/* Generate random UUID */
l_uuid_v4(app.uuid);
builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_append_basic(builder, 'o', app.path);
append_byte_array(builder, app.uuid, 16);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
}
static void cmd_create_network(int argc, char *argv[])
{
if (have_config) {
l_error("Mesh network configuration exists (%s)", cfg_fname);
return;
}
l_dbus_proxy_method_call(net_proxy, "CreateNetwork", create_net_setup,
create_net_reply, NULL,
NULL);
}
static void scan_reply(struct l_dbus_proxy *proxy, struct l_dbus_message *msg,
void *user_data)
{
if (l_dbus_message_is_error(msg)) {
const char *name;
l_dbus_message_get_error(msg, &name, NULL);
l_error("Failed to start unprovisioned scan: %s", name);
return;
}
bt_shell_printf("Unprovisioned scan started\n");
}
static void scan_setup(struct l_dbus_message *msg, void *user_data)
{
struct scan_data *data = user_data;
struct l_dbus_message_builder *builder;
builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_enter_array(builder, "{sv}");
append_dict_entry_basic(builder, "Seconds", "q", &data->secs);
append_dict_entry_basic(builder, "Server", "q", &data->dst);
l_dbus_message_builder_leave_array(builder);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
/* Destination info not needed after call */
l_free(data);
}
static void scan_start(void *user_data, uint16_t dst, uint32_t model)
{
struct scan_data *data;
if (model != (0xffff0000 | RPR_SVR_MODEL))
return;
data = l_malloc(sizeof(struct scan_data));
data->secs = L_PTR_TO_UINT(user_data);
data->dst = dst;
if (!l_dbus_proxy_method_call(local->mgmt_proxy, "UnprovisionedScan",
scan_setup, scan_reply, data, NULL))
l_free(data);
}
static void scan_to(struct l_timeout *timeout, void *user_data)
{
int cnt = 0;
if (l_queue_peek_head(devices) != finalized)
l_queue_push_head(devices, finalized);
l_timeout_remove(timeout);
scan_timeout = NULL;
bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
l_queue_foreach(devices, print_device, &cnt);
}
static void free_devices(void *a)
{
if (a == finalized)
return;
l_free(a);
}
static void cmd_scan_unprov(int argc, char *argv[])
{
uint32_t secs = 0;
bool enable;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (parse_argument_on_off(argc, argv, &enable) == FALSE) {
bt_shell_printf("Failed to parse input\n");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
if (argc == 3) {
sscanf(argv[2], "%u", &secs);
if (secs > UNPROV_SCAN_MAX_SECS)
secs = UNPROV_SCAN_MAX_SECS;
} else
secs = 60;
l_timeout_remove(scan_timeout);
scan_timeout = NULL;
if (enable) {
l_queue_clear(devices, free_devices);
remote_foreach_model(scan_start, L_UINT_TO_PTR(secs));
scan_timeout = l_timeout_create(secs, scan_to, NULL, NULL);
} else {
/* Mark devices queue as finalized */
l_queue_push_head(devices, finalized);
l_dbus_proxy_method_call(local->mgmt_proxy,
"UnprovisionedScanCancel",
NULL, NULL, NULL, NULL);
}
}
static uint8_t *parse_key(struct l_dbus_message_iter *iter, uint16_t id,
const char *name)
{
uint8_t *val;
uint32_t len;
if (!l_dbus_message_iter_get_fixed_array(iter, &val, &len)
|| len != 16) {
bt_shell_printf("Failed to parse %s %4.4x\n", name, id);
return NULL;
}
return val;
}
static bool parse_app_keys(struct l_dbus_message_iter *iter, uint16_t net_idx,
void *user_data)
{
struct l_dbus_message_iter app_keys, app_key, opts;
uint16_t app_idx;
if (!l_dbus_message_iter_get_variant(iter, "a(qaya{sv})", &app_keys))
return false;
while (l_dbus_message_iter_next_entry(&app_keys, &app_idx, &app_key,
&opts)) {
struct l_dbus_message_iter var;
uint8_t *val, *old_val = NULL;
const char *key;
val = parse_key(&app_key, app_idx, "AppKey");
if (!val)
return false;
while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
if (!strcmp(key, "OldKey")) {
if (!l_dbus_message_iter_get_variant(&var, "ay",
&app_key))
return false;
old_val = parse_key(&app_key, app_idx,
"old NetKey");
if (!old_val)
return false;
}
}
mesh_db_set_app_key(user_data, net_idx, app_idx, val, old_val);
}
return true;
}
static bool parse_net_keys(struct l_dbus_message_iter *iter, void *user_data)
{
struct l_dbus_message_iter net_keys, net_key, opts;
uint16_t idx;
if (!l_dbus_message_iter_get_variant(iter, "a(qaya{sv})", &net_keys))
return false;
while (l_dbus_message_iter_next_entry(&net_keys, &idx, &net_key,
&opts)) {
struct l_dbus_message_iter var;
uint8_t *val, *old_val = NULL;
uint8_t phase = KEY_REFRESH_PHASE_NONE;
const char *key;
val = parse_key(&net_key, idx, "NetKey");
if (!val)
return false;
while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
if (!strcmp(key, "AppKeys")) {
if (!parse_app_keys(&var, idx, user_data))
return false;
} else if (!strcmp(key, "Phase")) {
if (!l_dbus_message_iter_get_variant(&var, "y",
&phase))
return false;
} else if (!strcmp(key, "OldKey")) {
if (!l_dbus_message_iter_get_variant(&var, "ay",
&net_key))
return false;
old_val = parse_key(&net_key, idx,
"old NetKey");
if (!old_val)
return false;
}
}
mesh_db_set_net_key(user_data, idx, val, old_val, phase);
}
return true;
}
static bool parse_dev_keys(struct l_dbus_message_iter *iter, void *user_data)
{
struct l_dbus_message_iter keys, dev_key;
uint16_t unicast;
if (!l_dbus_message_iter_get_variant(iter, "a(qay)", &keys))
return false;
while (l_dbus_message_iter_next_entry(&keys, &unicast, &dev_key)) {
uint8_t *data;
data = parse_key(&dev_key, unicast, "Device Key");
if (!data)
return false;
mesh_db_set_device_key(user_data, unicast, data);
}
return true;
}
static void export_keys_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
struct l_dbus_message_iter iter, var;
char *cfg_dir = NULL, *fname = NULL;
const char *key;
bool is_error = true;
if (l_dbus_message_is_error(msg)) {
const char *name;
l_dbus_message_get_error(msg, &name, NULL);
bt_shell_printf("Failed to export keys: %s", name);
goto done;
}
if (!l_dbus_message_get_arguments(msg, "a{sv}", &iter)) {
bt_shell_printf("Malformed ExportKeys reply");
goto done;
}
while (l_dbus_message_iter_next_entry(&iter, &key, &var)) {
if (!strcmp(key, "NetKeys")) {
if (!parse_net_keys(&var, user_data))
goto done;
} else if (!strcmp(key, "DevKeys")) {
if (!parse_dev_keys(&var, user_data))
goto done;
}
}
is_error = false;
cfg_dir = l_strdup(cfg_fname);
cfg_dir = dirname(cfg_dir);
fname = l_strdup_printf("%s/%s", cfg_dir, DEFAULT_EXPORT_FILE);
done:
if (mesh_db_finish_export(is_error, user_data, fname)) {
if (!is_error)
bt_shell_printf("Config DB is exported to %s\n", fname);
}
l_free(cfg_dir);
l_free(fname);
}
static void cmd_export_db(int argc, char *argv[])
{
void *cfg_export;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
/* Generate a properly formatted DB from the local config */
cfg_export = mesh_db_prepare_export();
if (!cfg_export) {
bt_shell_printf("Failed to prepare config db\n");
return;
}
/* Export the keys from the daemon */
l_dbus_proxy_method_call(local->mgmt_proxy, "ExportKeys", NULL,
export_keys_reply, cfg_export, NULL);
}
static void cmd_list_unprov(int argc, char *argv[])
{
int cnt = 0;
bt_shell_printf(COLOR_YELLOW "Unprovisioned devices:\n" COLOR_OFF);
l_queue_foreach(devices, print_device, &cnt);
}
static void cmd_list_nodes(int argc, char *argv[])
{
remote_print_all();
}
static void cmd_keys(int argc, char *argv[])
{
keys_print_keys();
}
static void free_generic_request(void *data)
{
struct generic_request *req = data;
l_free(req->data1);
l_free(req->data2);
l_free(req);
}
static void import_node_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t primary, net_idx;
uint8_t ele_cnt;
if (l_dbus_message_is_error(msg)) {
const char *name;
l_dbus_message_get_error(msg, &name, NULL);
l_error("Failed to import remote node: %s", name);
return;
}
net_idx = (uint16_t) req->arg1;
primary = (uint16_t) req->arg2;
ele_cnt = (uint8_t) req->arg3;
remote_add_node(req->data1, primary, ele_cnt, net_idx);
}
static void import_node_setup(struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t primary;
uint8_t ele_cnt;
struct l_dbus_message_builder *builder;
primary = (uint16_t) req->arg2;
ele_cnt = (uint8_t) req->arg3;
builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_append_basic(builder, 'q', &primary);
l_dbus_message_builder_append_basic(builder, 'y', &ele_cnt);
append_byte_array(builder, req->data2, 16);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
}
static void cmd_import_node(int argc, char *argv[])
{
struct generic_request *req;
size_t sz;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (argc < 6) {
bt_shell_printf("UUID, element count and device key");
bt_shell_printf("Unicast, element count and device key");
bt_shell_printf("are required\n");
return;
}
req = l_new(struct generic_request, 1);
/* Device UUID */
req->data1 = l_util_from_hexstring(argv[1], &sz);
if (!req->data1 || sz != 16 || !l_uuid_is_valid(req->data1)) {
l_error("Failed to generate UUID array from %s", argv[1]);
goto fail;
}
/* NetKey Index*/
if (sscanf(argv[2], "%04x", &req->arg1) != 1)
goto fail;
/* Unicast of the primary element */
if (sscanf(argv[3], "%04x", &req->arg2) != 1)
goto fail;
/* Number of elements */
if (sscanf(argv[4], "%u", &req->arg3) != 1)
goto fail;
/* DevKey */
req->data2 = l_util_from_hexstring(argv[5], &sz);
if (!req->data2 || sz != 16) {
l_error("Failed to generate DevKey array from %s", argv[5]);
goto fail;
}
l_dbus_proxy_method_call(local->mgmt_proxy, "ImportRemoteNode",
import_node_setup, import_node_reply,
req, free_generic_request);
return;
fail:
free_generic_request(req);
}
static void subnet_set_phase_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t net_idx;
uint8_t phase;
if (l_dbus_message_is_error(msg)) {
const char *name;
l_dbus_message_get_error(msg, &name, NULL);
l_error("Failed to set subnet phase: %s", name);
return;
}
net_idx = (uint16_t) req->arg1;
phase = (uint8_t) req->arg2;
if (phase == KEY_REFRESH_PHASE_THREE)
phase = KEY_REFRESH_PHASE_NONE;
keys_set_net_key_phase(net_idx, phase, true);
}
static void subnet_set_phase_setup(struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t net_idx;
uint8_t phase;
net_idx = (uint16_t) req->arg1;
phase = (uint8_t) req->arg2;
l_dbus_message_set_arguments(msg, "qy", net_idx, phase);
}
static void cmd_subnet_set_phase(int argc, char *argv[])
{
struct generic_request *req;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (argc < 3) {
bt_shell_printf("NetKey index and phase are required\n");
return;
}
req = l_new(struct generic_request, 1);
if (sscanf(argv[1], "%04x", &req->arg1) != 1)
goto fail;
if (sscanf(argv[2], "%d", &req->arg2) != 1)
goto fail;
l_dbus_proxy_method_call(local->mgmt_proxy, "SetKeyPhase",
subnet_set_phase_setup,
subnet_set_phase_reply, req, l_free);
return;
fail:
l_free(req);
}
static void mgr_key_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t idx = (uint16_t) req->arg1;
const char *method = req->str;
if (l_dbus_message_is_error(msg)) {
const char *name;
l_dbus_message_get_error(msg, &name, NULL);
l_error("Method %s returned error: %s", method, name);
bt_shell_printf("Method %s returned error: %s\n", method, name);
return;
}
if (!strcmp("CreateSubnet", method)) {
keys_add_net_key(idx);
mesh_db_add_net_key(idx);
} else if (!strcmp("DeleteSubnet", method)) {
keys_del_net_key(idx);
mesh_db_del_net_key(idx);
} else if (!strcmp("UpdateSubnet", method)) {
keys_set_net_key_phase(idx, KEY_REFRESH_PHASE_ONE, true);
} else if (!strcmp("DeleteAppKey", method)) {
keys_del_app_key(idx);
mesh_db_del_app_key(idx);
}
}
static void mgr_key_setup(struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t idx = (uint16_t) req->arg1;
l_dbus_message_set_arguments(msg, "q", idx);
}
static void mgr_key_cmd(int argc, char *argv[], const char *method_name)
{
struct generic_request *req;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (argc < 2) {
bt_shell_printf("Missing required arguments\n");
return;
}
req = l_new(struct generic_request, 1);
if (sscanf(argv[1], "%04x", &req->arg1) != 1) {
l_free(req);
return;
}
req->str = method_name;
l_dbus_proxy_method_call(local->mgmt_proxy, method_name,
mgr_key_setup, mgr_key_reply,
req, l_free);
}
static void cmd_delete_appkey(int argc, char *argv[])
{
mgr_key_cmd(argc, argv, "DeleteAppKey");
}
static void cmd_update_appkey(int argc, char *argv[])
{
mgr_key_cmd(argc, argv, "UpdateAppKey");
}
static void cmd_delete_subnet(int argc, char *argv[])
{
mgr_key_cmd(argc, argv, "DeleteSubnet");
}
static void cmd_update_subnet(int argc, char *argv[])
{
mgr_key_cmd(argc, argv, "UpdateSubnet");
}
static void cmd_create_subnet(int argc, char *argv[])
{
mgr_key_cmd(argc, argv, "CreateSubnet");
}
static void add_key_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t net_idx, app_idx;
const char *method = req->str;
if (l_dbus_message_is_error(msg)) {
const char *name;
l_dbus_message_get_error(msg, &name, NULL);
l_error("%s failed: %s", method, name);
return;
}
net_idx = (uint16_t) req->arg1;
if (!strcmp(method, "ImportSubnet")) {
keys_add_net_key(net_idx);
mesh_db_add_net_key(net_idx);
return;
}
app_idx = (uint16_t) req->arg2;
keys_add_app_key(net_idx, app_idx);
mesh_db_add_app_key(net_idx, app_idx);
}
static void import_appkey_setup(struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t net_idx, app_idx;
struct l_dbus_message_builder *builder;
net_idx = (uint16_t) req->arg1;
app_idx = (uint16_t) req->arg2;
builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_append_basic(builder, 'q', &net_idx);
l_dbus_message_builder_append_basic(builder, 'q', &app_idx);
append_byte_array(builder, req->data1, 16);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
}
static void cmd_import_appkey(int argc, char *argv[])
{
struct generic_request *req;
size_t sz;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (argc < 4) {
bt_shell_printf("Netkey and AppKey indices and");
bt_shell_printf("key value are required\n");
return;
}
req = l_new(struct generic_request, 1);
if (sscanf(argv[1], "%04x", &req->arg1) != 1)
goto fail;
if (sscanf(argv[2], "%04x", &req->arg2) != 1)
goto fail;
req->data1 = l_util_from_hexstring(argv[3], &sz);
if (!req->data1 || sz != 16) {
l_error("Failed to generate key array from %s", argv[3]);
goto fail;
}
req->str = "ImportAppKey";
l_dbus_proxy_method_call(local->mgmt_proxy, "ImportAppKey",
import_appkey_setup, add_key_reply,
req, free_generic_request);
return;
fail:
free_generic_request(req);
}
static void import_subnet_setup(struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t net_idx;
struct l_dbus_message_builder *builder;
net_idx = (uint16_t) req->arg1;
builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_append_basic(builder, 'q', &net_idx);
append_byte_array(builder, req->data1, 16);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
}
static void cmd_import_subnet(int argc, char *argv[])
{
struct generic_request *req;
size_t sz;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (argc < 3) {
bt_shell_printf("NetKey index and value are required\n");
return;
}
req = l_new(struct generic_request, 1);
if (sscanf(argv[1], "%04x", &req->arg1) != 1)
goto fail;
req->data1 = l_util_from_hexstring(argv[2], &sz);
if (!req->data1 || sz != 16) {
l_error("Failed to generate key array from %s", argv[2]);
goto fail;
}
req->str = "ImportSubnet";
l_dbus_proxy_method_call(local->mgmt_proxy, "ImportSubnet",
import_subnet_setup, add_key_reply,
req, free_generic_request);
return;
fail:
free_generic_request(req);
}
static void create_appkey_setup(struct l_dbus_message *msg, void *user_data)
{
struct generic_request *req = user_data;
uint16_t net_idx, app_idx;
net_idx = (uint16_t) req->arg1;
app_idx = (uint16_t) req->arg2;
l_dbus_message_set_arguments(msg, "qq", net_idx, app_idx);
}
static void cmd_create_appkey(int argc, char *argv[])
{
struct generic_request *req;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (argc < 3) {
bt_shell_printf("AppKey index is required\n");
return;
}
req = l_new(struct generic_request, 1);
if (sscanf(argv[1], "%04x", &req->arg1) != 1)
goto fail;
if (sscanf(argv[2], "%04x", &req->arg2) != 1)
goto fail;
req->str = "CreateAppKey";
l_dbus_proxy_method_call(local->mgmt_proxy, "CreateAppKey",
create_appkey_setup, add_key_reply,
req, l_free);
return;
fail:
l_free(req);
}
static void add_node_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
if (l_dbus_message_is_error(msg)) {
const char *name;
prov_in_progress = false;
l_dbus_message_get_error(msg, &name, NULL);
l_error("Failed to start provisioning: %s", name);
return;
}
bt_shell_printf("Provisioning started\n");
}
static void reprov_reply(struct l_dbus_proxy *proxy,
struct l_dbus_message *msg, void *user_data)
{
if (l_dbus_message_is_error(msg)) {
const char *name;
prov_in_progress = false;
l_dbus_message_get_error(msg, &name, NULL);
l_error("Failed to start provisioning: %s", name);
return;
}
bt_shell_printf("Reprovisioning started\n");
}
static void reprovision_setup(struct l_dbus_message *msg, void *user_data)
{
uint16_t target = L_PTR_TO_UINT(user_data);
uint8_t nppi = L_PTR_TO_UINT(user_data) >> 16;
struct l_dbus_message_builder *builder;
builder = l_dbus_message_builder_new(msg);
l_dbus_message_builder_append_basic(builder, 'q', &target);
l_dbus_message_builder_enter_array(builder, "{sv}");
/* TODO: populate with options when defined */
append_dict_entry_basic(builder, "NPPI", "y", &nppi);
l_dbus_message_builder_leave_array(builder);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
}
static void add_node_setup(struct l_dbus_message *msg, void *user_data)
{
struct unprov_device *dev = user_data;
struct l_dbus_message_builder *builder;
builder = l_dbus_message_builder_new(msg);
append_byte_array(builder, dev->uuid, 16);
l_dbus_message_builder_enter_array(builder, "{sv}");
/* TODO: populate with options when defined */
l_dbus_message_builder_leave_array(builder);
l_dbus_message_builder_finalize(builder);
l_dbus_message_builder_destroy(builder);
}
static void cmd_start_prov(int argc, char *argv[])
{
struct unprov_device *dev = NULL;
int id;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (prov_in_progress) {
bt_shell_printf("Provisioning is already in progress\n");
return;
}
if (!argv[1]) {
bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED);
return;
}
if (*(argv[1]) == '#') {
if (sscanf(argv[1] + 1, "%d", &id) == 1)
dev = l_queue_find(devices, match_by_id,
L_UINT_TO_PTR(id));
if (!dev) {
bt_shell_printf(COLOR_RED "unknown id\n" COLOR_RED);
return;
}
} else if (strlen(argv[1]) == 32) {
size_t sz;
uint8_t *uuid = l_util_from_hexstring(argv[1], &sz);
if (sz != 16) {
bt_shell_printf(COLOR_RED "Invalid UUID\n" COLOR_RED);
return;
}
dev = l_queue_find(devices, match_device_uuid, uuid);
if (!dev) {
dev = l_new(struct unprov_device, 1);
memcpy(dev->uuid, uuid, 16);
l_queue_push_tail(devices, dev);
}
l_free(uuid);
} else {
bt_shell_printf(COLOR_RED "Requires UUID\n" COLOR_RED);
return;
}
if (l_dbus_proxy_method_call(local->mgmt_proxy, "AddNode",
add_node_setup, add_node_reply,
dev, NULL))
prov_in_progress = true;
}
static void cmd_start_reprov(int argc, char *argv[])
{
uint16_t target = 0;
uint8_t nppi = 0;
if (!local || !local->proxy || !local->mgmt_proxy) {
bt_shell_printf("Node is not attached\n");
return;
}
if (prov_in_progress) {
bt_shell_printf("Provisioning is already in progress\n");
return;
}
if (!argv[1]) {
bt_shell_printf(COLOR_RED "Requires Unicast\n" COLOR_RED);
return;
}
if (argv[2]) {
char *end;
nppi = strtol(argv[2], &end, 16);
}
if (strlen(argv[1]) == 4) {
char *end;
target = strtol(argv[1], &end, 16);
if (end != (argv[1] + 4)) {
bt_shell_printf(COLOR_RED "Invalid Unicast\n"
COLOR_RED);
return;
}
} else {
bt_shell_printf(COLOR_RED "Requires Unicast\n" COLOR_RED);
return;
}
if (l_dbus_proxy_method_call(local->mgmt_proxy, "Reprovision",
reprovision_setup, reprov_reply,
L_UINT_TO_PTR(target + (nppi << 16)),
NULL))
prov_in_progress = true;
}
static const struct bt_shell_menu main_menu = {
.name = "main",
.entries = {
{ "create", "[unicast_range_low]", cmd_create_network,
"Create new mesh network with one initial node" },
{ "discover-unprovisioned", "<on/off> [seconds]", cmd_scan_unprov,
"Look for devices to provision" },
{ "appkey-create", "<net_idx> <app_idx>", cmd_create_appkey,
"Create a new local AppKey" },
{ "appkey-import", "<net_idx> <app_idx> <key>", cmd_import_appkey,
"Import a new local AppKey" },
{ "appkey-update", "<app_idx>", cmd_update_appkey,
"Update local AppKey" },
{ "appkey-delete", "<app_idx>", cmd_delete_appkey,
"Delete local AppKey" },
{ "subnet-create", "<net_idx>", cmd_create_subnet,
"Create a new local subnet (NetKey)" },
{ "subnet-import", "<net_idx> <key>", cmd_import_subnet,
"Import a new local subnet (NetKey)" },
{ "subnet-update", "<net_idx>", cmd_update_subnet,
"Update local subnet (NetKey)" },
{ "subnet-delete", "<net_idx>", cmd_delete_subnet,
"Delete local subnet (NetKey)" },
{ "subnet-set-phase", "<net_idx> <phase>", cmd_subnet_set_phase,
"Set subnet (NetKey) phase" },
{ "list-unprovisioned", NULL, cmd_list_unprov,
"List unprovisioned devices" },
{ "provision", "<uuid>", cmd_start_prov,
"Initiate provisioning"},
{ "reprovision", "<unicast> [0|1|2]", cmd_start_reprov,
"Refresh Device Key"},
{ "node-import", "<uuid> <net_idx> <primary> <ele_count> <dev_key>",
cmd_import_node,
"Import an externally provisioned remote node"},
{ "list-nodes", NULL, cmd_list_nodes,
"List remote mesh nodes"},
{ "keys", NULL, cmd_keys,
"List available keys"},
{ "export-db", NULL, cmd_export_db,
"Export mesh configuration database"},
{ } },
};
static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)
{
const char *interface = l_dbus_proxy_get_interface(proxy);
const char *path = l_dbus_proxy_get_path(proxy);
bt_shell_printf("Proxy added: %s (%s)\n", interface, path);
if (!strcmp(interface, MESH_NETWORK_INTERFACE)) {
net_proxy = proxy;
/*
* If mesh network configuration has been read from
* storage, attach the provisioner/config-client node.
*/
if (local)
l_dbus_proxy_method_call(net_proxy, "Attach",
attach_node_setup,
attach_node_reply, NULL,
NULL);
return;
}
if (!strcmp(interface, MESH_MANAGEMENT_INTERFACE)) {
if (local && local->path) {
if (!strcmp(local->path, path))
local->mgmt_proxy = proxy;
} else
l_queue_push_tail(node_proxies, proxy);
return;
}
if (!strcmp(interface, MESH_NODE_INTERFACE)) {
if (local && local->path) {
if (!strcmp(local->path, path))
local->proxy = proxy;
} else
l_queue_push_tail(node_proxies, proxy);
}
}
static void proxy_removed(struct l_dbus_proxy *proxy, void *user_data)
{
const char *interface = l_dbus_proxy_get_interface(proxy);
const char *path = l_dbus_proxy_get_path(proxy);
bt_shell_printf("Proxy removed: %s (%s)\n", interface, path);
if (!strcmp(interface, MESH_NETWORK_INTERFACE)) {
bt_shell_printf("Mesh removed, terminating.\n");
l_main_quit();
return;
}
if (!strcmp(interface, MESH_NODE_INTERFACE)) {
if (local && local->path && !strcmp(local->path, path))
local->proxy = NULL;
l_queue_remove(node_proxies, proxy);
return;
}
if (!strcmp(interface, MESH_MANAGEMENT_INTERFACE)) {
if (local && local->path && !strcmp(local->path, path))
local->mgmt_proxy = NULL;
l_queue_remove(node_proxies, proxy);
}
}
static void build_model(struct l_dbus_message_builder *builder, uint16_t mod_id,
bool pub_enable, bool sub_enable)
{
l_dbus_message_builder_enter_struct(builder, "qa{sv}");
l_dbus_message_builder_append_basic(builder, 'q', &mod_id);
l_dbus_message_builder_enter_array(builder, "{sv}");
append_dict_entry_basic(builder, "Subscribe", "b", &sub_enable);
append_dict_entry_basic(builder, "Publish", "b", &pub_enable);
l_dbus_message_builder_leave_array(builder);
l_dbus_message_builder_leave_struct(builder);
}
static bool mod_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
l_dbus_message_builder_enter_array(builder, "(qa{sv})");
build_model(builder, app.ele.mods[0], false, false);
build_model(builder, app.ele.mods[1], false, false);
build_model(builder, app.ele.mods[2], false, false);
build_model(builder, app.ele.mods[3], false, false);
l_dbus_message_builder_leave_array(builder);
return true;
}
static bool vmod_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
l_dbus_message_builder_enter_array(builder, "(qqa{sv})");
l_dbus_message_builder_leave_array(builder);
return true;
}
static bool ele_idx_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
l_dbus_message_builder_append_basic(builder, 'y', &app.ele.index);
return true;
}
static struct l_dbus_message *dev_msg_recv_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
struct l_dbus_message_iter iter;
uint16_t src, idx;
uint8_t *data;
uint32_t n;
bool rmt;
if (!l_dbus_message_get_arguments(msg, "qbqay", &src, &rmt, &idx,
&iter)) {
l_error("Cannot parse received message");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
if (!l_dbus_message_iter_get_fixed_array(&iter, &data, &n)) {
l_error("Cannot parse received message: data");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
bt_shell_printf("Received dev key message (len %u):", n);
/* Pass to the configuration client */
if (cfgcli && cfgcli->ops.recv)
cfgcli->ops.recv(src, APP_IDX_DEV_REMOTE, data, n);
return l_dbus_message_new_method_return(msg);
}
static void setup_ele_iface(struct l_dbus_interface *iface)
{
/* Properties */
l_dbus_interface_property(iface, "Index", 0, "y", ele_idx_getter,
NULL);
l_dbus_interface_property(iface, "VendorModels", 0, "a(qqa{sv})",
vmod_getter, NULL);
l_dbus_interface_property(iface, "Models", 0, "a(qa{sv})", mod_getter,
NULL);
/* Methods */
l_dbus_interface_method(iface, "DevKeyMessageReceived", 0,
dev_msg_recv_call, "", "qbqay", "source",
"remote", "net_index", "data");
/* TODO: Other methods */
}
static int sort_rssi(const void *a, const void *b, void *user_data)
{
const struct unprov_device *new_dev = a;
const struct unprov_device *dev = b;
if (b == finalized)
return 1;
return dev->rssi - new_dev->rssi;
}
static struct l_dbus_message *scan_result_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
struct l_dbus_message_iter iter, opts, var;
struct unprov_device result, *dev;
int16_t rssi;
uint16_t server = 0;
uint32_t n;
uint8_t *prov_data;
const char *key;
const char *sig = "naya{sv}";
if (finalized == l_queue_peek_head(devices))
goto done;
if (!l_dbus_message_get_arguments(msg, sig, &rssi, &iter, &opts)) {
l_error("Cannot parse scan results");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
if (!l_dbus_message_iter_get_fixed_array(&iter, &prov_data, &n) ||
n < 16) {
l_error("Cannot parse scan result: data");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
while (l_dbus_message_iter_next_entry(&opts, &key, &var)) {
if (!strcmp(key, "Server"))
l_dbus_message_iter_get_variant(&var, "q", &server);
}
memcpy(result.uuid, prov_data, 16);
result.server = server;
result.rssi = rssi;
result.id = 0;
result.last_seen = time(NULL);
if (n > 16 && n <= 18)
result.oob_info = l_get_be16(prov_data + 16);
else
result.oob_info = 0;
if (n > 18 && n <= 22)
result.uri_hash = l_get_be32(prov_data + 18);
else
result.uri_hash = 0;
dev = l_queue_remove_if(devices, match_by_srv_uuid, &result);
if (!dev) {
bt_shell_printf("\r" COLOR_YELLOW "Results = %d\n" COLOR_OFF,
l_queue_length(devices) + 1);
dev = l_malloc(sizeof(struct unprov_device));
*dev = result;
} else if (dev->rssi < result.rssi)
*dev = result;
l_queue_insert(devices, dev, sort_rssi, NULL);
done:
return l_dbus_message_new_method_return(msg);
}
static struct l_dbus_message *req_reprov_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
uint8_t cnt;
uint16_t unicast, original;
struct l_dbus_message *reply;
if (!l_dbus_message_get_arguments(msg, "qy", &original, &cnt) ||
!IS_UNICAST(original)) {
l_error("Cannot parse request for reprov data");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
unicast = remote_get_next_unicast(low_addr, high_addr, cnt);
bt_shell_printf("Assign addresses for %u elements\n", cnt);
bt_shell_printf("Original: %4.4x New: %4.4x\n", original, unicast);
reply = l_dbus_message_new_method_return(msg);
l_dbus_message_set_arguments(reply, "q", unicast);
return reply;
}
static struct l_dbus_message *req_prov_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
uint8_t cnt;
uint16_t unicast;
struct l_dbus_message *reply;
/* Both calls handled identicaly except for parameter list */
if (!l_dbus_message_get_arguments(msg, "y", &cnt)) {
l_error("Cannot parse request for prov data");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
unicast = remote_get_next_unicast(low_addr, high_addr, cnt);
if (!IS_UNICAST(unicast)) {
l_error("Failed to allocate addresses for %u elements\n", cnt);
return l_dbus_message_new_error(msg,
"org.freedesktop.DBus.Error."
"Failed to allocate address", NULL);
}
bt_shell_printf("Assign addresses: %4.4x (cnt: %d)\n", unicast, cnt);
reply = l_dbus_message_new_method_return(msg);
l_dbus_message_set_arguments(reply, "qq", prov_net_idx, unicast);
return reply;
}
static void remove_device(uint8_t *uuid)
{
struct unprov_device *dev;
do {
dev = l_queue_remove_if(devices, match_device_uuid, uuid);
l_free(dev);
} while (dev);
}
static struct l_dbus_message *prov_cmplt_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
struct l_dbus_message_iter iter;
int16_t unicast;
uint8_t cnt;
uint32_t n;
uint8_t *uuid;
l_debug("ProvComplete");
if (!prov_in_progress)
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
prov_in_progress = false;
if (!l_dbus_message_get_arguments(msg, "ayqy", &iter, &unicast, &cnt)) {
l_error("Cannot parse add node complete message");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) ||
n != 16) {
l_error("Cannot parse add node complete message: uuid");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
remote_add_node(uuid, unicast, cnt, prov_net_idx);
bt_shell_printf("Provisioning done:\n");
remote_print_node(unicast);
remove_device(uuid);
if (!mesh_db_add_node(uuid, cnt, unicast, prov_net_idx))
l_error("Failed to store new remote node");
return l_dbus_message_new_method_return(msg);
}
static struct l_dbus_message *reprov_cmplt_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
uint16_t unicast, original;
uint8_t old_cnt, cnt, nppi;
l_debug("ReprovComplete");
if (!prov_in_progress)
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
prov_in_progress = false;
if (!l_dbus_message_get_arguments(msg, "qyqy", &original, &nppi,
&unicast, &cnt)) {
l_error("Cannot parse reprov complete message");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
l_debug("ReprovComplete org: %4.4x, nppi: %d, new: %4.4x, cnt: %d",
original, nppi, unicast, cnt);
old_cnt = remote_ele_cnt(original);
if (nppi != 1 && (original != unicast || cnt != old_cnt)) {
l_error("Invalid reprov complete message (NPPI == %d)", nppi);
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
if (nppi)
remote_reset_node(original, unicast, cnt,
mesh_db_get_iv_index());
bt_shell_printf("Reprovisioning done (nppi: %d):\n", nppi);
remote_print_node(unicast);
if (!mesh_db_reset_node(original, unicast, cnt))
l_error("Failed to reset remote node");
return l_dbus_message_new_method_return(msg);
}
static struct l_dbus_message *prov_fail_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
struct l_dbus_message_iter iter;
uint32_t n;
uint8_t *uuid;
char *str, *reason;
if (!prov_in_progress)
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
prov_in_progress = false;
if (!l_dbus_message_get_arguments(msg, "ays", &iter, &reason)) {
l_error("Cannot parse failed message");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
if (!l_dbus_message_iter_get_fixed_array(&iter, &uuid, &n) || n != 16) {
l_error("Cannot parse failed message: uuid");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
bt_shell_printf("Provisioning failed:\n");
str = l_util_hexstring_upper(uuid, 16);
bt_shell_printf("\t" COLOR_RED "UUID = %s\n" COLOR_OFF, str);
l_free(str);
remove_device(uuid);
bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason);
return l_dbus_message_new_method_return(msg);
}
static struct l_dbus_message *reprov_fail_call(struct l_dbus *dbus,
struct l_dbus_message *msg,
void *user_data)
{
struct l_dbus_message_iter iter;
uint16_t original = UNASSIGNED_ADDRESS;
char *reason;
if (!prov_in_progress)
return l_dbus_message_new_error(msg, dbus_err_fail, NULL);
prov_in_progress = false;
if (!l_dbus_message_get_arguments(msg, "qs", &iter, &reason) ||
!IS_UNICAST(original)) {
l_error("Cannot parse Reprov failed message");
return l_dbus_message_new_error(msg, dbus_err_args, NULL);
}
bt_shell_printf("Reprovisioning failed:\n");
bt_shell_printf("\t" COLOR_RED "UNICAST = %4.4x\n" COLOR_OFF, original);
bt_shell_printf("\t" COLOR_RED "%s\n" COLOR_OFF, reason);
return l_dbus_message_new_method_return(msg);
}
static void setup_prov_iface(struct l_dbus_interface *iface)
{
l_dbus_interface_method(iface, "ScanResult", 0, scan_result_call, "",
"naya{sv}", "rssi", "data", "options");
l_dbus_interface_method(iface, "RequestProvData", 0, req_prov_call,
"qq", "y", "net_index", "unicast", "count");
l_dbus_interface_method(iface, "RequestReprovData", 0, req_reprov_call,
"q", "qy", "unicast",
"original", "count");
l_dbus_interface_method(iface, "AddNodeComplete", 0,
prov_cmplt_call, "", "ayqy",
"uuid", "unicast", "count");
l_dbus_interface_method(iface, "ReprovComplete", 0,
reprov_cmplt_call, "", "qyqy",
"original", "nppi", "unicast", "count");
l_dbus_interface_method(iface, "AddNodeFailed", 0, prov_fail_call,
"", "ays", "uuid", "reason");
l_dbus_interface_method(iface, "ReprovFailed", 0, reprov_fail_call,
"", "qs", "unicast", "reason");
}
static bool cid_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
l_dbus_message_builder_append_basic(builder, 'q', &app.cid);
return true;
}
static bool pid_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
l_dbus_message_builder_append_basic(builder, 'q', &app.pid);
return true;
}
static bool vid_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
l_dbus_message_builder_append_basic(builder, 'q', &app.vid);
return true;
}
static bool crpl_getter(struct l_dbus *dbus,
struct l_dbus_message *message,
struct l_dbus_message_builder *builder,
void *user_data)
{
l_dbus_message_builder_append_basic(builder, 'q', &app.crpl);
return true;
}
static void attach_node(void *user_data)
{
l_dbus_proxy_method_call(net_proxy, "Attach", attach_node_setup,
attach_node_reply, NULL,
NULL);
}
static struct l_dbus_message *join_complete(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
char *str;
uint64_t tmp;
if (!l_dbus_message_get_arguments(message, "t", &tmp))
return l_dbus_message_new_error(message, dbus_err_args, NULL);
local = l_new(struct meshcfg_node, 1);
local->token.u64 = l_get_be64(&tmp);
str = l_util_hexstring(&local->token.u8[0], 8);
bt_shell_printf("Created new node with token %s\n", str);
l_free(str);
if (!mesh_db_create(cfg_fname, local->token.u8,
"Mesh Config Client Network")) {
l_free(local);
local = NULL;
return l_dbus_message_new_error(message, dbus_err_fail, NULL);
}
keys_add_net_key(PRIMARY_NET_IDX);
mesh_db_add_net_key(PRIMARY_NET_IDX);
remote_add_node(app.uuid, 0x0001, 1, PRIMARY_NET_IDX);
mesh_db_add_node(app.uuid, 0x0001, 1, PRIMARY_NET_IDX);
mesh_db_add_provisioner("BlueZ mesh-cfgclient", app.uuid,
low_addr, high_addr,
GROUP_ADDRESS_LOW, GROUP_ADDRESS_HIGH);
l_idle_oneshot(attach_node, NULL, NULL);
return l_dbus_message_new_method_return(message);
}
static void property_changed(struct l_dbus_proxy *proxy, const char *name,
struct l_dbus_message *msg, void *user_data)
{
const char *interface = l_dbus_proxy_get_interface(proxy);
const char *path = l_dbus_proxy_get_path(proxy);
if (strcmp(path, local->path))
return;
bt_shell_printf("Property changed: %s %s %s\n", name, path, interface);
if (!strcmp(interface, "org.bluez.mesh.Node1")) {
if (!strcmp(name, "IvIndex")) {
uint32_t ivi;
if (!l_dbus_message_get_arguments(msg, "u", &ivi))
return;
bt_shell_printf("New IV Index: %u\n", ivi);
iv_index = ivi;
mesh_db_set_iv_index(ivi);
remote_clear_rejected_addresses(ivi);
}
}
}
static void setup_app_iface(struct l_dbus_interface *iface)
{
l_dbus_interface_property(iface, "CompanyID", 0, "q", cid_getter,
NULL);
l_dbus_interface_property(iface, "VersionID", 0, "q", vid_getter,
NULL);
l_dbus_interface_property(iface, "ProductID", 0, "q", pid_getter,
NULL);
l_dbus_interface_property(iface, "CRPL", 0, "q", crpl_getter, NULL);
l_dbus_interface_method(iface, "JoinComplete", 0, join_complete,
"", "t", "token");
/* TODO: Methods */
}
static bool register_app(void)
{
if (!l_dbus_register_interface(dbus, MESH_APPLICATION_INTERFACE,
setup_app_iface, NULL, false)) {
l_error("Failed to register interface %s",
MESH_APPLICATION_INTERFACE);
return false;
}
if (!l_dbus_register_interface(dbus, MESH_PROVISIONER_INTERFACE,
setup_prov_iface, NULL, false)) {
l_error("Failed to register interface %s",
MESH_PROVISIONER_INTERFACE);
return false;
}
if (!l_dbus_register_object(dbus, app.path, NULL, NULL,
MESH_APPLICATION_INTERFACE, NULL,
MESH_PROVISIONER_INTERFACE, NULL,
NULL)) {
l_error("Failed to register object %s", app.path);
return false;
}
if (!register_agent())
return false;
if (!l_dbus_register_interface(dbus, MESH_ELEMENT_INTERFACE,
setup_ele_iface, NULL, false)) {
l_error("Failed to register interface %s",
MESH_ELEMENT_INTERFACE);
return false;
}
if (!l_dbus_register_object(dbus, app.ele.path, NULL, NULL,
MESH_ELEMENT_INTERFACE, NULL, NULL)) {
l_error("Failed to register object %s", app.ele.path);
return false;
}
if (!l_dbus_object_add_interface(dbus, app.path,
L_DBUS_INTERFACE_OBJECT_MANAGER,
NULL)) {
l_error("Failed to add interface %s",
L_DBUS_INTERFACE_OBJECT_MANAGER);
return false;
}
return true;
}
static void client_ready(struct l_dbus_client *client, void *user_data)
{
bt_shell_printf("D-Bus client ready\n");
if (!register_app())
bt_shell_quit(EXIT_FAILURE);
bt_shell_attach(fileno(stdin));
}
static void client_connected(struct l_dbus *dbus, void *user_data)
{
bt_shell_printf("D-Bus client connected\n");
bt_shell_set_prompt(PROMPT_ON, COLOR_BLUE);
}
static void client_disconnected(struct l_dbus *dbus, void *user_data)
{
bt_shell_printf("D-Bus client disconnected, exit\n");
bt_shell_quit(EXIT_SUCCESS);
}
static void ready_callback(void *user_data)
{
bt_shell_printf("Connected to D-Bus\n");
if (!l_dbus_object_manager_enable(dbus, "/"))
bt_shell_printf("Failed to register the ObjectManager\n");
}
static bool setup_cfg_storage(void)
{
struct stat st;
if (!config_opt) {
char *home;
char *mesh_dir;
home = getenv("XDG_CONFIG_HOME");
if (home) {
mesh_dir = l_strdup_printf("%s/meshcfg", home);
} else {
home = getenv("HOME");
if (!home) {
l_error("\"HOME\" not set\n");
return false;
}
mesh_dir = l_strdup_printf("%s/.config/meshcfg", home);
}
if (!mesh_dir)
return false;
if (stat(mesh_dir, &st) == 0) {
if (!S_ISDIR(st.st_mode)) {
l_error("%s not a directory", mesh_dir);
return false;
}
} else if (errno == ENOENT) {
if (mkdir(mesh_dir, 0700) != 0)
return false;
} else {
perror("Cannot open config directory");
return false;
}
cfg_fname = l_strdup_printf("%s/%s", mesh_dir,
DEFAULT_CFG_FILE);
l_free(mesh_dir);
} else {
cfg_fname = l_strdup_printf("%s", config_opt);
}
if (stat(cfg_fname, &st) == -1) {
if (errno == ENOENT) {
l_warn("\nWarning: config file \"%s\" not found",
cfg_fname);
return true;
}
perror("\nFailed to open config file");
return false;
}
have_config = true;
return true;
}
static bool read_mesh_config(void)
{
uint16_t range_l, range_h;
if (!mesh_db_load(cfg_fname)) {
l_error("Failed to load config from %s", cfg_fname);
return false;
}
local = l_new(struct meshcfg_node, 1);
if (!mesh_db_get_token(local->token.u8)) {
l_error("Failed to read the provisioner's token ID");
l_error("Check config file %s", cfg_fname);
l_free(local);
local = NULL;
return false;
}
l_info("Mesh configuration loaded from %s", cfg_fname);
if (mesh_db_get_addr_range(&range_l, &range_h)) {
low_addr = range_l;
high_addr = range_h;
}
iv_index = mesh_db_get_iv_index();
return true;
}
int main(int argc, char *argv[])
{
struct l_dbus_client *client;
uint32_t val;
int status;
bt_shell_init(argc, argv, &opt);
bt_shell_set_menu(&main_menu);
l_log_set_stderr();
if (address_opt && sscanf(address_opt, "%04x", &val) == 1)
low_addr = (uint16_t) val;
if (low_addr > DEFAULT_MAX_ADDRESS) {
l_error("Invalid start address");
bt_shell_cleanup();
return EXIT_FAILURE;
}
if (!low_addr)
low_addr = DEFAULT_START_ADDRESS;
if (range_opt && sscanf(address_opt, "%04x", &val) == 1) {
if (val == 0) {
l_error("Invalid address range");
bt_shell_cleanup();
return EXIT_FAILURE;
}
/* Inclusive */
high_addr = low_addr + val - 1;
}
if (!high_addr || high_addr > DEFAULT_MAX_ADDRESS)
high_addr = DEFAULT_MAX_ADDRESS;
if (net_idx_opt && sscanf(net_idx_opt, "%04x", &val) == 1)
prov_net_idx = (uint16_t) val;
else
prov_net_idx = DEFAULT_NET_INDEX;
if (!setup_cfg_storage()) {
bt_shell_cleanup();
return EXIT_FAILURE;
}
if (have_config && !read_mesh_config()) {
bt_shell_cleanup();
return EXIT_FAILURE;
}
bt_shell_set_prompt(PROMPT_OFF, NULL);
dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
l_dbus_set_ready_handler(dbus, ready_callback, NULL, NULL);
client = l_dbus_client_new(dbus, BLUEZ_MESH_NAME, "/org/bluez/mesh");
l_dbus_client_set_connect_handler(client, client_connected, NULL, NULL);
l_dbus_client_set_disconnect_handler(client, client_disconnected, NULL,
NULL);
l_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
property_changed, NULL, NULL);
l_dbus_client_set_ready_handler(client, client_ready, NULL, NULL);
node_proxies = l_queue_new();
devices = l_queue_new();
status = bt_shell_run();
l_dbus_client_destroy(client);
l_dbus_destroy(dbus);
cfgcli_cleanup();
return status;
}