summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2019-02-11 18:36:27 +0200
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>2019-02-13 17:09:13 +0200
commit183d47c1760107fbe3958c3f7d63cc17cef745ae (patch)
treefeaf5bbc401f54378e632cfef81d8835d0509e8d /client
parent6d410a0e2e2e801e8555a42f799ede5787565bf7 (diff)
downloadbluez-183d47c1760107fbe3958c3f7d63cc17cef745ae.tar.gz
client: Add gatt.clone command
This adds clone command to gatt submenu which can be use to clone services as follow: Clone all services from the connected device: > gatt.clone Clone a given service > gatt.select-attribute <attribute/uuid> > gatt.clone In either case there is a prompt to confirm since this may add a lot of service the user must confim before proceding. Then finally: > gatt.register-application
Diffstat (limited to 'client')
-rw-r--r--client/gatt.c235
-rw-r--r--client/gatt.h1
-rw-r--r--client/main.c17
3 files changed, 253 insertions, 0 deletions
diff --git a/client/gatt.c b/client/gatt.c
index cbcff30da..6728b1c33 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -47,6 +47,7 @@
#include "gatt.h"
#define APP_PATH "/org/bluez/app"
+#define DEVICE_INTERFACE "org.bluez.Device1"
#define PROFILE_INTERFACE "org.bluez.GattProfile1"
#define SERVICE_INTERFACE "org.bluez.GattService1"
#define CHRC_INTERFACE "org.bluez.GattCharacteristic1"
@@ -72,6 +73,7 @@ struct desc {
struct chrc {
struct service *service;
+ GDBusProxy *proxy;
char *path;
uint16_t handle;
char *uuid;
@@ -89,6 +91,7 @@ struct chrc {
struct service {
DBusConnection *conn;
+ GDBusProxy *proxy;
char *path;
uint16_t handle;
char *uuid;
@@ -2653,3 +2656,235 @@ void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
+
+static GDBusProxy *select_service(GDBusProxy *proxy)
+{
+ GList *l;
+
+ for (l = services; l; l = g_list_next(l)) {
+ GDBusProxy *p = l->data;
+
+ if (proxy == p || g_str_has_prefix(g_dbus_proxy_get_path(proxy),
+ g_dbus_proxy_get_path(p)))
+ return p;
+ }
+
+ return NULL;
+}
+
+static void clone_chrc(struct GDBusProxy *proxy)
+{
+ struct service *service;
+ struct chrc *chrc;
+ DBusMessageIter iter;
+ DBusMessageIter array;
+ const char *uuid;
+ char **flags;
+ int i;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ if (g_dbus_proxy_get_property(proxy, "Flags", &iter) == FALSE)
+ return;
+
+ flags = g_new0(char *, dbus_message_iter_get_element_count(&iter) + 1);
+
+ dbus_message_iter_recurse(&iter, &array);
+
+ for (i = 0; dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING;
+ i++) {
+ const char *flag;
+
+ dbus_message_iter_get_basic(&array, &flag);
+
+ flags[i] = g_strdup(flag);
+
+ dbus_message_iter_next(&array);
+ }
+
+ service = g_list_last(local_services)->data;
+
+ chrc = g_new0(struct chrc, 1);
+ chrc->service = service;
+ chrc->proxy = proxy;
+ chrc->uuid = g_strdup(uuid);
+ chrc->path = g_strdup_printf("%s/chrc%u", service->path,
+ g_list_length(service->chrcs));
+ chrc->flags = flags;
+
+ if (g_dbus_register_interface(service->conn, chrc->path, CHRC_INTERFACE,
+ chrc_methods, NULL, chrc_properties,
+ chrc, chrc_free) == FALSE) {
+ bt_shell_printf("Failed to register characteristic object\n");
+ chrc_free(chrc);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ service->chrcs = g_list_append(service->chrcs, chrc);
+
+ print_chrc(chrc, COLORED_NEW);
+}
+
+static void clone_chrcs(struct GDBusProxy *proxy)
+{
+ GList *l;
+
+ for (l = characteristics; l; l = g_list_next(l)) {
+ GDBusProxy *p = l->data;
+
+ if (g_str_has_prefix(g_dbus_proxy_get_path(p),
+ g_dbus_proxy_get_path(proxy)))
+ clone_chrc(p);
+ }
+}
+
+static void clone_service(struct GDBusProxy *proxy)
+{
+ struct service *service;
+ DBusMessageIter iter;
+ const char *uuid;
+ dbus_bool_t primary;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &primary);
+
+ if (!strcmp(uuid, "00001800-0000-1000-8000-00805f9b34fb") ||
+ !strcmp(uuid, "00001801-0000-1000-8000-00805f9b34fb"))
+ return;
+
+ service = g_new0(struct service, 1);
+ service->conn = bt_shell_get_env("DBUS_CONNECTION");
+ service->proxy = proxy;
+ service->path = g_strdup_printf("%s/service%u", APP_PATH,
+ g_list_length(local_services));
+ service->uuid = g_strdup(uuid);
+ service->primary = primary;
+
+ if (g_dbus_register_interface(service->conn, service->path,
+ SERVICE_INTERFACE, NULL, NULL,
+ service_properties, service,
+ service_free) == FALSE) {
+ bt_shell_printf("Failed to register service object\n");
+ service_free(service);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print_service(service, COLORED_NEW);
+
+ local_services = g_list_append(local_services, service);
+
+ clone_chrcs(proxy);
+}
+
+static void clone_device(struct GDBusProxy *proxy)
+{
+ GList *l;
+
+ for (l = services; l; l = g_list_next(l)) {
+ struct GDBusProxy *p = l->data;
+
+ if (g_str_has_prefix(g_dbus_proxy_get_path(p),
+ g_dbus_proxy_get_path(proxy)))
+ clone_service(p);
+ }
+}
+
+static void service_clone(const char *input, void *user_data)
+{
+ struct GDBusProxy *proxy = user_data;
+
+ if (!strcmp(input, "yes"))
+ return clone_service(proxy);
+ else if (!strcmp(input, "no"))
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ else if (!strcmp(input, "all"))
+ return clone_device(proxy);
+
+ bt_shell_printf("Invalid option: %s\n", input);
+
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
+
+static void device_clone(const char *input, void *user_data)
+{
+ struct GDBusProxy *proxy = user_data;
+
+ if (!strcmp(input, "yes"))
+ return clone_device(proxy);
+ else if (!strcmp(input, "no"))
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+
+ bt_shell_printf("Invalid option: %s\n", input);
+
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
+
+static const char *proxy_get_name(struct GDBusProxy *proxy)
+{
+ DBusMessageIter iter;
+ const char *uuid;
+ const char *str;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return NULL;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ str = bt_uuidstr_to_str(uuid);
+
+ return str ? str : uuid;
+}
+
+static const char *proxy_get_alias(struct GDBusProxy *proxy)
+{
+ DBusMessageIter iter;
+ const char *alias;
+
+ if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == FALSE)
+ return NULL;
+
+ dbus_message_iter_get_basic(&iter, &alias);
+
+ return alias;
+}
+
+void gatt_clone_attribute(GDBusProxy *proxy, int argc, char *argv[])
+{
+ GDBusProxy *service = NULL;
+
+ if (argc > 1) {
+ proxy = gatt_select_attribute(proxy, argv[1]);
+ if (!proxy) {
+ bt_shell_printf("Unable to find attribute %s\n",
+ argv[1]);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+ }
+
+ if (!strcmp(g_dbus_proxy_get_interface(proxy), DEVICE_INTERFACE)) {
+ bt_shell_prompt_input(proxy_get_alias(proxy),
+ "Clone (yes/no):",
+ device_clone, proxy);
+ }
+
+ /* Only clone services */
+ service = select_service(proxy);
+ if (service) {
+ bt_shell_prompt_input(proxy_get_name(proxy),
+ "Clone (yes/no/all):",
+ service_clone, service);
+ return;
+ }
+
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+}
diff --git a/client/gatt.h b/client/gatt.h
index d4d7bedc6..09ca618d3 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -37,6 +37,7 @@ char *gatt_attribute_generator(const char *text, int state);
void gatt_read_attribute(GDBusProxy *proxy, int argc, char *argv[]);
void gatt_write_attribute(GDBusProxy *proxy, int argc, char *argv[]);
void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
+void gatt_clone_attribute(GDBusProxy *proxy, int argc, char *argv[]);
void gatt_acquire_write(GDBusProxy *proxy, const char *arg);
void gatt_release_write(GDBusProxy *proxy, const char *arg);
diff --git a/client/main.c b/client/main.c
index e91794504..1f6e04578 100644
--- a/client/main.c
+++ b/client/main.c
@@ -2105,6 +2105,19 @@ static void cmd_notify(int argc, char *argv[])
gatt_notify_attribute(default_attr, enable ? true : false);
}
+static void cmd_clone(int argc, char *argv[])
+{
+ GDBusProxy *proxy;
+
+ proxy = default_attr ? default_attr : default_dev;
+ if (!proxy) {
+ bt_shell_printf("Not connected\n");
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+
+ gatt_clone_attribute(proxy, argc, argv);
+}
+
static void cmd_register_app(int argc, char *argv[])
{
if (check_default_ctrl() == FALSE)
@@ -2624,6 +2637,8 @@ static const struct bt_shell_menu gatt_menu = {
"Release Notify file descriptor" },
{ "notify", "<on/off>", cmd_notify, "Notify attribute value",
NULL },
+ { "clone", "[dev/attribute/UUID]", cmd_clone,
+ "Clone a device or attribute" },
{ "register-application", "[UUID ...]", cmd_register_app,
"Register profile to connect" },
{ "unregister-application", NULL, cmd_unregister_app,
@@ -2757,6 +2772,8 @@ int main(int argc, char *argv[])
dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
g_dbus_attach_object_manager(dbus_conn);
+ bt_shell_set_env("DBUS_CONNECTION", dbus_conn);
+
client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
g_dbus_client_set_connect_watch(client, connect_handler, NULL);