mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-24 04:34:17 +08:00
551a0c80ab
When attaching a local provisioner node, always request own composition data to accommodate functional consolidation of regular and remote provisioning mechanisms. The knowledge of the own node composition is necessary for provisioning initiation and self configuration.
2672 lines
64 KiB
C
2672 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 COLOR_BLUE "[mesh-cfgclient]" COLOR_OFF "# "
|
|
#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;
|
|
|
|
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;
|
|
|
|
dev->last_seen = time(NULL);
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|