1 // SPDX-License-Identifier: GPL-2.0-or-later
4 * BlueZ - Bluetooth protocol stack for Linux
6 * Copyright (C) 2014 Intel Corporation. All rights reserved.
23 #include <sys/types.h>
24 #include <sys/socket.h>
28 #include "src/shared/util.h"
29 #include "src/shared/queue.h"
30 #include "src/shared/io.h"
31 #include "src/shared/shell.h"
32 #include "gdbus/gdbus.h"
35 #define APP_PATH "/org/bluez/app"
36 #define DEVICE_INTERFACE "org.bluez.Device1"
37 #define PROFILE_INTERFACE "org.bluez.GattProfile1"
38 #define SERVICE_INTERFACE "org.bluez.GattService1"
39 #define CHRC_INTERFACE "org.bluez.GattCharacteristic1"
40 #define DESC_INTERFACE "org.bluez.GattDescriptor1"
42 /* String display constants */
43 #define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
44 #define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
45 #define COLORED_DEL COLOR_RED "DEL" COLOR_OFF
47 #define MAX_ATTR_VAL_LEN 512
56 unsigned int max_val_len;
61 struct service *service;
70 unsigned int max_val_len;
75 bool authorization_req;
89 static GList *local_services;
90 static GList *services;
91 static GList *characteristics;
92 static GList *descriptors;
93 static GList *managers;
95 static DBusMessage *pending_message = NULL;
103 static struct sock_io write_io;
104 static struct sock_io notify_io;
106 static void print_service(struct service *service, const char *description)
110 text = bt_uuidstr_to_str(service->uuid);
112 bt_shell_printf("%s%s%s%s Service (Handle 0x%04x)\n\t%s\n\t"
114 description ? "[" : "",
116 description ? "] " : "",
117 service->primary ? "Primary" :
119 service->handle, service->path,
122 bt_shell_printf("%s%s%s%s Service (Handle 0x%04x)\n\t%s\n\t%s"
124 description ? "[" : "",
126 description ? "] " : "",
127 service->primary ? "Primary" :
129 service->handle, service->path,
130 service->uuid, text);
133 static void print_inc_service(struct service *service, const char *description)
137 text = bt_uuidstr_to_str(service->uuid);
139 bt_shell_printf("%s%s%s%s Included Service (Handle 0x%04x)\n\t"
141 description ? "[" : "",
143 description ? "] " : "",
144 service->primary ? "Primary" :
146 service->handle, service->path,
149 bt_shell_printf("%s%s%s%s Included Service (Handle 0x%04x)\n\t"
151 description ? "[" : "",
153 description ? "] " : "",
154 service->primary ? "Primary" :
156 service->handle, service->path,
157 service->uuid, text);
160 static void print_service_proxy(GDBusProxy *proxy, const char *description)
162 struct service service;
163 DBusMessageIter iter;
167 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
170 dbus_message_iter_get_basic(&iter, &uuid);
172 if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE)
175 dbus_message_iter_get_basic(&iter, &primary);
177 service.path = (char *) g_dbus_proxy_get_path(proxy);
178 service.uuid = (char *) uuid;
179 service.primary = primary;
181 print_service(&service, description);
184 void gatt_add_service(GDBusProxy *proxy)
186 services = g_list_append(services, proxy);
188 print_service_proxy(proxy, COLORED_NEW);
191 static struct service *remove_service_by_proxy(struct GDBusProxy *proxy)
195 for (l = local_services; l; l = g_list_next(l)) {
196 struct service *service = l->data;
198 if (service->proxy == proxy) {
199 local_services = g_list_delete_link(local_services, l);
207 void gatt_remove_service(GDBusProxy *proxy)
209 struct service *service;
212 l = g_list_find(services, proxy);
216 services = g_list_delete_link(services, l);
218 print_service_proxy(proxy, COLORED_DEL);
220 service = remove_service_by_proxy(proxy);
222 g_dbus_unregister_interface(service->conn, service->path,
226 static void print_chrc(struct chrc *chrc, const char *description)
230 text = bt_uuidstr_to_str(chrc->uuid);
232 bt_shell_printf("%s%s%sCharacteristic (Handle 0x%04x)\n\t%s\n\t"
234 description ? "[" : "",
236 description ? "] " : "",
237 chrc->handle, chrc->path, chrc->uuid);
239 bt_shell_printf("%s%s%sCharacteristic (Handle 0x%04x)\n\t%s\n\t"
241 description ? "[" : "",
243 description ? "] " : "",
244 chrc->handle, chrc->path, chrc->uuid,
248 static void print_characteristic(GDBusProxy *proxy, const char *description)
251 DBusMessageIter iter;
254 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
257 dbus_message_iter_get_basic(&iter, &uuid);
259 chrc.path = (char *) g_dbus_proxy_get_path(proxy);
260 chrc.uuid = (char *) uuid;
262 print_chrc(&chrc, description);
265 static gboolean chrc_is_child(GDBusProxy *characteristic)
267 DBusMessageIter iter;
270 if (!g_dbus_proxy_get_property(characteristic, "Service", &iter))
273 dbus_message_iter_get_basic(&iter, &service);
275 return g_dbus_proxy_lookup(services, NULL, service,
276 "org.bluez.GattService1") != NULL;
279 void gatt_add_characteristic(GDBusProxy *proxy)
281 if (!chrc_is_child(proxy))
284 characteristics = g_list_append(characteristics, proxy);
286 print_characteristic(proxy, COLORED_NEW);
289 static void notify_io_destroy(void)
291 io_destroy(notify_io.io);
292 memset(¬ify_io, 0, sizeof(notify_io));
295 static void write_io_destroy(void)
297 io_destroy(write_io.io);
298 memset(&write_io, 0, sizeof(write_io));
301 void gatt_remove_characteristic(GDBusProxy *proxy)
305 l = g_list_find(characteristics, proxy);
309 characteristics = g_list_delete_link(characteristics, l);
311 print_characteristic(proxy, COLORED_DEL);
313 if (write_io.proxy == proxy)
315 else if (notify_io.proxy == proxy)
319 static void print_desc(struct desc *desc, const char *description)
323 text = bt_uuidstr_to_str(desc->uuid);
325 bt_shell_printf("%s%s%sDescriptor (Handle 0x%04x)\n\t%s\n\t"
327 description ? "[" : "",
329 description ? "] " : "",
330 desc->handle, desc->path, desc->uuid);
332 bt_shell_printf("%s%s%sDescriptor (Handle 0x%04x)\n\t%s\n\t"
334 description ? "[" : "",
336 description ? "] " : "",
337 desc->handle, desc->path, desc->uuid,
341 static void print_descriptor(GDBusProxy *proxy, const char *description)
344 DBusMessageIter iter;
347 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
350 dbus_message_iter_get_basic(&iter, &uuid);
352 desc.path = (char *) g_dbus_proxy_get_path(proxy);
353 desc.uuid = (char *) uuid;
355 print_desc(&desc, description);
358 static gboolean descriptor_is_child(GDBusProxy *characteristic)
361 DBusMessageIter iter;
362 const char *service, *path;
364 if (!g_dbus_proxy_get_property(characteristic, "Characteristic", &iter))
367 dbus_message_iter_get_basic(&iter, &service);
369 for (l = characteristics; l; l = g_list_next(l)) {
370 GDBusProxy *proxy = l->data;
372 path = g_dbus_proxy_get_path(proxy);
374 if (!strcmp(path, service))
381 void gatt_add_descriptor(GDBusProxy *proxy)
383 if (!descriptor_is_child(proxy))
386 descriptors = g_list_append(descriptors, proxy);
388 print_descriptor(proxy, COLORED_NEW);
391 void gatt_remove_descriptor(GDBusProxy *proxy)
395 l = g_list_find(descriptors, proxy);
399 descriptors = g_list_delete_link(descriptors, l);
401 print_descriptor(proxy, COLORED_DEL);
404 static void list_attributes(const char *path, GList *source)
408 for (l = source; l; l = g_list_next(l)) {
409 GDBusProxy *proxy = l->data;
410 const char *proxy_path;
412 proxy_path = g_dbus_proxy_get_path(proxy);
414 if (!g_str_has_prefix(proxy_path, path))
417 if (source == services) {
418 print_service_proxy(proxy, NULL);
419 list_attributes(proxy_path, characteristics);
420 } else if (source == characteristics) {
421 print_characteristic(proxy, NULL);
422 list_attributes(proxy_path, descriptors);
423 } else if (source == descriptors)
424 print_descriptor(proxy, NULL);
428 static void list_descs(GList *descs)
432 for (l = descs; l; l = g_list_next(l)) {
433 struct desc *desc = l->data;
435 print_desc(desc, NULL);
439 static void list_chrcs(GList *chrcs)
443 for (l = chrcs; l; l = g_list_next(l)) {
444 struct chrc *chrc = l->data;
446 print_chrc(chrc, NULL);
448 list_descs(chrc->descs);
452 static void list_services(void)
456 for (l = local_services; l; l = g_list_next(l)) {
457 struct service *service = l->data;
459 print_service(service, NULL);
461 list_chrcs(service->chrcs);
465 void gatt_list_attributes(const char *path)
467 if (path && !strcmp(path, "local")) {
469 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
472 list_attributes(path, services);
473 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
476 static GDBusProxy *select_attribute(const char *path)
480 proxy = g_dbus_proxy_lookup(services, NULL, path,
481 "org.bluez.GattService1");
485 proxy = g_dbus_proxy_lookup(characteristics, NULL, path,
486 "org.bluez.GattCharacteristic1");
490 return g_dbus_proxy_lookup(descriptors, NULL, path,
491 "org.bluez.GattDescriptor1");
494 static GDBusProxy *select_proxy_by_uuid(GDBusProxy *parent, const char *uuid,
499 DBusMessageIter iter;
501 for (l = source; l; l = g_list_next(l)) {
502 GDBusProxy *proxy = l->data;
504 if (parent && !g_str_has_prefix(g_dbus_proxy_get_path(proxy),
505 g_dbus_proxy_get_path(parent)))
508 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
511 dbus_message_iter_get_basic(&iter, &value);
513 if (strcasecmp(uuid, value) == 0)
516 if (strlen(uuid) == 4 && !strncasecmp(value + 4, uuid, 4))
523 static GDBusProxy *select_attribute_by_uuid(GDBusProxy *parent,
528 proxy = select_proxy_by_uuid(parent, uuid, services);
532 proxy = select_proxy_by_uuid(parent, uuid, characteristics);
536 return select_proxy_by_uuid(parent, uuid, descriptors);
539 GDBusProxy *gatt_select_attribute(GDBusProxy *parent, const char *arg)
542 return select_attribute(arg);
545 GDBusProxy *proxy = select_attribute_by_uuid(parent, arg);
550 return select_attribute_by_uuid(NULL, arg);
553 static char *attribute_generator(const char *text, int state, GList *source)
561 return g_dbus_proxy_path_lookup(source, &index, text);
564 char *gatt_attribute_generator(const char *text, int state)
566 static GList *list = NULL;
576 list1 = g_list_copy(characteristics);
577 list1 = g_list_concat(list1, g_list_copy(descriptors));
579 list = g_list_copy(services);
580 list = g_list_concat(list, list1);
583 return attribute_generator(text, state, list);
586 static void read_reply(DBusMessage *message, void *user_data)
589 DBusMessageIter iter, array;
593 dbus_error_init(&error);
595 if (dbus_set_error_from_message(&error, message) == TRUE) {
596 bt_shell_printf("Failed to read: %s\n", error.name);
597 dbus_error_free(&error);
598 return bt_shell_noninteractive_quit(EXIT_FAILURE);
601 dbus_message_iter_init(message, &iter);
603 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
604 bt_shell_printf("Invalid response to read\n");
605 return bt_shell_noninteractive_quit(EXIT_FAILURE);
608 dbus_message_iter_recurse(&iter, &array);
609 dbus_message_iter_get_fixed_array(&array, &value, &len);
612 bt_shell_printf("Unable to parse value\n");
613 return bt_shell_noninteractive_quit(EXIT_FAILURE);
616 bt_shell_hexdump(value, len);
618 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
621 static void read_setup(DBusMessageIter *iter, void *user_data)
623 DBusMessageIter dict;
624 uint16_t *offset = user_data;
626 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
627 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
628 DBUS_TYPE_STRING_AS_STRING
629 DBUS_TYPE_VARIANT_AS_STRING
630 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
633 g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16, offset);
635 dbus_message_iter_close_container(iter, &dict);
638 static void read_attribute(GDBusProxy *proxy, uint16_t offset)
640 if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup, read_reply,
641 &offset, NULL) == FALSE) {
642 bt_shell_printf("Failed to read\n");
643 return bt_shell_noninteractive_quit(EXIT_FAILURE);
646 bt_shell_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
649 void gatt_read_attribute(GDBusProxy *proxy, int argc, char *argv[])
654 iface = g_dbus_proxy_get_interface(proxy);
655 if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
656 !strcmp(iface, "org.bluez.GattDescriptor1")) {
659 offset = atoi(argv[1]);
661 read_attribute(proxy, offset);
665 bt_shell_printf("Unable to read attribute %s\n",
666 g_dbus_proxy_get_path(proxy));
667 return bt_shell_noninteractive_quit(EXIT_FAILURE);
670 static void write_reply(DBusMessage *message, void *user_data)
674 dbus_error_init(&error);
676 if (dbus_set_error_from_message(&error, message) == TRUE) {
677 bt_shell_printf("Failed to write: %s\n", error.name);
678 dbus_error_free(&error);
679 return bt_shell_noninteractive_quit(EXIT_FAILURE);
682 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
685 struct write_attribute_data {
692 static void write_setup(DBusMessageIter *iter, void *user_data)
694 struct write_attribute_data *wd = user_data;
695 DBusMessageIter array, dict;
697 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
698 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
701 dbus_message_iter_close_container(iter, &array);
703 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
704 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
705 DBUS_TYPE_STRING_AS_STRING
706 DBUS_TYPE_VARIANT_AS_STRING
707 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
711 g_dbus_dict_append_entry(&dict, "type", DBUS_TYPE_STRING,
714 g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16,
717 dbus_message_iter_close_container(iter, &dict);
720 static int sock_send(struct io *io, struct iovec *iov, size_t iovlen)
725 memset(&msg, 0, sizeof(msg));
727 msg.msg_iovlen = iovlen;
729 ret = sendmsg(io_get_fd(io), &msg, MSG_NOSIGNAL);
732 bt_shell_printf("sendmsg: %s", strerror(-ret));
738 static void write_attribute(GDBusProxy *proxy,
739 struct write_attribute_data *data)
741 /* Write using the fd if it has been acquired and fit the MTU */
742 if (proxy == write_io.proxy &&
743 (write_io.io && write_io.mtu >= data->iov.iov_len)) {
744 bt_shell_printf("Attempting to write fd %d\n",
745 io_get_fd(write_io.io));
746 if (sock_send(write_io.io, &data->iov, 1) < 0) {
747 bt_shell_printf("Failed to write: %s", strerror(errno));
748 return bt_shell_noninteractive_quit(EXIT_FAILURE);
753 if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
754 write_reply, data, NULL) == FALSE) {
755 bt_shell_printf("Failed to write\n");
756 return bt_shell_noninteractive_quit(EXIT_FAILURE);
759 bt_shell_printf("Attempting to write %s\n",
760 g_dbus_proxy_get_path(proxy));
763 static uint8_t *str2bytearray(char *arg, size_t *val_len)
765 uint8_t value[MAX_ATTR_VAL_LEN];
769 for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) {
776 if (i >= G_N_ELEMENTS(value)) {
777 bt_shell_printf("Too much data\n");
781 val = strtol(entry, &endptr, 0);
782 if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
783 bt_shell_printf("Invalid value at index %d\n", i);
792 return g_memdup(value, i);
795 void gatt_write_attribute(GDBusProxy *proxy, int argc, char *argv[])
798 struct write_attribute_data data;
800 memset(&data, 0, sizeof(data));
802 iface = g_dbus_proxy_get_interface(proxy);
803 if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
804 !strcmp(iface, "org.bluez.GattDescriptor1")) {
805 data.iov.iov_base = str2bytearray(argv[1], &data.iov.iov_len);
808 data.offset = atoi(argv[2]);
813 write_attribute(proxy, &data);
817 bt_shell_printf("Unable to write attribute %s\n",
818 g_dbus_proxy_get_path(proxy));
820 return bt_shell_noninteractive_quit(EXIT_FAILURE);
823 static bool sock_read(struct io *io, void *user_data)
825 struct chrc *chrc = user_data;
828 uint8_t buf[MAX_ATTR_VAL_LEN];
829 int fd = io_get_fd(io);
832 if (io != notify_io.io && !chrc)
836 iov.iov_len = sizeof(buf);
838 memset(&msg, 0, sizeof(msg));
842 bytes_read = recvmsg(fd, &msg, MSG_DONTWAIT);
843 if (bytes_read < 0) {
844 bt_shell_printf("recvmsg: %s", strerror(errno));
852 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) "
853 "written:\n", chrc->path,
854 bt_uuidstr_to_str(chrc->uuid));
856 bt_shell_printf("[" COLORED_CHG "] %s Notification:\n",
857 g_dbus_proxy_get_path(notify_io.proxy));
859 bt_shell_hexdump(buf, bytes_read);
864 static bool sock_hup(struct io *io, void *user_data)
866 struct chrc *chrc = user_data;
869 bt_shell_printf("Attribute %s %s sock closed\n", chrc->path,
870 io == chrc->write_io ? "Write" : "Notify");
872 if (io == chrc->write_io) {
873 io_destroy(chrc->write_io);
874 chrc->write_io = NULL;
876 io_destroy(chrc->notify_io);
877 chrc->notify_io = NULL;
883 bt_shell_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write");
885 if (io == notify_io.io)
893 static struct io *sock_io_new(int fd, void *user_data)
899 io_set_close_on_destroy(io, true);
901 io_set_read_handler(io, sock_read, user_data, NULL);
903 io_set_disconnect_handler(io, sock_hup, user_data, NULL);
908 static void acquire_write_reply(DBusMessage *message, void *user_data)
913 dbus_error_init(&error);
915 if (dbus_set_error_from_message(&error, message) == TRUE) {
916 bt_shell_printf("Failed to acquire write: %s\n", error.name);
917 dbus_error_free(&error);
918 write_io.proxy = NULL;
919 return bt_shell_noninteractive_quit(EXIT_FAILURE);
925 if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
926 DBUS_TYPE_UINT16, &write_io.mtu,
927 DBUS_TYPE_INVALID) == false)) {
928 bt_shell_printf("Invalid AcquireWrite response\n");
929 return bt_shell_noninteractive_quit(EXIT_FAILURE);
932 bt_shell_printf("AcquireWrite success: fd %d MTU %u\n", fd,
935 write_io.io = sock_io_new(fd, NULL);
936 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
939 static void acquire_setup(DBusMessageIter *iter, void *user_data)
941 DBusMessageIter dict;
943 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
944 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
945 DBUS_TYPE_STRING_AS_STRING
946 DBUS_TYPE_VARIANT_AS_STRING
947 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
950 dbus_message_iter_close_container(iter, &dict);
953 void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
957 iface = g_dbus_proxy_get_interface(proxy);
958 if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
959 bt_shell_printf("Unable to acquire write: %s not a"
961 g_dbus_proxy_get_path(proxy));
962 return bt_shell_noninteractive_quit(EXIT_FAILURE);
965 if (g_dbus_proxy_method_call(proxy, "AcquireWrite", acquire_setup,
966 acquire_write_reply, NULL, NULL) == FALSE) {
967 bt_shell_printf("Failed to AcquireWrite\n");
968 return bt_shell_noninteractive_quit(EXIT_FAILURE);
971 write_io.proxy = proxy;
974 void gatt_release_write(GDBusProxy *proxy, const char *arg)
976 if (proxy != write_io.proxy || !write_io.io) {
977 bt_shell_printf("Write not acquired\n");
978 return bt_shell_noninteractive_quit(EXIT_FAILURE);
983 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
986 static void acquire_notify_reply(DBusMessage *message, void *user_data)
991 dbus_error_init(&error);
993 if (dbus_set_error_from_message(&error, message) == TRUE) {
994 bt_shell_printf("Failed to acquire notify: %s\n", error.name);
995 dbus_error_free(&error);
996 write_io.proxy = NULL;
997 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1001 io_destroy(notify_io.io);
1002 notify_io.io = NULL;
1007 if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
1008 DBUS_TYPE_UINT16, ¬ify_io.mtu,
1009 DBUS_TYPE_INVALID) == false)) {
1010 bt_shell_printf("Invalid AcquireNotify response\n");
1011 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1014 bt_shell_printf("AcquireNotify success: fd %d MTU %u\n", fd,
1017 notify_io.io = sock_io_new(fd, NULL);
1019 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1022 void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
1026 iface = g_dbus_proxy_get_interface(proxy);
1027 if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
1028 bt_shell_printf("Unable to acquire notify: %s not a"
1029 " characteristic\n",
1030 g_dbus_proxy_get_path(proxy));
1031 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1034 if (g_dbus_proxy_method_call(proxy, "AcquireNotify", acquire_setup,
1035 acquire_notify_reply, NULL, NULL) == FALSE) {
1036 bt_shell_printf("Failed to AcquireNotify\n");
1037 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1040 notify_io.proxy = proxy;
1043 void gatt_release_notify(GDBusProxy *proxy, const char *arg)
1045 if (proxy != notify_io.proxy || !notify_io.io) {
1046 bt_shell_printf("Notify not acquired\n");
1047 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1050 notify_io_destroy();
1052 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1055 static void notify_reply(DBusMessage *message, void *user_data)
1057 bool enable = GPOINTER_TO_UINT(user_data);
1060 dbus_error_init(&error);
1062 if (dbus_set_error_from_message(&error, message) == TRUE) {
1063 bt_shell_printf("Failed to %s notify: %s\n",
1064 enable ? "start" : "stop", error.name);
1065 dbus_error_free(&error);
1066 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1069 bt_shell_printf("Notify %s\n", enable == TRUE ? "started" : "stopped");
1071 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1074 static void notify_attribute(GDBusProxy *proxy, bool enable)
1079 method = "StartNotify";
1081 method = "StopNotify";
1083 if (g_dbus_proxy_method_call(proxy, method, NULL, notify_reply,
1084 GUINT_TO_POINTER(enable), NULL) == FALSE) {
1085 bt_shell_printf("Failed to %s notify\n",
1086 enable ? "start" : "stop");
1087 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1090 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1093 void gatt_notify_attribute(GDBusProxy *proxy, bool enable)
1097 iface = g_dbus_proxy_get_interface(proxy);
1098 if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
1099 notify_attribute(proxy, enable);
1103 bt_shell_printf("Unable to notify attribute %s\n",
1104 g_dbus_proxy_get_path(proxy));
1106 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1109 static void register_app_setup(DBusMessageIter *iter, void *user_data)
1111 DBusMessageIter opt;
1112 const char *path = "/";
1114 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
1116 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1117 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1118 DBUS_TYPE_STRING_AS_STRING
1119 DBUS_TYPE_VARIANT_AS_STRING
1120 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1122 dbus_message_iter_close_container(iter, &opt);
1126 static void register_app_reply(DBusMessage *message, void *user_data)
1130 dbus_error_init(&error);
1132 if (dbus_set_error_from_message(&error, message) == TRUE) {
1133 bt_shell_printf("Failed to register application: %s\n",
1135 dbus_error_free(&error);
1136 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1139 bt_shell_printf("Application registered\n");
1141 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1144 void gatt_add_manager(GDBusProxy *proxy)
1146 managers = g_list_append(managers, proxy);
1149 void gatt_remove_manager(GDBusProxy *proxy)
1151 managers = g_list_remove(managers, proxy);
1154 static int match_proxy(const void *a, const void *b)
1156 GDBusProxy *proxy1 = (void *) a;
1157 GDBusProxy *proxy2 = (void *) b;
1159 return strcmp(g_dbus_proxy_get_path(proxy1),
1160 g_dbus_proxy_get_path(proxy2));
1163 static DBusMessage *release_profile(DBusConnection *conn,
1164 DBusMessage *msg, void *user_data)
1166 g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
1168 return dbus_message_new_method_return(msg);
1171 static const GDBusMethodTable methods[] = {
1172 { GDBUS_METHOD("Release", NULL, NULL, release_profile) },
1176 static gboolean get_uuids(const GDBusPropertyTable *property,
1177 DBusMessageIter *iter, void *data)
1179 DBusMessageIter entry;
1182 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1183 DBUS_TYPE_STRING_AS_STRING, &entry);
1185 for (uuid = uuids; uuid; uuid = g_list_next(uuid->next))
1186 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
1189 dbus_message_iter_close_container(iter, &entry);
1194 static const GDBusPropertyTable properties[] = {
1195 { "UUIDs", "as", get_uuids },
1199 void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy,
1200 int argc, char *argv[])
1205 l = g_list_find_custom(managers, proxy, match_proxy);
1207 bt_shell_printf("Unable to find GattManager proxy\n");
1208 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1211 for (i = 0; i < argc; i++)
1212 uuids = g_list_append(uuids, g_strdup(argv[i]));
1215 if (g_dbus_register_interface(conn, APP_PATH,
1216 PROFILE_INTERFACE, methods,
1217 NULL, properties, NULL,
1219 bt_shell_printf("Failed to register application"
1221 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1225 if (g_dbus_proxy_method_call(l->data, "RegisterApplication",
1227 register_app_reply, NULL,
1229 bt_shell_printf("Failed register application\n");
1230 g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
1231 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1235 static void unregister_app_reply(DBusMessage *message, void *user_data)
1237 DBusConnection *conn = user_data;
1240 dbus_error_init(&error);
1242 if (dbus_set_error_from_message(&error, message) == TRUE) {
1243 bt_shell_printf("Failed to unregister application: %s\n",
1245 dbus_error_free(&error);
1246 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1249 bt_shell_printf("Application unregistered\n");
1252 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1254 g_list_free_full(uuids, g_free);
1257 g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
1259 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1262 static void unregister_app_setup(DBusMessageIter *iter, void *user_data)
1264 const char *path = "/";
1266 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
1269 void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy)
1273 l = g_list_find_custom(managers, proxy, match_proxy);
1275 bt_shell_printf("Unable to find GattManager proxy\n");
1276 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1279 if (g_dbus_proxy_method_call(l->data, "UnregisterApplication",
1280 unregister_app_setup,
1281 unregister_app_reply, conn,
1283 bt_shell_printf("Failed unregister profile\n");
1284 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1288 static void desc_free(void *data)
1290 struct desc *desc = data;
1294 g_strfreev(desc->flags);
1295 g_free(desc->value);
1299 static void desc_unregister(void *data)
1301 struct desc *desc = data;
1303 print_desc(desc, COLORED_DEL);
1305 g_dbus_unregister_interface(desc->chrc->service->conn, desc->path,
1309 static void chrc_free(void *data)
1311 struct chrc *chrc = data;
1313 g_list_free_full(chrc->descs, desc_unregister);
1316 g_strfreev(chrc->flags);
1317 g_free(chrc->value);
1321 static void chrc_unregister(void *data)
1323 struct chrc *chrc = data;
1325 print_chrc(chrc, COLORED_DEL);
1327 g_dbus_unregister_interface(chrc->service->conn, chrc->path,
1331 static void inc_unregister(void *data)
1338 static void service_free(void *data)
1340 struct service *service = data;
1342 g_list_free_full(service->chrcs, chrc_unregister);
1343 g_list_free_full(service->inc, inc_unregister);
1344 g_free(service->path);
1345 g_free(service->uuid);
1349 static gboolean service_get_handle(const GDBusPropertyTable *property,
1350 DBusMessageIter *iter, void *data)
1352 struct service *service = data;
1354 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
1360 static void service_set_handle(const GDBusPropertyTable *property,
1361 DBusMessageIter *value, GDBusPendingPropertySet id,
1364 struct service *service = data;
1366 if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) {
1367 g_dbus_pending_property_error(id, "org.bluez.InvalidArguments",
1368 "Invalid arguments in method call");
1372 dbus_message_iter_get_basic(value, &service->handle);
1374 print_service(service, COLORED_CHG);
1376 g_dbus_pending_property_success(id);
1379 static gboolean service_get_uuid(const GDBusPropertyTable *property,
1380 DBusMessageIter *iter, void *data)
1382 struct service *service = data;
1384 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &service->uuid);
1389 static gboolean service_get_primary(const GDBusPropertyTable *property,
1390 DBusMessageIter *iter, void *data)
1392 struct service *service = data;
1393 dbus_bool_t primary;
1395 primary = service->primary ? TRUE : FALSE;
1397 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary);
1403 static gboolean service_get_includes(const GDBusPropertyTable *property,
1404 DBusMessageIter *iter, void *data)
1406 DBusMessageIter array;
1407 struct service *service = data;
1412 for (l = service->inc ; l; l = g_list_next(l)) {
1415 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1416 DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
1418 dbus_message_iter_append_basic(&array,
1419 DBUS_TYPE_OBJECT_PATH, &inc);
1423 dbus_message_iter_close_container(iter, &array);
1432 static gboolean service_exist_includes(const GDBusPropertyTable *property,
1435 struct service *service = data;
1445 static const GDBusPropertyTable service_properties[] = {
1446 { "Handle", "q", service_get_handle, service_set_handle },
1447 { "UUID", "s", service_get_uuid },
1448 { "Primary", "b", service_get_primary },
1449 { "Includes", "ao", service_get_includes,
1450 NULL, service_exist_includes },
1454 static void service_set_primary(const char *input, void *user_data)
1456 struct service *service = user_data;
1458 if (!strcmp(input, "yes"))
1459 service->primary = true;
1460 else if (!strcmp(input, "no")) {
1461 service->primary = false;
1463 bt_shell_printf("Invalid option: %s\n", input);
1464 local_services = g_list_remove(local_services, service);
1465 print_service(service, COLORED_DEL);
1466 g_dbus_unregister_interface(service->conn, service->path,
1471 void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
1472 int argc, char *argv[])
1474 struct service *service;
1475 bool primary = true;
1477 service = g_new0(struct service, 1);
1478 service->conn = conn;
1479 service->uuid = g_strdup(argv[1]);
1480 service->path = g_strdup_printf("%s/service%u", APP_PATH,
1481 g_list_length(local_services));
1482 service->primary = primary;
1485 service->handle = atoi(argv[2]);
1487 if (g_dbus_register_interface(conn, service->path,
1488 SERVICE_INTERFACE, NULL, NULL,
1489 service_properties, service,
1490 service_free) == FALSE) {
1491 bt_shell_printf("Failed to register service object\n");
1492 service_free(service);
1493 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1496 print_service(service, COLORED_NEW);
1498 local_services = g_list_append(local_services, service);
1500 bt_shell_prompt_input(service->path, "Primary (yes/no):",
1501 service_set_primary, service);
1503 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1506 static struct service *service_find(const char *pattern)
1510 for (l = local_services; l; l = g_list_next(l)) {
1511 struct service *service = l->data;
1513 /* match object path */
1514 if (!strcmp(service->path, pattern))
1518 if (!strcmp(service->uuid, pattern))
1525 void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
1526 int argc, char *argv[])
1528 struct service *service;
1530 service = service_find(argv[1]);
1532 bt_shell_printf("Failed to unregister service object\n");
1533 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1536 local_services = g_list_remove(local_services, service);
1538 print_service(service, COLORED_DEL);
1540 g_dbus_unregister_interface(service->conn, service->path,
1543 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1546 static char *inc_find(struct service *serv, char *path)
1550 for (lc = serv->inc; lc; lc = g_list_next(lc)) {
1551 char *incp = lc->data;
1552 /* match object path */
1553 if (!strcmp(incp, path))
1560 void gatt_register_include(DBusConnection *conn, GDBusProxy *proxy,
1561 int argc, char *argv[])
1563 struct service *service, *inc_service;
1566 if (!local_services) {
1567 bt_shell_printf("No service registered\n");
1568 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1571 service = g_list_last(local_services)->data;
1574 inc_service = service_find(argv[1]);
1576 bt_shell_printf("Failed to find service object\n");
1577 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1580 inc_path = g_strdup(service->path);
1582 inc_service->inc = g_list_append(inc_service->inc, inc_path);
1584 print_service(inc_service, COLORED_NEW);
1585 print_inc_service(service, COLORED_NEW);
1587 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1590 void gatt_unregister_include(DBusConnection *conn, GDBusProxy *proxy,
1591 int argc, char *argv[])
1593 struct service *ser_inc, *service;
1596 service = service_find(argv[1]);
1598 bt_shell_printf("Failed to unregister include service"
1600 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1603 ser_inc = service_find(argv[2]);
1605 bt_shell_printf("Failed to find include service object\n");
1606 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1609 path = inc_find(service, ser_inc->path);
1611 service->inc = g_list_remove(service->inc, path);
1612 inc_unregister(path);
1615 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1618 static gboolean chrc_get_handle(const GDBusPropertyTable *property,
1619 DBusMessageIter *iter, void *data)
1621 struct chrc *chrc = data;
1623 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &chrc->handle);
1628 static void chrc_set_handle(const GDBusPropertyTable *property,
1629 DBusMessageIter *value, GDBusPendingPropertySet id,
1632 struct chrc *chrc = data;
1634 if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) {
1635 g_dbus_pending_property_error(id, "org.bluez.InvalidArguments",
1636 "Invalid arguments in method call");
1640 dbus_message_iter_get_basic(value, &chrc->handle);
1642 print_chrc(chrc, COLORED_CHG);
1644 g_dbus_pending_property_success(id);
1647 static gboolean chrc_get_uuid(const GDBusPropertyTable *property,
1648 DBusMessageIter *iter, void *data)
1650 struct chrc *chrc = data;
1652 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chrc->uuid);
1657 static gboolean chrc_get_service(const GDBusPropertyTable *property,
1658 DBusMessageIter *iter, void *data)
1660 struct chrc *chrc = data;
1662 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
1663 &chrc->service->path);
1668 static gboolean chrc_get_value(const GDBusPropertyTable *property,
1669 DBusMessageIter *iter, void *data)
1671 struct chrc *chrc = data;
1672 DBusMessageIter array;
1674 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
1676 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
1677 &chrc->value, chrc->value_len);
1679 dbus_message_iter_close_container(iter, &array);
1684 static gboolean chrc_get_notifying(const GDBusPropertyTable *property,
1685 DBusMessageIter *iter, void *data)
1687 struct chrc *chrc = data;
1690 value = chrc->notifying ? TRUE : FALSE;
1692 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1697 static gboolean chrc_get_flags(const GDBusPropertyTable *property,
1698 DBusMessageIter *iter, void *data)
1700 struct chrc *chrc = data;
1702 DBusMessageIter array;
1704 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
1706 for (i = 0; chrc->flags[i]; i++)
1707 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
1710 dbus_message_iter_close_container(iter, &array);
1715 static gboolean chrc_get_write_acquired(const GDBusPropertyTable *property,
1716 DBusMessageIter *iter, void *data)
1718 struct chrc *chrc = data;
1721 value = chrc->write_io ? TRUE : FALSE;
1723 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1728 static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property,
1731 struct chrc *chrc = data;
1737 for (i = 0; chrc->flags[i]; i++) {
1738 if (!strcmp("write-without-response", chrc->flags[i]))
1745 static gboolean chrc_get_notify_acquired(const GDBusPropertyTable *property,
1746 DBusMessageIter *iter, void *data)
1748 struct chrc *chrc = data;
1751 value = chrc->notify_io ? TRUE : FALSE;
1753 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1758 static gboolean chrc_notify_acquired_exists(const GDBusPropertyTable *property,
1761 struct chrc *chrc = data;
1767 for (i = 0; chrc->flags[i]; i++) {
1768 if (!strcmp("notify", chrc->flags[i]))
1775 static const GDBusPropertyTable chrc_properties[] = {
1776 { "Handle", "q", chrc_get_handle, chrc_set_handle, NULL },
1777 { "UUID", "s", chrc_get_uuid, NULL, NULL },
1778 { "Service", "o", chrc_get_service, NULL, NULL },
1779 { "Value", "ay", chrc_get_value, NULL, NULL },
1780 { "Notifying", "b", chrc_get_notifying, NULL, NULL },
1781 { "Flags", "as", chrc_get_flags, NULL, NULL },
1782 { "WriteAcquired", "b", chrc_get_write_acquired, NULL,
1783 chrc_write_acquired_exists },
1784 { "NotifyAcquired", "b", chrc_get_notify_acquired, NULL,
1785 chrc_notify_acquired_exists },
1789 static const char *path_to_address(const char *path)
1792 DBusMessageIter iter;
1793 const char *address = path;
1795 proxy = bt_shell_get_env(path);
1797 if (g_dbus_proxy_get_property(proxy, "Address", &iter))
1798 dbus_message_iter_get_basic(&iter, &address);
1803 static int parse_options(DBusMessageIter *iter, uint16_t *offset, uint16_t *mtu,
1804 char **device, char **link,
1805 bool *prep_authorize)
1807 DBusMessageIter dict;
1809 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
1812 dbus_message_iter_recurse(iter, &dict);
1814 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1816 DBusMessageIter value, entry;
1819 dbus_message_iter_recurse(&dict, &entry);
1820 dbus_message_iter_get_basic(&entry, &key);
1822 dbus_message_iter_next(&entry);
1823 dbus_message_iter_recurse(&entry, &value);
1825 var = dbus_message_iter_get_arg_type(&value);
1826 if (strcasecmp(key, "offset") == 0) {
1827 if (var != DBUS_TYPE_UINT16)
1830 dbus_message_iter_get_basic(&value, offset);
1831 } else if (strcasecmp(key, "MTU") == 0) {
1832 if (var != DBUS_TYPE_UINT16)
1835 dbus_message_iter_get_basic(&value, mtu);
1836 } else if (strcasecmp(key, "device") == 0) {
1837 if (var != DBUS_TYPE_OBJECT_PATH)
1840 dbus_message_iter_get_basic(&value, device);
1841 } else if (strcasecmp(key, "link") == 0) {
1842 if (var != DBUS_TYPE_STRING)
1845 dbus_message_iter_get_basic(&value, link);
1846 } else if (strcasecmp(key, "prepare-authorize") == 0) {
1847 if (var != DBUS_TYPE_BOOLEAN)
1849 if (prep_authorize) {
1852 dbus_message_iter_get_basic(&value, &tmp);
1853 *prep_authorize = !!tmp;
1857 dbus_message_iter_next(&dict);
1863 static DBusMessage *read_value(DBusMessage *msg, uint8_t *value,
1867 DBusMessageIter iter, array;
1869 reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1871 dbus_message_iter_init_append(reply, &iter);
1872 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
1873 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
1875 dbus_message_iter_close_container(&iter, &array);
1880 struct authorize_attribute_data {
1881 DBusConnection *conn;
1886 static void authorize_read_response(const char *input, void *user_data)
1888 struct authorize_attribute_data *aad = user_data;
1889 struct chrc *chrc = aad->attribute;
1893 if (!strcmp(input, "no")) {
1894 err = "org.bluez.Error.NotAuthorized";
1899 if (aad->offset > chrc->value_len) {
1900 err = "org.bluez.Error.InvalidOffset";
1905 reply = read_value(pending_message, &chrc->value[aad->offset],
1906 chrc->value_len - aad->offset);
1908 g_dbus_send_message(aad->conn, reply);
1915 g_dbus_send_error(aad->conn, pending_message, err, NULL);
1919 static bool is_device_trusted(const char *path)
1922 DBusMessageIter iter;
1923 bool trusted = false;
1925 proxy = bt_shell_get_env(path);
1927 if (g_dbus_proxy_get_property(proxy, "Trusted", &iter))
1928 dbus_message_iter_get_basic(&iter, &trusted);
1933 struct read_attribute_data {
1938 static void proxy_read_reply(DBusMessage *message, void *user_data)
1940 struct read_attribute_data *data = user_data;
1941 DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION");
1943 DBusMessageIter iter, array;
1948 dbus_error_init(&error);
1950 if (dbus_set_error_from_message(&error, message) == TRUE) {
1951 bt_shell_printf("Failed to read: %s\n", error.name);
1952 dbus_error_free(&error);
1953 g_dbus_send_error(conn, data->msg, error.name, "%s",
1958 dbus_message_iter_init(message, &iter);
1960 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1961 bt_shell_printf("Invalid response to read\n");
1962 g_dbus_send_error(conn, data->msg,
1963 "org.bluez.Error.InvalidArguments", NULL);
1967 dbus_message_iter_recurse(&iter, &array);
1968 dbus_message_iter_get_fixed_array(&array, &value, &len);
1971 bt_shell_printf("Unable to parse value\n");
1972 g_dbus_send_error(conn, data->msg,
1973 "org.bluez.Error.InvalidArguments", NULL);
1976 reply = read_value(data->msg, value, len);
1978 g_dbus_send_message(conn, reply);
1981 dbus_message_unref(data->msg);
1985 static void proxy_read_setup(DBusMessageIter *iter, void *user_data)
1987 DBusMessageIter dict;
1988 struct read_attribute_data *data = user_data;
1990 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1991 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1992 DBUS_TYPE_STRING_AS_STRING
1993 DBUS_TYPE_VARIANT_AS_STRING
1994 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1997 g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16,
2000 dbus_message_iter_close_container(iter, &dict);
2003 static DBusMessage *proxy_read_value(struct GDBusProxy *proxy, DBusMessage *msg,
2006 struct read_attribute_data *data;
2008 data = new0(struct read_attribute_data, 1);
2009 data->msg = dbus_message_ref(msg);
2010 data->offset = offset;
2012 if (g_dbus_proxy_method_call(proxy, "ReadValue", proxy_read_setup,
2013 proxy_read_reply, data, NULL))
2016 bt_shell_printf("Failed to read\n");
2018 return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
2022 static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,
2025 struct chrc *chrc = user_data;
2026 DBusMessageIter iter;
2027 uint16_t offset = 0;
2028 char *device, *link;
2031 dbus_message_iter_init(msg, &iter);
2033 if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
2034 return g_dbus_create_error(msg,
2035 "org.bluez.Error.InvalidArguments",
2038 bt_shell_printf("[%s (%s)] ReadValue: %s offset %u link %s\n",
2039 chrc->path, bt_uuidstr_to_str(chrc->uuid),
2040 path_to_address(device), offset, link);
2043 return proxy_read_value(chrc->proxy, msg, offset);
2046 if (!is_device_trusted(device) && chrc->authorization_req) {
2047 struct authorize_attribute_data *aad;
2049 aad = g_new0(struct authorize_attribute_data, 1);
2051 aad->attribute = chrc;
2052 aad->offset = offset;
2054 str = g_strdup_printf("Authorize attribute(%s) read (yes/no):",
2057 bt_shell_prompt_input("gatt", str, authorize_read_response,
2061 pending_message = dbus_message_ref(msg);
2066 if (offset > chrc->value_len)
2067 return g_dbus_create_error(msg, "org.bluez.Error.InvalidOffset",
2070 return read_value(msg, &chrc->value[offset], chrc->value_len - offset);
2073 static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
2075 DBusMessageIter array;
2077 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
2080 dbus_message_iter_recurse(iter, &array);
2081 dbus_message_iter_get_fixed_array(&array, value, len);
2086 static int write_value(size_t *dst_len, uint8_t **dst_value, uint8_t *src_val,
2087 size_t src_len, uint16_t offset, uint16_t max_len)
2089 if ((offset + src_len) > max_len)
2092 if ((offset + src_len) != *dst_len) {
2093 *dst_len = offset + src_len;
2094 *dst_value = g_realloc(*dst_value, *dst_len);
2097 memcpy(*dst_value + offset, src_val, src_len);
2102 static void authorize_write_response(const char *input, void *user_data)
2104 struct authorize_attribute_data *aad = user_data;
2105 struct chrc *chrc = aad->attribute;
2106 bool prep_authorize = false;
2107 DBusMessageIter iter;
2113 dbus_message_iter_init(pending_message, &iter);
2114 if (parse_value_arg(&iter, &value, &value_len)) {
2115 err = "org.bluez.Error.InvalidArguments";
2120 dbus_message_iter_next(&iter);
2121 if (parse_options(&iter, NULL, NULL, NULL, NULL, &prep_authorize)) {
2122 err = "org.bluez.Error.InvalidArguments";
2127 if (!strcmp(input, "no")) {
2128 err = "org.bluez.Error.NotAuthorized";
2133 if (aad->offset > chrc->value_len) {
2134 err = "org.bluez.Error.InvalidOffset";
2139 /* Authorization check of prepare writes */
2140 if (prep_authorize) {
2141 reply = g_dbus_create_reply(pending_message, DBUS_TYPE_INVALID);
2142 g_dbus_send_message(aad->conn, reply);
2148 if (write_value(&chrc->value_len, &chrc->value, value, value_len,
2149 aad->offset, chrc->max_val_len)) {
2150 err = "org.bluez.Error.InvalidValueLength";
2155 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written",
2156 chrc->path, bt_uuidstr_to_str(chrc->uuid));
2158 g_dbus_emit_property_changed(aad->conn, chrc->path, CHRC_INTERFACE,
2161 reply = g_dbus_create_reply(pending_message, DBUS_TYPE_INVALID);
2162 g_dbus_send_message(aad->conn, reply);
2169 g_dbus_send_error(aad->conn, pending_message, err, NULL);
2173 static void proxy_write_reply(DBusMessage *message, void *user_data)
2175 struct write_attribute_data *data = user_data;
2176 DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION");
2179 dbus_error_init(&error);
2181 if (dbus_set_error_from_message(&error, message)) {
2182 bt_shell_printf("Failed to write: %s\n", error.name);
2183 g_dbus_send_error(conn, data->msg, error.name, "%s",
2186 g_dbus_send_reply(conn, data->msg, DBUS_TYPE_INVALID);
2188 dbus_message_unref(data->msg);
2192 static DBusMessage *proxy_write_value(struct GDBusProxy *proxy,
2193 DBusMessage *msg, uint8_t *value,
2194 int value_len, uint16_t offset)
2196 struct write_attribute_data *data;
2199 data = new0(struct write_attribute_data, 1);
2200 data->msg = dbus_message_ref(msg);
2201 data->iov.iov_base = (void *) value;
2202 data->iov.iov_len = value_len;
2203 data->offset = offset;
2205 if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
2206 proxy_write_reply, data, NULL))
2210 bt_shell_printf("Failed to write\n");
2212 return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
2216 static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
2219 struct chrc *chrc = user_data;
2220 uint16_t offset = 0;
2221 bool prep_authorize = false;
2222 char *device = NULL, *link = NULL;
2223 DBusMessageIter iter;
2228 dbus_message_iter_init(msg, &iter);
2230 if (parse_value_arg(&iter, &value, &value_len))
2231 return g_dbus_create_error(msg,
2232 "org.bluez.Error.InvalidArguments", NULL);
2234 dbus_message_iter_next(&iter);
2235 if (parse_options(&iter, &offset, NULL, &device, &link,
2237 return g_dbus_create_error(msg,
2238 "org.bluez.Error.InvalidArguments", NULL);
2240 bt_shell_printf("[%s (%s)] WriteValue: %s offset %u link %s\n",
2241 chrc->path, bt_uuidstr_to_str(chrc->uuid),
2242 path_to_address(device), offset, link);
2244 bt_shell_hexdump(value, value_len);
2247 return proxy_write_value(chrc->proxy, msg, value, value_len,
2250 if (!is_device_trusted(device) && chrc->authorization_req) {
2251 struct authorize_attribute_data *aad;
2253 aad = g_new0(struct authorize_attribute_data, 1);
2255 aad->attribute = chrc;
2256 aad->offset = offset;
2258 str = g_strdup_printf("Authorize attribute(%s) write (yes/no):",
2261 bt_shell_prompt_input("gatt", str, authorize_write_response,
2265 pending_message = dbus_message_ref(msg);
2270 if (offset > chrc->value_len)
2271 return g_dbus_create_error(msg,
2272 "org.bluez.Error.InvalidOffset", NULL);
2275 /* Authorization check of prepare writes */
2277 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2279 if (write_value(&chrc->value_len, &chrc->value, value, value_len,
2280 offset, chrc->max_val_len))
2281 return g_dbus_create_error(msg,
2282 "org.bluez.Error.InvalidValueLength", NULL);
2284 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written",
2285 chrc->path, bt_uuidstr_to_str(chrc->uuid));
2287 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "Value");
2289 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2292 static DBusMessage *create_sock(struct chrc *chrc, DBusMessage *msg)
2299 if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC,
2301 return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
2304 dir = dbus_message_has_member(msg, "AcquireWrite");
2306 io = sock_io_new(fds[!dir], chrc);
2310 return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
2314 reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &fds[dir],
2315 DBUS_TYPE_UINT16, &chrc->mtu,
2321 chrc->write_io = io;
2323 chrc->notify_io = io;
2325 bt_shell_printf("[" COLORED_CHG "] Attribute %s %s sock acquired\n",
2326 chrc->path, dir ? "Write" : "Notify");
2331 static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
2334 struct chrc *chrc = user_data;
2335 DBusMessageIter iter;
2337 char *device = NULL, *link= NULL;
2339 dbus_message_iter_init(msg, &iter);
2342 return g_dbus_create_error(msg,
2343 "org.bluez.Error.NotPermitted",
2346 if (parse_options(&iter, NULL, &chrc->mtu, &device, &link, NULL))
2347 return g_dbus_create_error(msg,
2348 "org.bluez.Error.InvalidArguments",
2351 bt_shell_printf("AcquireWrite: %s link %s\n", path_to_address(device),
2354 reply = create_sock(chrc, msg);
2357 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
2363 static DBusMessage *chrc_acquire_notify(DBusConnection *conn, DBusMessage *msg,
2366 struct chrc *chrc = user_data;
2367 DBusMessageIter iter;
2369 char *device = NULL, *link = NULL;
2371 dbus_message_iter_init(msg, &iter);
2373 if (chrc->notify_io)
2374 return g_dbus_create_error(msg,
2375 "org.bluez.Error.NotPermitted",
2378 if (parse_options(&iter, NULL, &chrc->mtu, &device, &link, NULL))
2379 return g_dbus_create_error(msg,
2380 "org.bluez.Error.InvalidArguments",
2383 bt_shell_printf("AcquireNotify: %s link %s\n", path_to_address(device),
2386 reply = create_sock(chrc, msg);
2388 if (chrc->notify_io)
2389 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
2395 struct notify_attribute_data {
2401 static void proxy_notify_reply(DBusMessage *message, void *user_data)
2403 struct notify_attribute_data *data = user_data;
2404 DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION");
2407 dbus_error_init(&error);
2409 if (dbus_set_error_from_message(&error, message) == TRUE) {
2410 bt_shell_printf("Failed to %s: %s\n",
2411 data->enable ? "StartNotify" : "StopNotify",
2413 dbus_error_free(&error);
2414 g_dbus_send_error(conn, data->msg, error.name, "%s",
2419 g_dbus_send_reply(conn, data->msg, DBUS_TYPE_INVALID);
2421 data->chrc->notifying = data->enable;
2422 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) "
2423 "notifications %s\n",
2425 bt_uuidstr_to_str(data->chrc->uuid),
2426 data->enable ? "enabled" : "disabled");
2427 g_dbus_emit_property_changed(conn, data->chrc->path, CHRC_INTERFACE,
2431 dbus_message_unref(data->msg);
2435 static DBusMessage *proxy_notify(struct chrc *chrc, DBusMessage *msg,
2438 struct notify_attribute_data *data;
2442 method = "StartNotify";
2444 method = "StopNotify";
2446 data = new0(struct notify_attribute_data, 1);
2448 data->msg = dbus_message_ref(msg);
2449 data->enable = enable;
2451 if (g_dbus_proxy_method_call(chrc->proxy, method, NULL,
2452 proxy_notify_reply, data, NULL))
2455 return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
2459 static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
2462 struct chrc *chrc = user_data;
2464 if (chrc->notifying)
2465 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2468 return proxy_notify(chrc, msg, true);
2470 chrc->notifying = true;
2471 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) notifications "
2472 "enabled", chrc->path, bt_uuidstr_to_str(chrc->uuid));
2473 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
2476 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2479 static DBusMessage *chrc_stop_notify(DBusConnection *conn, DBusMessage *msg,
2482 struct chrc *chrc = user_data;
2484 if (!chrc->notifying)
2485 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2488 return proxy_notify(chrc, msg, false);
2490 chrc->notifying = false;
2491 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) notifications "
2492 "disabled", chrc->path, bt_uuidstr_to_str(chrc->uuid));
2493 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
2496 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2499 static DBusMessage *chrc_confirm(DBusConnection *conn, DBusMessage *msg,
2502 struct chrc *chrc = user_data;
2504 bt_shell_printf("Attribute %s (%s) indication confirm received",
2505 chrc->path, bt_uuidstr_to_str(chrc->uuid));
2507 return dbus_message_new_method_return(msg);
2510 static const GDBusMethodTable chrc_methods[] = {
2511 { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
2512 GDBUS_ARGS({ "value", "ay" }),
2514 { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
2515 { "options", "a{sv}" }),
2516 NULL, chrc_write_value) },
2517 { GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }),
2518 NULL, chrc_acquire_write) },
2519 { GDBUS_METHOD("AcquireNotify", GDBUS_ARGS({ "options", "a{sv}" }),
2520 NULL, chrc_acquire_notify) },
2521 { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
2522 { GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
2523 { GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) },
2527 static void chrc_set_value(const char *input, void *user_data)
2529 struct chrc *chrc = user_data;
2531 g_free(chrc->value);
2533 chrc->value = str2bytearray((char *) input, &chrc->value_len);
2536 print_chrc(chrc, COLORED_DEL);
2537 chrc_unregister(chrc);
2540 chrc->max_val_len = chrc->value_len;
2543 static gboolean attr_authorization_flag_exists(char **flags)
2547 for (i = 0; flags[i]; i++) {
2548 if (!strcmp("authorize", flags[i]))
2555 void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy,
2556 int argc, char *argv[])
2558 struct service *service;
2561 if (!local_services) {
2562 bt_shell_printf("No service registered\n");
2563 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2566 service = g_list_last(local_services)->data;
2568 chrc = g_new0(struct chrc, 1);
2569 chrc->service = service;
2570 chrc->uuid = g_strdup(argv[1]);
2571 chrc->path = g_strdup_printf("%s/chrc%u", service->path,
2572 g_list_length(service->chrcs));
2573 chrc->flags = g_strsplit(argv[2], ",", -1);
2574 chrc->authorization_req = attr_authorization_flag_exists(chrc->flags);
2577 chrc->handle = atoi(argv[3]);
2579 if (g_dbus_register_interface(conn, chrc->path, CHRC_INTERFACE,
2580 chrc_methods, NULL, chrc_properties,
2581 chrc, chrc_free) == FALSE) {
2582 bt_shell_printf("Failed to register characteristic object\n");
2584 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2587 service->chrcs = g_list_append(service->chrcs, chrc);
2589 print_chrc(chrc, COLORED_NEW);
2591 bt_shell_prompt_input(chrc->path, "Enter value:", chrc_set_value, chrc);
2593 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
2596 static struct chrc *chrc_find(const char *pattern)
2599 struct service *service;
2602 for (l = local_services; l; l = g_list_next(l)) {
2605 for (lc = service->chrcs; lc; lc = g_list_next(lc)) {
2608 /* match object path */
2609 if (!strcmp(chrc->path, pattern))
2613 if (!strcmp(chrc->uuid, pattern))
2621 void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
2622 int argc, char *argv[])
2626 chrc = chrc_find(argv[1]);
2628 bt_shell_printf("Failed to unregister characteristic object\n");
2629 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2632 chrc->service->chrcs = g_list_remove(chrc->service->chrcs, chrc);
2634 chrc_unregister(chrc);
2636 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
2639 static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
2642 struct desc *desc = user_data;
2643 DBusMessageIter iter;
2644 uint16_t offset = 0;
2645 char *device = NULL, *link = NULL;
2647 dbus_message_iter_init(msg, &iter);
2649 if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
2650 return g_dbus_create_error(msg,
2651 "org.bluez.Error.InvalidArguments",
2654 bt_shell_printf("[%s (%s)] ReadValue: %s offset %u link %s\n",
2655 desc->path, bt_uuidstr_to_str(desc->uuid),
2656 path_to_address(device), offset, link);
2658 if (offset > desc->value_len)
2659 return g_dbus_create_error(msg, "org.bluez.Error.InvalidOffset",
2662 return read_value(msg, &desc->value[offset], desc->value_len - offset);
2665 static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
2668 struct desc *desc = user_data;
2669 DBusMessageIter iter;
2670 uint16_t offset = 0;
2671 char *device = NULL, *link = NULL;
2675 dbus_message_iter_init(msg, &iter);
2677 if (parse_value_arg(&iter, &value, &value_len))
2678 return g_dbus_create_error(msg,
2679 "org.bluez.Error.InvalidArguments", NULL);
2681 dbus_message_iter_next(&iter);
2682 if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
2683 return g_dbus_create_error(msg,
2684 "org.bluez.Error.InvalidArguments", NULL);
2686 if (offset > desc->value_len)
2687 return g_dbus_create_error(msg,
2688 "org.bluez.Error.InvalidOffset", NULL);
2690 if (write_value(&desc->value_len, &desc->value, value,
2691 value_len, offset, desc->max_val_len))
2692 return g_dbus_create_error(msg,
2693 "org.bluez.Error.InvalidValueLength", NULL);
2695 bt_shell_printf("[%s (%s)] WriteValue: %s offset %u link %s\n",
2696 desc->path, bt_uuidstr_to_str(desc->uuid),
2697 path_to_address(device), offset, link);
2699 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written",
2700 desc->path, bt_uuidstr_to_str(desc->uuid));
2702 g_dbus_emit_property_changed(conn, desc->path, CHRC_INTERFACE, "Value");
2704 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2707 static const GDBusMethodTable desc_methods[] = {
2708 { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
2709 GDBUS_ARGS({ "value", "ay" }),
2711 { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
2712 { "options", "a{sv}" }),
2713 NULL, desc_write_value) },
2717 static gboolean desc_get_handle(const GDBusPropertyTable *property,
2718 DBusMessageIter *iter, void *data)
2720 struct desc *desc = data;
2722 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &desc->handle);
2727 static void desc_set_handle(const GDBusPropertyTable *property,
2728 DBusMessageIter *value, GDBusPendingPropertySet id,
2731 struct desc *desc = data;
2733 if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) {
2734 g_dbus_pending_property_error(id, "org.bluez.InvalidArguments",
2735 "Invalid arguments in method call");
2739 dbus_message_iter_get_basic(value, &desc->handle);
2741 print_desc(desc, COLORED_CHG);
2743 g_dbus_pending_property_success(id);
2746 static gboolean desc_get_uuid(const GDBusPropertyTable *property,
2747 DBusMessageIter *iter, void *data)
2749 struct desc *desc = data;
2751 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
2756 static gboolean desc_get_chrc(const GDBusPropertyTable *property,
2757 DBusMessageIter *iter, void *data)
2759 struct desc *desc = data;
2761 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
2767 static gboolean desc_get_value(const GDBusPropertyTable *property,
2768 DBusMessageIter *iter, void *data)
2770 struct desc *desc = data;
2771 DBusMessageIter array;
2773 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
2776 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
2780 dbus_message_iter_close_container(iter, &array);
2785 static gboolean desc_get_flags(const GDBusPropertyTable *property,
2786 DBusMessageIter *iter, void *data)
2788 struct desc *desc = data;
2790 DBusMessageIter array;
2792 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
2794 for (i = 0; desc->flags[i]; i++)
2795 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
2798 dbus_message_iter_close_container(iter, &array);
2803 static const GDBusPropertyTable desc_properties[] = {
2804 { "Handle", "q", desc_get_handle, desc_set_handle, NULL },
2805 { "UUID", "s", desc_get_uuid, NULL, NULL },
2806 { "Characteristic", "o", desc_get_chrc, NULL, NULL },
2807 { "Value", "ay", desc_get_value, NULL, NULL },
2808 { "Flags", "as", desc_get_flags, NULL, NULL },
2812 static void desc_set_value(const char *input, void *user_data)
2814 struct desc *desc = user_data;
2816 g_free(desc->value);
2818 desc->value = str2bytearray((char *) input, &desc->value_len);
2821 print_desc(desc, COLORED_DEL);
2822 desc_unregister(desc);
2825 desc->max_val_len = desc->value_len;
2828 void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy,
2829 int argc, char *argv[])
2831 struct service *service;
2834 if (!local_services) {
2835 bt_shell_printf("No service registered\n");
2836 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2839 service = g_list_last(local_services)->data;
2841 if (!service->chrcs) {
2842 bt_shell_printf("No characteristic registered\n");
2843 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2846 desc = g_new0(struct desc, 1);
2847 desc->chrc = g_list_last(service->chrcs)->data;
2848 desc->uuid = g_strdup(argv[1]);
2849 desc->path = g_strdup_printf("%s/desc%u", desc->chrc->path,
2850 g_list_length(desc->chrc->descs));
2851 desc->flags = g_strsplit(argv[2], ",", -1);
2854 desc->handle = atoi(argv[3]);
2856 if (g_dbus_register_interface(conn, desc->path, DESC_INTERFACE,
2857 desc_methods, NULL, desc_properties,
2858 desc, desc_free) == FALSE) {
2859 bt_shell_printf("Failed to register descriptor object\n");
2861 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2864 desc->chrc->descs = g_list_append(desc->chrc->descs, desc);
2866 print_desc(desc, COLORED_NEW);
2868 bt_shell_prompt_input(desc->path, "Enter value:", desc_set_value, desc);
2870 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
2873 static struct desc *desc_find(const char *pattern)
2876 struct service *service;
2880 for (l = local_services; l; l = g_list_next(l)) {
2883 for (lc = service->chrcs; lc; lc = g_list_next(lc)) {
2886 for (ld = chrc->descs; ld; ld = g_list_next(ld)) {
2889 /* match object path */
2890 if (!strcmp(desc->path, pattern))
2894 if (!strcmp(desc->uuid, pattern))
2903 void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
2904 int argc, char *argv[])
2908 desc = desc_find(argv[1]);
2910 bt_shell_printf("Failed to unregister descriptor object\n");
2911 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2914 desc->chrc->descs = g_list_remove(desc->chrc->descs, desc);
2916 desc_unregister(desc);
2918 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
2921 static GDBusProxy *select_service(GDBusProxy *proxy)
2925 for (l = services; l; l = g_list_next(l)) {
2926 GDBusProxy *p = l->data;
2928 if (proxy == p || g_str_has_prefix(g_dbus_proxy_get_path(proxy),
2929 g_dbus_proxy_get_path(p)))
2936 static void proxy_property_changed(GDBusProxy *proxy, const char *name,
2937 DBusMessageIter *iter, void *user_data)
2939 DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION");
2940 struct chrc *chrc = user_data;
2942 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) %s:\n",
2943 chrc->path, bt_uuidstr_to_str(chrc->uuid), name);
2945 if (!strcmp(name, "Value")) {
2946 DBusMessageIter array;
2950 if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
2951 dbus_message_iter_recurse(iter, &array);
2952 dbus_message_iter_get_fixed_array(&array, &value, &len);
2953 write_value(&chrc->value_len, &chrc->value, value, len,
2954 0, chrc->max_val_len);
2955 bt_shell_hexdump(value, len);
2959 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, name);
2962 static void clone_chrc(struct GDBusProxy *proxy)
2964 struct service *service;
2966 DBusMessageIter iter;
2967 DBusMessageIter array;
2972 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
2975 dbus_message_iter_get_basic(&iter, &uuid);
2977 if (g_dbus_proxy_get_property(proxy, "Flags", &iter) == FALSE)
2980 dbus_message_iter_recurse(&iter, &array);
2982 for (i = 0; dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING;
2984 dbus_message_iter_get_basic(&array, &flags[i]);
2985 dbus_message_iter_next(&array);
2990 service = g_list_last(local_services)->data;
2992 chrc = g_new0(struct chrc, 1);
2993 chrc->service = service;
2994 chrc->proxy = proxy;
2995 chrc->uuid = g_strdup(uuid);
2996 chrc->path = g_strdup_printf("%s/chrc%u", service->path,
2997 g_list_length(service->chrcs));
2998 chrc->flags = g_strdupv(flags);
3000 if (g_dbus_register_interface(service->conn, chrc->path, CHRC_INTERFACE,
3001 chrc_methods, NULL, chrc_properties,
3002 chrc, chrc_free) == FALSE) {
3003 bt_shell_printf("Failed to register characteristic object\n");
3005 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3008 g_dbus_proxy_set_property_watch(proxy, proxy_property_changed, chrc);
3010 service->chrcs = g_list_append(service->chrcs, chrc);
3012 print_chrc(chrc, COLORED_NEW);
3015 static void clone_chrcs(struct GDBusProxy *proxy)
3019 for (l = characteristics; l; l = g_list_next(l)) {
3020 GDBusProxy *p = l->data;
3022 if (g_str_has_prefix(g_dbus_proxy_get_path(p),
3023 g_dbus_proxy_get_path(proxy)))
3028 static void clone_service(struct GDBusProxy *proxy)
3030 struct service *service;
3031 DBusMessageIter iter;
3033 dbus_bool_t primary;
3035 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
3038 dbus_message_iter_get_basic(&iter, &uuid);
3040 if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE)
3043 dbus_message_iter_get_basic(&iter, &primary);
3045 if (!strcmp(uuid, "00001800-0000-1000-8000-00805f9b34fb") ||
3046 !strcmp(uuid, "00001801-0000-1000-8000-00805f9b34fb"))
3049 service = g_new0(struct service, 1);
3050 service->conn = bt_shell_get_env("DBUS_CONNECTION");
3051 service->proxy = proxy;
3052 service->path = g_strdup_printf("%s/service%u", APP_PATH,
3053 g_list_length(local_services));
3054 service->uuid = g_strdup(uuid);
3055 service->primary = primary;
3057 if (g_dbus_register_interface(service->conn, service->path,
3058 SERVICE_INTERFACE, NULL, NULL,
3059 service_properties, service,
3060 service_free) == FALSE) {
3061 bt_shell_printf("Failed to register service object\n");
3062 service_free(service);
3063 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3066 print_service(service, COLORED_NEW);
3068 local_services = g_list_append(local_services, service);
3073 static void clone_device(struct GDBusProxy *proxy)
3077 for (l = services; l; l = g_list_next(l)) {
3078 struct GDBusProxy *p = l->data;
3080 if (g_str_has_prefix(g_dbus_proxy_get_path(p),
3081 g_dbus_proxy_get_path(proxy)))
3086 static void service_clone(const char *input, void *user_data)
3088 struct GDBusProxy *proxy = user_data;
3090 if (!strcmp(input, "yes"))
3091 return clone_service(proxy);
3092 else if (!strcmp(input, "no"))
3093 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3094 else if (!strcmp(input, "all"))
3095 return clone_device(proxy);
3097 bt_shell_printf("Invalid option: %s\n", input);
3099 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3102 static void device_clone(const char *input, void *user_data)
3104 struct GDBusProxy *proxy = user_data;
3106 if (!strcmp(input, "yes"))
3107 return clone_device(proxy);
3108 else if (!strcmp(input, "no"))
3109 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3111 bt_shell_printf("Invalid option: %s\n", input);
3113 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3116 static const char *proxy_get_name(struct GDBusProxy *proxy)
3118 DBusMessageIter iter;
3122 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
3125 dbus_message_iter_get_basic(&iter, &uuid);
3127 str = bt_uuidstr_to_str(uuid);
3129 return str ? str : uuid;
3132 static const char *proxy_get_alias(struct GDBusProxy *proxy)
3134 DBusMessageIter iter;
3137 if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == FALSE)
3140 dbus_message_iter_get_basic(&iter, &alias);
3145 void gatt_clone_attribute(GDBusProxy *proxy, int argc, char *argv[])
3147 GDBusProxy *service = NULL;
3150 proxy = gatt_select_attribute(proxy, argv[1]);
3152 bt_shell_printf("Unable to find attribute %s\n",
3154 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3158 if (!strcmp(g_dbus_proxy_get_interface(proxy), DEVICE_INTERFACE)) {
3159 bt_shell_prompt_input(proxy_get_alias(proxy),
3161 device_clone, proxy);
3164 /* Only clone services */
3165 service = select_service(proxy);
3167 bt_shell_prompt_input(proxy_get_name(proxy),
3168 "Clone (yes/no/all):",
3169 service_clone, service);
3173 return bt_shell_noninteractive_quit(EXIT_FAILURE);