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 static int parse_offset(const char *arg)
652 unsigned long offset;
654 offset = strtoul(arg, &endptr, 0);
655 if (!endptr || *endptr != '\0' || offset > UINT16_MAX) {
656 bt_shell_printf("Invalid offload: %s", arg);
663 void gatt_read_attribute(GDBusProxy *proxy, int argc, char *argv[])
668 iface = g_dbus_proxy_get_interface(proxy);
669 if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
670 !strcmp(iface, "org.bluez.GattDescriptor1")) {
673 offset = parse_offset(argv[1]);
678 read_attribute(proxy, offset);
683 bt_shell_printf("Unable to read attribute %s\n",
684 g_dbus_proxy_get_path(proxy));
685 return bt_shell_noninteractive_quit(EXIT_FAILURE);
688 static void write_reply(DBusMessage *message, void *user_data)
692 dbus_error_init(&error);
694 if (dbus_set_error_from_message(&error, message) == TRUE) {
695 bt_shell_printf("Failed to write: %s\n", error.name);
696 dbus_error_free(&error);
697 return bt_shell_noninteractive_quit(EXIT_FAILURE);
700 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
703 struct write_attribute_data {
710 static void write_setup(DBusMessageIter *iter, void *user_data)
712 struct write_attribute_data *wd = user_data;
713 DBusMessageIter array, dict;
715 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
716 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
719 dbus_message_iter_close_container(iter, &array);
721 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
722 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
723 DBUS_TYPE_STRING_AS_STRING
724 DBUS_TYPE_VARIANT_AS_STRING
725 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
729 g_dbus_dict_append_entry(&dict, "type", DBUS_TYPE_STRING,
732 g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16,
735 dbus_message_iter_close_container(iter, &dict);
738 static int sock_send(struct io *io, struct iovec *iov, size_t iovlen)
743 memset(&msg, 0, sizeof(msg));
745 msg.msg_iovlen = iovlen;
747 ret = sendmsg(io_get_fd(io), &msg, MSG_NOSIGNAL);
750 bt_shell_printf("sendmsg: %s", strerror(-ret));
756 static void write_attribute(GDBusProxy *proxy,
757 struct write_attribute_data *data)
759 /* Write using the fd if it has been acquired and fit the MTU */
760 if (proxy == write_io.proxy &&
761 (write_io.io && write_io.mtu >= data->iov.iov_len)) {
762 bt_shell_printf("Attempting to write fd %d\n",
763 io_get_fd(write_io.io));
764 if (sock_send(write_io.io, &data->iov, 1) < 0) {
765 bt_shell_printf("Failed to write: %s", strerror(errno));
766 return bt_shell_noninteractive_quit(EXIT_FAILURE);
771 if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
772 write_reply, data, NULL) == FALSE) {
773 bt_shell_printf("Failed to write\n");
774 return bt_shell_noninteractive_quit(EXIT_FAILURE);
777 bt_shell_printf("Attempting to write %s\n",
778 g_dbus_proxy_get_path(proxy));
781 static uint8_t *str2bytearray(char *arg, size_t *val_len)
783 uint8_t value[MAX_ATTR_VAL_LEN];
787 for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) {
794 if (i >= G_N_ELEMENTS(value)) {
795 bt_shell_printf("Too much data\n");
799 val = strtol(entry, &endptr, 0);
800 if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
801 bt_shell_printf("Invalid value at index %d\n", i);
810 return g_memdup(value, i);
813 void gatt_write_attribute(GDBusProxy *proxy, int argc, char *argv[])
816 struct write_attribute_data data;
818 memset(&data, 0, sizeof(data));
820 iface = g_dbus_proxy_get_interface(proxy);
821 if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
822 !strcmp(iface, "org.bluez.GattDescriptor1")) {
823 data.iov.iov_base = str2bytearray(argv[1], &data.iov.iov_len);
828 offset = parse_offset(argv[2]);
832 data.offset = offset;
838 write_attribute(proxy, &data);
843 bt_shell_printf("Unable to write attribute %s\n",
844 g_dbus_proxy_get_path(proxy));
846 return bt_shell_noninteractive_quit(EXIT_FAILURE);
849 static bool sock_read(struct io *io, void *user_data)
851 struct chrc *chrc = user_data;
854 uint8_t buf[MAX_ATTR_VAL_LEN];
855 int fd = io_get_fd(io);
858 if (io != notify_io.io && !chrc)
862 iov.iov_len = sizeof(buf);
864 memset(&msg, 0, sizeof(msg));
868 bytes_read = recvmsg(fd, &msg, MSG_DONTWAIT);
869 if (bytes_read < 0) {
870 bt_shell_printf("recvmsg: %s", strerror(errno));
878 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) "
879 "written:\n", chrc->path,
880 bt_uuidstr_to_str(chrc->uuid));
882 bt_shell_printf("[" COLORED_CHG "] %s Notification:\n",
883 g_dbus_proxy_get_path(notify_io.proxy));
885 bt_shell_hexdump(buf, bytes_read);
890 static bool sock_hup(struct io *io, void *user_data)
892 struct chrc *chrc = user_data;
895 bt_shell_printf("Attribute %s %s sock closed\n", chrc->path,
896 io == chrc->write_io ? "Write" : "Notify");
898 if (io == chrc->write_io) {
899 io_destroy(chrc->write_io);
900 chrc->write_io = NULL;
902 io_destroy(chrc->notify_io);
903 chrc->notify_io = NULL;
909 bt_shell_printf("%s closed\n", io == notify_io.io ? "Notify" : "Write");
911 if (io == notify_io.io)
919 static struct io *sock_io_new(int fd, void *user_data)
925 io_set_close_on_destroy(io, true);
927 io_set_read_handler(io, sock_read, user_data, NULL);
929 io_set_disconnect_handler(io, sock_hup, user_data, NULL);
934 static void acquire_write_reply(DBusMessage *message, void *user_data)
939 dbus_error_init(&error);
941 if (dbus_set_error_from_message(&error, message) == TRUE) {
942 bt_shell_printf("Failed to acquire write: %s\n", error.name);
943 dbus_error_free(&error);
944 write_io.proxy = NULL;
945 return bt_shell_noninteractive_quit(EXIT_FAILURE);
951 if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
952 DBUS_TYPE_UINT16, &write_io.mtu,
953 DBUS_TYPE_INVALID) == false)) {
954 bt_shell_printf("Invalid AcquireWrite response\n");
955 return bt_shell_noninteractive_quit(EXIT_FAILURE);
958 bt_shell_printf("AcquireWrite success: fd %d MTU %u\n", fd,
961 write_io.io = sock_io_new(fd, NULL);
962 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
965 static void acquire_setup(DBusMessageIter *iter, void *user_data)
967 DBusMessageIter dict;
969 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
970 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
971 DBUS_TYPE_STRING_AS_STRING
972 DBUS_TYPE_VARIANT_AS_STRING
973 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
976 dbus_message_iter_close_container(iter, &dict);
979 void gatt_acquire_write(GDBusProxy *proxy, const char *arg)
983 iface = g_dbus_proxy_get_interface(proxy);
984 if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
985 bt_shell_printf("Unable to acquire write: %s not a"
987 g_dbus_proxy_get_path(proxy));
988 return bt_shell_noninteractive_quit(EXIT_FAILURE);
991 if (g_dbus_proxy_method_call(proxy, "AcquireWrite", acquire_setup,
992 acquire_write_reply, NULL, NULL) == FALSE) {
993 bt_shell_printf("Failed to AcquireWrite\n");
994 return bt_shell_noninteractive_quit(EXIT_FAILURE);
997 write_io.proxy = proxy;
1000 void gatt_release_write(GDBusProxy *proxy, const char *arg)
1002 if (proxy != write_io.proxy || !write_io.io) {
1003 bt_shell_printf("Write not acquired\n");
1004 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1009 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1012 static void acquire_notify_reply(DBusMessage *message, void *user_data)
1017 dbus_error_init(&error);
1019 if (dbus_set_error_from_message(&error, message) == TRUE) {
1020 bt_shell_printf("Failed to acquire notify: %s\n", error.name);
1021 dbus_error_free(&error);
1022 write_io.proxy = NULL;
1023 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1027 io_destroy(notify_io.io);
1028 notify_io.io = NULL;
1033 if ((dbus_message_get_args(message, NULL, DBUS_TYPE_UNIX_FD, &fd,
1034 DBUS_TYPE_UINT16, ¬ify_io.mtu,
1035 DBUS_TYPE_INVALID) == false)) {
1036 bt_shell_printf("Invalid AcquireNotify response\n");
1037 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1040 bt_shell_printf("AcquireNotify success: fd %d MTU %u\n", fd,
1043 notify_io.io = sock_io_new(fd, NULL);
1045 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1048 void gatt_acquire_notify(GDBusProxy *proxy, const char *arg)
1052 iface = g_dbus_proxy_get_interface(proxy);
1053 if (strcmp(iface, "org.bluez.GattCharacteristic1")) {
1054 bt_shell_printf("Unable to acquire notify: %s not a"
1055 " characteristic\n",
1056 g_dbus_proxy_get_path(proxy));
1057 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1060 if (g_dbus_proxy_method_call(proxy, "AcquireNotify", acquire_setup,
1061 acquire_notify_reply, NULL, NULL) == FALSE) {
1062 bt_shell_printf("Failed to AcquireNotify\n");
1063 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1066 notify_io.proxy = proxy;
1069 void gatt_release_notify(GDBusProxy *proxy, const char *arg)
1071 if (proxy != notify_io.proxy || !notify_io.io) {
1072 bt_shell_printf("Notify not acquired\n");
1073 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1076 notify_io_destroy();
1078 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1081 static void notify_reply(DBusMessage *message, void *user_data)
1083 bool enable = GPOINTER_TO_UINT(user_data);
1086 dbus_error_init(&error);
1088 if (dbus_set_error_from_message(&error, message) == TRUE) {
1089 bt_shell_printf("Failed to %s notify: %s\n",
1090 enable ? "start" : "stop", error.name);
1091 dbus_error_free(&error);
1092 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1095 bt_shell_printf("Notify %s\n", enable == TRUE ? "started" : "stopped");
1097 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1100 static void notify_attribute(GDBusProxy *proxy, bool enable)
1105 method = "StartNotify";
1107 method = "StopNotify";
1109 if (g_dbus_proxy_method_call(proxy, method, NULL, notify_reply,
1110 GUINT_TO_POINTER(enable), NULL) == FALSE) {
1111 bt_shell_printf("Failed to %s notify\n",
1112 enable ? "start" : "stop");
1113 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1116 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1119 void gatt_notify_attribute(GDBusProxy *proxy, bool enable)
1123 iface = g_dbus_proxy_get_interface(proxy);
1124 if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
1125 notify_attribute(proxy, enable);
1129 bt_shell_printf("Unable to notify attribute %s\n",
1130 g_dbus_proxy_get_path(proxy));
1132 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1135 static void register_app_setup(DBusMessageIter *iter, void *user_data)
1137 DBusMessageIter opt;
1138 const char *path = "/";
1140 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
1142 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1143 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1144 DBUS_TYPE_STRING_AS_STRING
1145 DBUS_TYPE_VARIANT_AS_STRING
1146 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1148 dbus_message_iter_close_container(iter, &opt);
1152 static void register_app_reply(DBusMessage *message, void *user_data)
1156 dbus_error_init(&error);
1158 if (dbus_set_error_from_message(&error, message) == TRUE) {
1159 bt_shell_printf("Failed to register application: %s\n",
1161 dbus_error_free(&error);
1162 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1165 bt_shell_printf("Application registered\n");
1167 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1170 void gatt_add_manager(GDBusProxy *proxy)
1172 managers = g_list_append(managers, proxy);
1175 void gatt_remove_manager(GDBusProxy *proxy)
1177 managers = g_list_remove(managers, proxy);
1180 static int match_proxy(const void *a, const void *b)
1182 GDBusProxy *proxy1 = (void *) a;
1183 GDBusProxy *proxy2 = (void *) b;
1185 return strcmp(g_dbus_proxy_get_path(proxy1),
1186 g_dbus_proxy_get_path(proxy2));
1189 static DBusMessage *release_profile(DBusConnection *conn,
1190 DBusMessage *msg, void *user_data)
1192 g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
1194 return dbus_message_new_method_return(msg);
1197 static const GDBusMethodTable methods[] = {
1198 { GDBUS_METHOD("Release", NULL, NULL, release_profile) },
1202 static gboolean get_uuids(const GDBusPropertyTable *property,
1203 DBusMessageIter *iter, void *data)
1205 DBusMessageIter entry;
1208 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1209 DBUS_TYPE_STRING_AS_STRING, &entry);
1211 for (uuid = uuids; uuid; uuid = g_list_next(uuid->next))
1212 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
1215 dbus_message_iter_close_container(iter, &entry);
1220 static const GDBusPropertyTable properties[] = {
1221 { "UUIDs", "as", get_uuids },
1225 void gatt_register_app(DBusConnection *conn, GDBusProxy *proxy,
1226 int argc, char *argv[])
1231 l = g_list_find_custom(managers, proxy, match_proxy);
1233 bt_shell_printf("Unable to find GattManager proxy\n");
1234 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1237 for (i = 0; i < argc; i++)
1238 uuids = g_list_append(uuids, g_strdup(argv[i]));
1241 if (g_dbus_register_interface(conn, APP_PATH,
1242 PROFILE_INTERFACE, methods,
1243 NULL, properties, NULL,
1245 bt_shell_printf("Failed to register application"
1247 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1251 if (g_dbus_proxy_method_call(l->data, "RegisterApplication",
1253 register_app_reply, NULL,
1255 bt_shell_printf("Failed register application\n");
1256 g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
1257 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1261 static void unregister_app_reply(DBusMessage *message, void *user_data)
1263 DBusConnection *conn = user_data;
1266 dbus_error_init(&error);
1268 if (dbus_set_error_from_message(&error, message) == TRUE) {
1269 bt_shell_printf("Failed to unregister application: %s\n",
1271 dbus_error_free(&error);
1272 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1275 bt_shell_printf("Application unregistered\n");
1278 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1280 g_list_free_full(uuids, g_free);
1283 g_dbus_unregister_interface(conn, APP_PATH, PROFILE_INTERFACE);
1285 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1288 static void unregister_app_setup(DBusMessageIter *iter, void *user_data)
1290 const char *path = "/";
1292 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
1295 void gatt_unregister_app(DBusConnection *conn, GDBusProxy *proxy)
1299 l = g_list_find_custom(managers, proxy, match_proxy);
1301 bt_shell_printf("Unable to find GattManager proxy\n");
1302 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1305 if (g_dbus_proxy_method_call(l->data, "UnregisterApplication",
1306 unregister_app_setup,
1307 unregister_app_reply, conn,
1309 bt_shell_printf("Failed unregister profile\n");
1310 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1314 static void desc_free(void *data)
1316 struct desc *desc = data;
1320 g_strfreev(desc->flags);
1321 g_free(desc->value);
1325 static void desc_unregister(void *data)
1327 struct desc *desc = data;
1329 print_desc(desc, COLORED_DEL);
1331 g_dbus_unregister_interface(desc->chrc->service->conn, desc->path,
1335 static void chrc_free(void *data)
1337 struct chrc *chrc = data;
1339 g_list_free_full(chrc->descs, desc_unregister);
1342 g_strfreev(chrc->flags);
1343 g_free(chrc->value);
1347 static void chrc_unregister(void *data)
1349 struct chrc *chrc = data;
1351 print_chrc(chrc, COLORED_DEL);
1353 g_dbus_unregister_interface(chrc->service->conn, chrc->path,
1357 static void inc_unregister(void *data)
1364 static void service_free(void *data)
1366 struct service *service = data;
1368 g_list_free_full(service->chrcs, chrc_unregister);
1369 g_list_free_full(service->inc, inc_unregister);
1370 g_free(service->path);
1371 g_free(service->uuid);
1375 static gboolean service_get_handle(const GDBusPropertyTable *property,
1376 DBusMessageIter *iter, void *data)
1378 struct service *service = data;
1380 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
1386 static void service_set_handle(const GDBusPropertyTable *property,
1387 DBusMessageIter *value, GDBusPendingPropertySet id,
1390 struct service *service = data;
1392 if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) {
1393 g_dbus_pending_property_error(id, "org.bluez.InvalidArguments",
1394 "Invalid arguments in method call");
1398 dbus_message_iter_get_basic(value, &service->handle);
1400 print_service(service, COLORED_CHG);
1402 g_dbus_pending_property_success(id);
1405 static gboolean service_get_uuid(const GDBusPropertyTable *property,
1406 DBusMessageIter *iter, void *data)
1408 struct service *service = data;
1410 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &service->uuid);
1415 static gboolean service_get_primary(const GDBusPropertyTable *property,
1416 DBusMessageIter *iter, void *data)
1418 struct service *service = data;
1419 dbus_bool_t primary;
1421 primary = service->primary ? TRUE : FALSE;
1423 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &primary);
1429 static gboolean service_get_includes(const GDBusPropertyTable *property,
1430 DBusMessageIter *iter, void *data)
1432 DBusMessageIter array;
1433 struct service *service = data;
1438 for (l = service->inc ; l; l = g_list_next(l)) {
1441 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
1442 DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
1444 dbus_message_iter_append_basic(&array,
1445 DBUS_TYPE_OBJECT_PATH, &inc);
1449 dbus_message_iter_close_container(iter, &array);
1458 static gboolean service_exist_includes(const GDBusPropertyTable *property,
1461 struct service *service = data;
1471 static const GDBusPropertyTable service_properties[] = {
1472 { "Handle", "q", service_get_handle, service_set_handle },
1473 { "UUID", "s", service_get_uuid },
1474 { "Primary", "b", service_get_primary },
1475 { "Includes", "ao", service_get_includes,
1476 NULL, service_exist_includes },
1480 static void service_set_primary(const char *input, void *user_data)
1482 struct service *service = user_data;
1484 if (!strcmp(input, "yes"))
1485 service->primary = true;
1486 else if (!strcmp(input, "no")) {
1487 service->primary = false;
1489 bt_shell_printf("Invalid option: %s\n", input);
1490 local_services = g_list_remove(local_services, service);
1491 print_service(service, COLORED_DEL);
1492 g_dbus_unregister_interface(service->conn, service->path,
1497 static uint16_t parse_handle(const char *arg)
1499 char *endptr = NULL;
1500 unsigned long handle;
1502 handle = strtoul(arg, &endptr, 0);
1503 if (!endptr || *endptr != '\0' || !handle || handle > UINT16_MAX) {
1504 bt_shell_printf("Invalid handle: %s", arg);
1511 void gatt_register_service(DBusConnection *conn, GDBusProxy *proxy,
1512 int argc, char *argv[])
1514 struct service *service;
1515 bool primary = true;
1517 service = g_new0(struct service, 1);
1518 service->conn = conn;
1519 service->uuid = g_strdup(argv[1]);
1520 service->path = g_strdup_printf("%s/service%u", APP_PATH,
1521 g_list_length(local_services));
1522 service->primary = primary;
1525 service->handle = parse_handle(argv[2]);
1526 if (!service->handle)
1527 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1530 if (g_dbus_register_interface(conn, service->path,
1531 SERVICE_INTERFACE, NULL, NULL,
1532 service_properties, service,
1533 service_free) == FALSE) {
1534 bt_shell_printf("Failed to register service object\n");
1535 service_free(service);
1536 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1539 print_service(service, COLORED_NEW);
1541 local_services = g_list_append(local_services, service);
1543 bt_shell_prompt_input(service->path, "Primary (yes/no):",
1544 service_set_primary, service);
1546 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1549 static struct service *service_find(const char *pattern)
1553 for (l = local_services; l; l = g_list_next(l)) {
1554 struct service *service = l->data;
1556 /* match object path */
1557 if (!strcmp(service->path, pattern))
1561 if (!strcmp(service->uuid, pattern))
1568 void gatt_unregister_service(DBusConnection *conn, GDBusProxy *proxy,
1569 int argc, char *argv[])
1571 struct service *service;
1573 service = service_find(argv[1]);
1575 bt_shell_printf("Failed to unregister service object\n");
1576 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1579 local_services = g_list_remove(local_services, service);
1581 print_service(service, COLORED_DEL);
1583 g_dbus_unregister_interface(service->conn, service->path,
1586 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1589 static char *inc_find(struct service *serv, char *path)
1593 for (lc = serv->inc; lc; lc = g_list_next(lc)) {
1594 char *incp = lc->data;
1595 /* match object path */
1596 if (!strcmp(incp, path))
1603 void gatt_register_include(DBusConnection *conn, GDBusProxy *proxy,
1604 int argc, char *argv[])
1606 struct service *service, *inc_service;
1609 if (!local_services) {
1610 bt_shell_printf("No service registered\n");
1611 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1614 service = g_list_last(local_services)->data;
1617 inc_service = service_find(argv[1]);
1619 bt_shell_printf("Failed to find service object\n");
1620 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1623 inc_path = g_strdup(service->path);
1625 inc_service->inc = g_list_append(inc_service->inc, inc_path);
1627 print_service(inc_service, COLORED_NEW);
1628 print_inc_service(service, COLORED_NEW);
1630 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1633 void gatt_unregister_include(DBusConnection *conn, GDBusProxy *proxy,
1634 int argc, char *argv[])
1636 struct service *ser_inc, *service;
1639 service = service_find(argv[1]);
1641 bt_shell_printf("Failed to unregister include service"
1643 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1646 ser_inc = service_find(argv[2]);
1648 bt_shell_printf("Failed to find include service object\n");
1649 return bt_shell_noninteractive_quit(EXIT_FAILURE);
1652 path = inc_find(service, ser_inc->path);
1654 service->inc = g_list_remove(service->inc, path);
1655 inc_unregister(path);
1658 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
1661 static gboolean chrc_get_handle(const GDBusPropertyTable *property,
1662 DBusMessageIter *iter, void *data)
1664 struct chrc *chrc = data;
1666 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &chrc->handle);
1671 static void chrc_set_handle(const GDBusPropertyTable *property,
1672 DBusMessageIter *value, GDBusPendingPropertySet id,
1675 struct chrc *chrc = data;
1677 if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) {
1678 g_dbus_pending_property_error(id, "org.bluez.InvalidArguments",
1679 "Invalid arguments in method call");
1683 dbus_message_iter_get_basic(value, &chrc->handle);
1685 print_chrc(chrc, COLORED_CHG);
1687 g_dbus_pending_property_success(id);
1690 static gboolean chrc_get_uuid(const GDBusPropertyTable *property,
1691 DBusMessageIter *iter, void *data)
1693 struct chrc *chrc = data;
1695 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &chrc->uuid);
1700 static gboolean chrc_get_service(const GDBusPropertyTable *property,
1701 DBusMessageIter *iter, void *data)
1703 struct chrc *chrc = data;
1705 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
1706 &chrc->service->path);
1711 static gboolean chrc_get_value(const GDBusPropertyTable *property,
1712 DBusMessageIter *iter, void *data)
1714 struct chrc *chrc = data;
1715 DBusMessageIter array;
1717 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
1719 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
1720 &chrc->value, chrc->value_len);
1722 dbus_message_iter_close_container(iter, &array);
1727 static gboolean chrc_get_notifying(const GDBusPropertyTable *property,
1728 DBusMessageIter *iter, void *data)
1730 struct chrc *chrc = data;
1733 value = chrc->notifying ? TRUE : FALSE;
1735 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1740 static gboolean chrc_get_flags(const GDBusPropertyTable *property,
1741 DBusMessageIter *iter, void *data)
1743 struct chrc *chrc = data;
1745 DBusMessageIter array;
1747 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
1749 for (i = 0; chrc->flags[i]; i++)
1750 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
1753 dbus_message_iter_close_container(iter, &array);
1758 static gboolean chrc_get_write_acquired(const GDBusPropertyTable *property,
1759 DBusMessageIter *iter, void *data)
1761 struct chrc *chrc = data;
1764 value = chrc->write_io ? TRUE : FALSE;
1766 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1771 static gboolean chrc_write_acquired_exists(const GDBusPropertyTable *property,
1774 struct chrc *chrc = data;
1780 for (i = 0; chrc->flags[i]; i++) {
1781 if (!strcmp("write-without-response", chrc->flags[i]))
1788 static gboolean chrc_get_notify_acquired(const GDBusPropertyTable *property,
1789 DBusMessageIter *iter, void *data)
1791 struct chrc *chrc = data;
1794 value = chrc->notify_io ? TRUE : FALSE;
1796 dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
1801 static gboolean chrc_notify_acquired_exists(const GDBusPropertyTable *property,
1804 struct chrc *chrc = data;
1810 for (i = 0; chrc->flags[i]; i++) {
1811 if (!strcmp("notify", chrc->flags[i]))
1818 static const GDBusPropertyTable chrc_properties[] = {
1819 { "Handle", "q", chrc_get_handle, chrc_set_handle, NULL },
1820 { "UUID", "s", chrc_get_uuid, NULL, NULL },
1821 { "Service", "o", chrc_get_service, NULL, NULL },
1822 { "Value", "ay", chrc_get_value, NULL, NULL },
1823 { "Notifying", "b", chrc_get_notifying, NULL, NULL },
1824 { "Flags", "as", chrc_get_flags, NULL, NULL },
1825 { "WriteAcquired", "b", chrc_get_write_acquired, NULL,
1826 chrc_write_acquired_exists },
1827 { "NotifyAcquired", "b", chrc_get_notify_acquired, NULL,
1828 chrc_notify_acquired_exists },
1832 static const char *path_to_address(const char *path)
1835 DBusMessageIter iter;
1836 const char *address = path;
1838 proxy = bt_shell_get_env(path);
1840 if (g_dbus_proxy_get_property(proxy, "Address", &iter))
1841 dbus_message_iter_get_basic(&iter, &address);
1846 static int parse_options(DBusMessageIter *iter, uint16_t *offset, uint16_t *mtu,
1847 char **device, char **link,
1848 bool *prep_authorize)
1850 DBusMessageIter dict;
1852 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
1855 dbus_message_iter_recurse(iter, &dict);
1857 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1859 DBusMessageIter value, entry;
1862 dbus_message_iter_recurse(&dict, &entry);
1863 dbus_message_iter_get_basic(&entry, &key);
1865 dbus_message_iter_next(&entry);
1866 dbus_message_iter_recurse(&entry, &value);
1868 var = dbus_message_iter_get_arg_type(&value);
1869 if (strcasecmp(key, "offset") == 0) {
1870 if (var != DBUS_TYPE_UINT16)
1873 dbus_message_iter_get_basic(&value, offset);
1874 } else if (strcasecmp(key, "MTU") == 0) {
1875 if (var != DBUS_TYPE_UINT16)
1878 dbus_message_iter_get_basic(&value, mtu);
1879 } else if (strcasecmp(key, "device") == 0) {
1880 if (var != DBUS_TYPE_OBJECT_PATH)
1883 dbus_message_iter_get_basic(&value, device);
1884 } else if (strcasecmp(key, "link") == 0) {
1885 if (var != DBUS_TYPE_STRING)
1888 dbus_message_iter_get_basic(&value, link);
1889 } else if (strcasecmp(key, "prepare-authorize") == 0) {
1890 if (var != DBUS_TYPE_BOOLEAN)
1892 if (prep_authorize) {
1895 dbus_message_iter_get_basic(&value, &tmp);
1896 *prep_authorize = !!tmp;
1900 dbus_message_iter_next(&dict);
1906 static DBusMessage *read_value(DBusMessage *msg, uint8_t *value,
1910 DBusMessageIter iter, array;
1912 reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1914 dbus_message_iter_init_append(reply, &iter);
1915 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "y", &array);
1916 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
1918 dbus_message_iter_close_container(&iter, &array);
1923 struct authorize_attribute_data {
1924 DBusConnection *conn;
1929 static void authorize_read_response(const char *input, void *user_data)
1931 struct authorize_attribute_data *aad = user_data;
1932 struct chrc *chrc = aad->attribute;
1936 if (!strcmp(input, "no")) {
1937 err = "org.bluez.Error.NotAuthorized";
1942 if (aad->offset > chrc->value_len) {
1943 err = "org.bluez.Error.InvalidOffset";
1948 reply = read_value(pending_message, &chrc->value[aad->offset],
1949 chrc->value_len - aad->offset);
1951 g_dbus_send_message(aad->conn, reply);
1958 g_dbus_send_error(aad->conn, pending_message, err, NULL);
1962 static bool is_device_trusted(const char *path)
1965 DBusMessageIter iter;
1966 bool trusted = false;
1968 proxy = bt_shell_get_env(path);
1970 if (g_dbus_proxy_get_property(proxy, "Trusted", &iter))
1971 dbus_message_iter_get_basic(&iter, &trusted);
1976 struct read_attribute_data {
1981 static void proxy_read_reply(DBusMessage *message, void *user_data)
1983 struct read_attribute_data *data = user_data;
1984 DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION");
1986 DBusMessageIter iter, array;
1991 dbus_error_init(&error);
1993 if (dbus_set_error_from_message(&error, message) == TRUE) {
1994 bt_shell_printf("Failed to read: %s\n", error.name);
1995 dbus_error_free(&error);
1996 g_dbus_send_error(conn, data->msg, error.name, "%s",
2001 dbus_message_iter_init(message, &iter);
2003 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
2004 bt_shell_printf("Invalid response to read\n");
2005 g_dbus_send_error(conn, data->msg,
2006 "org.bluez.Error.InvalidArguments", NULL);
2010 dbus_message_iter_recurse(&iter, &array);
2011 dbus_message_iter_get_fixed_array(&array, &value, &len);
2014 bt_shell_printf("Unable to parse value\n");
2015 g_dbus_send_error(conn, data->msg,
2016 "org.bluez.Error.InvalidArguments", NULL);
2019 reply = read_value(data->msg, value, len);
2021 g_dbus_send_message(conn, reply);
2024 dbus_message_unref(data->msg);
2028 static void proxy_read_setup(DBusMessageIter *iter, void *user_data)
2030 DBusMessageIter dict;
2031 struct read_attribute_data *data = user_data;
2033 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
2034 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2035 DBUS_TYPE_STRING_AS_STRING
2036 DBUS_TYPE_VARIANT_AS_STRING
2037 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2040 g_dbus_dict_append_entry(&dict, "offset", DBUS_TYPE_UINT16,
2043 dbus_message_iter_close_container(iter, &dict);
2046 static DBusMessage *proxy_read_value(struct GDBusProxy *proxy, DBusMessage *msg,
2049 struct read_attribute_data *data;
2051 data = new0(struct read_attribute_data, 1);
2052 data->msg = dbus_message_ref(msg);
2053 data->offset = offset;
2055 if (g_dbus_proxy_method_call(proxy, "ReadValue", proxy_read_setup,
2056 proxy_read_reply, data, NULL))
2059 bt_shell_printf("Failed to read\n");
2061 return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
2065 static DBusMessage *chrc_read_value(DBusConnection *conn, DBusMessage *msg,
2068 struct chrc *chrc = user_data;
2069 DBusMessageIter iter;
2070 uint16_t offset = 0;
2071 char *device, *link;
2074 dbus_message_iter_init(msg, &iter);
2076 if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
2077 return g_dbus_create_error(msg,
2078 "org.bluez.Error.InvalidArguments",
2081 bt_shell_printf("[%s (%s)] ReadValue: %s offset %u link %s\n",
2082 chrc->path, bt_uuidstr_to_str(chrc->uuid),
2083 path_to_address(device), offset, link);
2086 return proxy_read_value(chrc->proxy, msg, offset);
2089 if (!is_device_trusted(device) && chrc->authorization_req) {
2090 struct authorize_attribute_data *aad;
2092 aad = g_new0(struct authorize_attribute_data, 1);
2094 aad->attribute = chrc;
2095 aad->offset = offset;
2097 str = g_strdup_printf("Authorize attribute(%s) read (yes/no):",
2100 bt_shell_prompt_input("gatt", str, authorize_read_response,
2104 pending_message = dbus_message_ref(msg);
2109 if (offset > chrc->value_len)
2110 return g_dbus_create_error(msg, "org.bluez.Error.InvalidOffset",
2113 return read_value(msg, &chrc->value[offset], chrc->value_len - offset);
2116 static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
2118 DBusMessageIter array;
2120 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
2123 dbus_message_iter_recurse(iter, &array);
2124 dbus_message_iter_get_fixed_array(&array, value, len);
2129 static int write_value(size_t *dst_len, uint8_t **dst_value, uint8_t *src_val,
2130 size_t src_len, uint16_t offset, uint16_t max_len)
2132 if ((offset + src_len) > max_len)
2135 if ((offset + src_len) != *dst_len) {
2136 *dst_len = offset + src_len;
2137 *dst_value = g_realloc(*dst_value, *dst_len);
2140 memcpy(*dst_value + offset, src_val, src_len);
2145 static void authorize_write_response(const char *input, void *user_data)
2147 struct authorize_attribute_data *aad = user_data;
2148 struct chrc *chrc = aad->attribute;
2149 bool prep_authorize = false;
2150 DBusMessageIter iter;
2156 dbus_message_iter_init(pending_message, &iter);
2157 if (parse_value_arg(&iter, &value, &value_len)) {
2158 err = "org.bluez.Error.InvalidArguments";
2163 dbus_message_iter_next(&iter);
2164 if (parse_options(&iter, NULL, NULL, NULL, NULL, &prep_authorize)) {
2165 err = "org.bluez.Error.InvalidArguments";
2170 if (!strcmp(input, "no")) {
2171 err = "org.bluez.Error.NotAuthorized";
2176 if (aad->offset > chrc->value_len) {
2177 err = "org.bluez.Error.InvalidOffset";
2182 /* Authorization check of prepare writes */
2183 if (prep_authorize) {
2184 reply = g_dbus_create_reply(pending_message, DBUS_TYPE_INVALID);
2185 g_dbus_send_message(aad->conn, reply);
2191 if (write_value(&chrc->value_len, &chrc->value, value, value_len,
2192 aad->offset, chrc->max_val_len)) {
2193 err = "org.bluez.Error.InvalidValueLength";
2198 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written",
2199 chrc->path, bt_uuidstr_to_str(chrc->uuid));
2201 g_dbus_emit_property_changed(aad->conn, chrc->path, CHRC_INTERFACE,
2204 reply = g_dbus_create_reply(pending_message, DBUS_TYPE_INVALID);
2205 g_dbus_send_message(aad->conn, reply);
2212 g_dbus_send_error(aad->conn, pending_message, err, NULL);
2216 static void proxy_write_reply(DBusMessage *message, void *user_data)
2218 struct write_attribute_data *data = user_data;
2219 DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION");
2222 dbus_error_init(&error);
2224 if (dbus_set_error_from_message(&error, message)) {
2225 bt_shell_printf("Failed to write: %s\n", error.name);
2226 g_dbus_send_error(conn, data->msg, error.name, "%s",
2229 g_dbus_send_reply(conn, data->msg, DBUS_TYPE_INVALID);
2231 dbus_message_unref(data->msg);
2235 static DBusMessage *proxy_write_value(struct GDBusProxy *proxy,
2236 DBusMessage *msg, uint8_t *value,
2237 int value_len, uint16_t offset)
2239 struct write_attribute_data *data;
2242 data = new0(struct write_attribute_data, 1);
2243 data->msg = dbus_message_ref(msg);
2244 data->iov.iov_base = (void *) value;
2245 data->iov.iov_len = value_len;
2246 data->offset = offset;
2248 if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
2249 proxy_write_reply, data, NULL))
2253 bt_shell_printf("Failed to write\n");
2255 return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
2259 static DBusMessage *chrc_write_value(DBusConnection *conn, DBusMessage *msg,
2262 struct chrc *chrc = user_data;
2263 uint16_t offset = 0;
2264 bool prep_authorize = false;
2265 char *device = NULL, *link = NULL;
2266 DBusMessageIter iter;
2271 dbus_message_iter_init(msg, &iter);
2273 if (parse_value_arg(&iter, &value, &value_len))
2274 return g_dbus_create_error(msg,
2275 "org.bluez.Error.InvalidArguments", NULL);
2277 dbus_message_iter_next(&iter);
2278 if (parse_options(&iter, &offset, NULL, &device, &link,
2280 return g_dbus_create_error(msg,
2281 "org.bluez.Error.InvalidArguments", NULL);
2283 bt_shell_printf("[%s (%s)] WriteValue: %s offset %u link %s\n",
2284 chrc->path, bt_uuidstr_to_str(chrc->uuid),
2285 path_to_address(device), offset, link);
2287 bt_shell_hexdump(value, value_len);
2290 return proxy_write_value(chrc->proxy, msg, value, value_len,
2293 if (!is_device_trusted(device) && chrc->authorization_req) {
2294 struct authorize_attribute_data *aad;
2296 aad = g_new0(struct authorize_attribute_data, 1);
2298 aad->attribute = chrc;
2299 aad->offset = offset;
2301 str = g_strdup_printf("Authorize attribute(%s) write (yes/no):",
2304 bt_shell_prompt_input("gatt", str, authorize_write_response,
2308 pending_message = dbus_message_ref(msg);
2313 if (offset > chrc->value_len)
2314 return g_dbus_create_error(msg,
2315 "org.bluez.Error.InvalidOffset", NULL);
2318 /* Authorization check of prepare writes */
2320 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2322 if (write_value(&chrc->value_len, &chrc->value, value, value_len,
2323 offset, chrc->max_val_len))
2324 return g_dbus_create_error(msg,
2325 "org.bluez.Error.InvalidValueLength", NULL);
2327 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written",
2328 chrc->path, bt_uuidstr_to_str(chrc->uuid));
2330 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, "Value");
2332 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2335 static DBusMessage *create_sock(struct chrc *chrc, DBusMessage *msg)
2342 if (socketpair(AF_LOCAL, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC,
2344 return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
2347 dir = dbus_message_has_member(msg, "AcquireWrite");
2349 io = sock_io_new(fds[!dir], chrc);
2353 return g_dbus_create_error(msg, "org.bluez.Error.Failed", "%s",
2357 reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, &fds[dir],
2358 DBUS_TYPE_UINT16, &chrc->mtu,
2364 chrc->write_io = io;
2366 chrc->notify_io = io;
2368 bt_shell_printf("[" COLORED_CHG "] Attribute %s %s sock acquired\n",
2369 chrc->path, dir ? "Write" : "Notify");
2374 static DBusMessage *chrc_acquire_write(DBusConnection *conn, DBusMessage *msg,
2377 struct chrc *chrc = user_data;
2378 DBusMessageIter iter;
2380 char *device = NULL, *link= NULL;
2382 dbus_message_iter_init(msg, &iter);
2385 return g_dbus_create_error(msg,
2386 "org.bluez.Error.NotPermitted",
2389 if (parse_options(&iter, NULL, &chrc->mtu, &device, &link, NULL))
2390 return g_dbus_create_error(msg,
2391 "org.bluez.Error.InvalidArguments",
2394 bt_shell_printf("AcquireWrite: %s link %s\n", path_to_address(device),
2397 reply = create_sock(chrc, msg);
2400 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
2406 static DBusMessage *chrc_acquire_notify(DBusConnection *conn, DBusMessage *msg,
2409 struct chrc *chrc = user_data;
2410 DBusMessageIter iter;
2412 char *device = NULL, *link = NULL;
2414 dbus_message_iter_init(msg, &iter);
2416 if (chrc->notify_io)
2417 return g_dbus_create_error(msg,
2418 "org.bluez.Error.NotPermitted",
2421 if (parse_options(&iter, NULL, &chrc->mtu, &device, &link, NULL))
2422 return g_dbus_create_error(msg,
2423 "org.bluez.Error.InvalidArguments",
2426 bt_shell_printf("AcquireNotify: %s link %s\n", path_to_address(device),
2429 reply = create_sock(chrc, msg);
2431 if (chrc->notify_io)
2432 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
2438 struct notify_attribute_data {
2444 static void proxy_notify_reply(DBusMessage *message, void *user_data)
2446 struct notify_attribute_data *data = user_data;
2447 DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION");
2450 dbus_error_init(&error);
2452 if (dbus_set_error_from_message(&error, message) == TRUE) {
2453 bt_shell_printf("Failed to %s: %s\n",
2454 data->enable ? "StartNotify" : "StopNotify",
2456 dbus_error_free(&error);
2457 g_dbus_send_error(conn, data->msg, error.name, "%s",
2462 g_dbus_send_reply(conn, data->msg, DBUS_TYPE_INVALID);
2464 data->chrc->notifying = data->enable;
2465 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) "
2466 "notifications %s\n",
2468 bt_uuidstr_to_str(data->chrc->uuid),
2469 data->enable ? "enabled" : "disabled");
2470 g_dbus_emit_property_changed(conn, data->chrc->path, CHRC_INTERFACE,
2474 dbus_message_unref(data->msg);
2478 static DBusMessage *proxy_notify(struct chrc *chrc, DBusMessage *msg,
2481 struct notify_attribute_data *data;
2485 method = "StartNotify";
2487 method = "StopNotify";
2489 data = new0(struct notify_attribute_data, 1);
2491 data->msg = dbus_message_ref(msg);
2492 data->enable = enable;
2494 if (g_dbus_proxy_method_call(chrc->proxy, method, NULL,
2495 proxy_notify_reply, data, NULL))
2498 return g_dbus_create_error(msg, "org.bluez.Error.InvalidArguments",
2502 static DBusMessage *chrc_start_notify(DBusConnection *conn, DBusMessage *msg,
2505 struct chrc *chrc = user_data;
2507 if (chrc->notifying)
2508 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2511 return proxy_notify(chrc, msg, true);
2513 chrc->notifying = true;
2514 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) notifications "
2515 "enabled", chrc->path, bt_uuidstr_to_str(chrc->uuid));
2516 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
2519 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2522 static DBusMessage *chrc_stop_notify(DBusConnection *conn, DBusMessage *msg,
2525 struct chrc *chrc = user_data;
2527 if (!chrc->notifying)
2528 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2531 return proxy_notify(chrc, msg, false);
2533 chrc->notifying = false;
2534 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) notifications "
2535 "disabled", chrc->path, bt_uuidstr_to_str(chrc->uuid));
2536 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE,
2539 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2542 static DBusMessage *chrc_confirm(DBusConnection *conn, DBusMessage *msg,
2545 struct chrc *chrc = user_data;
2547 bt_shell_printf("Attribute %s (%s) indication confirm received",
2548 chrc->path, bt_uuidstr_to_str(chrc->uuid));
2550 return dbus_message_new_method_return(msg);
2553 static const GDBusMethodTable chrc_methods[] = {
2554 { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
2555 GDBUS_ARGS({ "value", "ay" }),
2557 { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
2558 { "options", "a{sv}" }),
2559 NULL, chrc_write_value) },
2560 { GDBUS_METHOD("AcquireWrite", GDBUS_ARGS({ "options", "a{sv}" }),
2561 NULL, chrc_acquire_write) },
2562 { GDBUS_METHOD("AcquireNotify", GDBUS_ARGS({ "options", "a{sv}" }),
2563 NULL, chrc_acquire_notify) },
2564 { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, chrc_start_notify) },
2565 { GDBUS_METHOD("StopNotify", NULL, NULL, chrc_stop_notify) },
2566 { GDBUS_METHOD("Confirm", NULL, NULL, chrc_confirm) },
2570 static void chrc_set_value(const char *input, void *user_data)
2572 struct chrc *chrc = user_data;
2574 g_free(chrc->value);
2576 chrc->value = str2bytearray((char *) input, &chrc->value_len);
2579 print_chrc(chrc, COLORED_DEL);
2580 chrc_unregister(chrc);
2583 chrc->max_val_len = chrc->value_len;
2586 static gboolean attr_authorization_flag_exists(char **flags)
2590 for (i = 0; flags[i]; i++) {
2591 if (!strcmp("authorize", flags[i]))
2598 void gatt_register_chrc(DBusConnection *conn, GDBusProxy *proxy,
2599 int argc, char *argv[])
2601 struct service *service;
2604 if (!local_services) {
2605 bt_shell_printf("No service registered\n");
2606 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2609 service = g_list_last(local_services)->data;
2611 chrc = g_new0(struct chrc, 1);
2612 chrc->service = service;
2613 chrc->uuid = g_strdup(argv[1]);
2614 chrc->path = g_strdup_printf("%s/chrc%u", service->path,
2615 g_list_length(service->chrcs));
2616 chrc->flags = g_strsplit(argv[2], ",", -1);
2617 chrc->authorization_req = attr_authorization_flag_exists(chrc->flags);
2620 chrc->handle = parse_handle(argv[3]);
2622 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2625 if (g_dbus_register_interface(conn, chrc->path, CHRC_INTERFACE,
2626 chrc_methods, NULL, chrc_properties,
2627 chrc, chrc_free) == FALSE) {
2628 bt_shell_printf("Failed to register characteristic object\n");
2630 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2633 service->chrcs = g_list_append(service->chrcs, chrc);
2635 print_chrc(chrc, COLORED_NEW);
2637 bt_shell_prompt_input(chrc->path, "Enter value:", chrc_set_value, chrc);
2639 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
2642 static struct chrc *chrc_find(const char *pattern)
2645 struct service *service;
2648 for (l = local_services; l; l = g_list_next(l)) {
2651 for (lc = service->chrcs; lc; lc = g_list_next(lc)) {
2654 /* match object path */
2655 if (!strcmp(chrc->path, pattern))
2659 if (!strcmp(chrc->uuid, pattern))
2667 void gatt_unregister_chrc(DBusConnection *conn, GDBusProxy *proxy,
2668 int argc, char *argv[])
2672 chrc = chrc_find(argv[1]);
2674 bt_shell_printf("Failed to unregister characteristic object\n");
2675 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2678 chrc->service->chrcs = g_list_remove(chrc->service->chrcs, chrc);
2680 chrc_unregister(chrc);
2682 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
2685 static DBusMessage *desc_read_value(DBusConnection *conn, DBusMessage *msg,
2688 struct desc *desc = user_data;
2689 DBusMessageIter iter;
2690 uint16_t offset = 0;
2691 char *device = NULL, *link = NULL;
2693 dbus_message_iter_init(msg, &iter);
2695 if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
2696 return g_dbus_create_error(msg,
2697 "org.bluez.Error.InvalidArguments",
2700 bt_shell_printf("[%s (%s)] ReadValue: %s offset %u link %s\n",
2701 desc->path, bt_uuidstr_to_str(desc->uuid),
2702 path_to_address(device), offset, link);
2704 if (offset > desc->value_len)
2705 return g_dbus_create_error(msg, "org.bluez.Error.InvalidOffset",
2708 return read_value(msg, &desc->value[offset], desc->value_len - offset);
2711 static DBusMessage *desc_write_value(DBusConnection *conn, DBusMessage *msg,
2714 struct desc *desc = user_data;
2715 DBusMessageIter iter;
2716 uint16_t offset = 0;
2717 char *device = NULL, *link = NULL;
2721 dbus_message_iter_init(msg, &iter);
2723 if (parse_value_arg(&iter, &value, &value_len))
2724 return g_dbus_create_error(msg,
2725 "org.bluez.Error.InvalidArguments", NULL);
2727 dbus_message_iter_next(&iter);
2728 if (parse_options(&iter, &offset, NULL, &device, &link, NULL))
2729 return g_dbus_create_error(msg,
2730 "org.bluez.Error.InvalidArguments", NULL);
2732 if (offset > desc->value_len)
2733 return g_dbus_create_error(msg,
2734 "org.bluez.Error.InvalidOffset", NULL);
2736 if (write_value(&desc->value_len, &desc->value, value,
2737 value_len, offset, desc->max_val_len))
2738 return g_dbus_create_error(msg,
2739 "org.bluez.Error.InvalidValueLength", NULL);
2741 bt_shell_printf("[%s (%s)] WriteValue: %s offset %u link %s\n",
2742 desc->path, bt_uuidstr_to_str(desc->uuid),
2743 path_to_address(device), offset, link);
2745 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) written",
2746 desc->path, bt_uuidstr_to_str(desc->uuid));
2748 g_dbus_emit_property_changed(conn, desc->path, CHRC_INTERFACE, "Value");
2750 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2753 static const GDBusMethodTable desc_methods[] = {
2754 { GDBUS_ASYNC_METHOD("ReadValue", GDBUS_ARGS({ "options", "a{sv}" }),
2755 GDBUS_ARGS({ "value", "ay" }),
2757 { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" },
2758 { "options", "a{sv}" }),
2759 NULL, desc_write_value) },
2763 static gboolean desc_get_handle(const GDBusPropertyTable *property,
2764 DBusMessageIter *iter, void *data)
2766 struct desc *desc = data;
2768 dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &desc->handle);
2773 static void desc_set_handle(const GDBusPropertyTable *property,
2774 DBusMessageIter *value, GDBusPendingPropertySet id,
2777 struct desc *desc = data;
2779 if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16) {
2780 g_dbus_pending_property_error(id, "org.bluez.InvalidArguments",
2781 "Invalid arguments in method call");
2785 dbus_message_iter_get_basic(value, &desc->handle);
2787 print_desc(desc, COLORED_CHG);
2789 g_dbus_pending_property_success(id);
2792 static gboolean desc_get_uuid(const GDBusPropertyTable *property,
2793 DBusMessageIter *iter, void *data)
2795 struct desc *desc = data;
2797 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &desc->uuid);
2802 static gboolean desc_get_chrc(const GDBusPropertyTable *property,
2803 DBusMessageIter *iter, void *data)
2805 struct desc *desc = data;
2807 dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH,
2813 static gboolean desc_get_value(const GDBusPropertyTable *property,
2814 DBusMessageIter *iter, void *data)
2816 struct desc *desc = data;
2817 DBusMessageIter array;
2819 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
2822 dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
2826 dbus_message_iter_close_container(iter, &array);
2831 static gboolean desc_get_flags(const GDBusPropertyTable *property,
2832 DBusMessageIter *iter, void *data)
2834 struct desc *desc = data;
2836 DBusMessageIter array;
2838 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
2840 for (i = 0; desc->flags[i]; i++)
2841 dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
2844 dbus_message_iter_close_container(iter, &array);
2849 static const GDBusPropertyTable desc_properties[] = {
2850 { "Handle", "q", desc_get_handle, desc_set_handle, NULL },
2851 { "UUID", "s", desc_get_uuid, NULL, NULL },
2852 { "Characteristic", "o", desc_get_chrc, NULL, NULL },
2853 { "Value", "ay", desc_get_value, NULL, NULL },
2854 { "Flags", "as", desc_get_flags, NULL, NULL },
2858 static void desc_set_value(const char *input, void *user_data)
2860 struct desc *desc = user_data;
2862 g_free(desc->value);
2864 desc->value = str2bytearray((char *) input, &desc->value_len);
2867 print_desc(desc, COLORED_DEL);
2868 desc_unregister(desc);
2871 desc->max_val_len = desc->value_len;
2874 void gatt_register_desc(DBusConnection *conn, GDBusProxy *proxy,
2875 int argc, char *argv[])
2877 struct service *service;
2880 if (!local_services) {
2881 bt_shell_printf("No service registered\n");
2882 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2885 service = g_list_last(local_services)->data;
2887 if (!service->chrcs) {
2888 bt_shell_printf("No characteristic registered\n");
2889 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2892 desc = g_new0(struct desc, 1);
2893 desc->chrc = g_list_last(service->chrcs)->data;
2894 desc->uuid = g_strdup(argv[1]);
2895 desc->path = g_strdup_printf("%s/desc%u", desc->chrc->path,
2896 g_list_length(desc->chrc->descs));
2897 desc->flags = g_strsplit(argv[2], ",", -1);
2900 desc->handle = parse_handle(argv[3]);
2902 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2905 if (g_dbus_register_interface(conn, desc->path, DESC_INTERFACE,
2906 desc_methods, NULL, desc_properties,
2907 desc, desc_free) == FALSE) {
2908 bt_shell_printf("Failed to register descriptor object\n");
2910 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2913 desc->chrc->descs = g_list_append(desc->chrc->descs, desc);
2915 print_desc(desc, COLORED_NEW);
2917 bt_shell_prompt_input(desc->path, "Enter value:", desc_set_value, desc);
2919 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
2922 static struct desc *desc_find(const char *pattern)
2925 struct service *service;
2929 for (l = local_services; l; l = g_list_next(l)) {
2932 for (lc = service->chrcs; lc; lc = g_list_next(lc)) {
2935 for (ld = chrc->descs; ld; ld = g_list_next(ld)) {
2938 /* match object path */
2939 if (!strcmp(desc->path, pattern))
2943 if (!strcmp(desc->uuid, pattern))
2952 void gatt_unregister_desc(DBusConnection *conn, GDBusProxy *proxy,
2953 int argc, char *argv[])
2957 desc = desc_find(argv[1]);
2959 bt_shell_printf("Failed to unregister descriptor object\n");
2960 return bt_shell_noninteractive_quit(EXIT_FAILURE);
2963 desc->chrc->descs = g_list_remove(desc->chrc->descs, desc);
2965 desc_unregister(desc);
2967 return bt_shell_noninteractive_quit(EXIT_SUCCESS);
2970 static GDBusProxy *select_service(GDBusProxy *proxy)
2974 for (l = services; l; l = g_list_next(l)) {
2975 GDBusProxy *p = l->data;
2977 if (proxy == p || g_str_has_prefix(g_dbus_proxy_get_path(proxy),
2978 g_dbus_proxy_get_path(p)))
2985 static void proxy_property_changed(GDBusProxy *proxy, const char *name,
2986 DBusMessageIter *iter, void *user_data)
2988 DBusConnection *conn = bt_shell_get_env("DBUS_CONNECTION");
2989 struct chrc *chrc = user_data;
2991 bt_shell_printf("[" COLORED_CHG "] Attribute %s (%s) %s:\n",
2992 chrc->path, bt_uuidstr_to_str(chrc->uuid), name);
2994 if (!strcmp(name, "Value")) {
2995 DBusMessageIter array;
2999 if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
3000 dbus_message_iter_recurse(iter, &array);
3001 dbus_message_iter_get_fixed_array(&array, &value, &len);
3002 write_value(&chrc->value_len, &chrc->value, value, len,
3003 0, chrc->max_val_len);
3004 bt_shell_hexdump(value, len);
3008 g_dbus_emit_property_changed(conn, chrc->path, CHRC_INTERFACE, name);
3011 static void clone_chrc(struct GDBusProxy *proxy)
3013 struct service *service;
3015 DBusMessageIter iter;
3016 DBusMessageIter array;
3021 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
3024 dbus_message_iter_get_basic(&iter, &uuid);
3026 if (g_dbus_proxy_get_property(proxy, "Flags", &iter) == FALSE)
3029 dbus_message_iter_recurse(&iter, &array);
3031 for (i = 0; dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING;
3033 dbus_message_iter_get_basic(&array, &flags[i]);
3034 dbus_message_iter_next(&array);
3039 service = g_list_last(local_services)->data;
3041 chrc = g_new0(struct chrc, 1);
3042 chrc->service = service;
3043 chrc->proxy = proxy;
3044 chrc->uuid = g_strdup(uuid);
3045 chrc->path = g_strdup_printf("%s/chrc%u", service->path,
3046 g_list_length(service->chrcs));
3047 chrc->flags = g_strdupv(flags);
3049 if (g_dbus_register_interface(service->conn, chrc->path, CHRC_INTERFACE,
3050 chrc_methods, NULL, chrc_properties,
3051 chrc, chrc_free) == FALSE) {
3052 bt_shell_printf("Failed to register characteristic object\n");
3054 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3057 g_dbus_proxy_set_property_watch(proxy, proxy_property_changed, chrc);
3059 service->chrcs = g_list_append(service->chrcs, chrc);
3061 print_chrc(chrc, COLORED_NEW);
3064 static void clone_chrcs(struct GDBusProxy *proxy)
3068 for (l = characteristics; l; l = g_list_next(l)) {
3069 GDBusProxy *p = l->data;
3071 if (g_str_has_prefix(g_dbus_proxy_get_path(p),
3072 g_dbus_proxy_get_path(proxy)))
3077 static void clone_service(struct GDBusProxy *proxy)
3079 struct service *service;
3080 DBusMessageIter iter;
3082 dbus_bool_t primary;
3084 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
3087 dbus_message_iter_get_basic(&iter, &uuid);
3089 if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE)
3092 dbus_message_iter_get_basic(&iter, &primary);
3094 if (!strcmp(uuid, "00001800-0000-1000-8000-00805f9b34fb") ||
3095 !strcmp(uuid, "00001801-0000-1000-8000-00805f9b34fb"))
3098 service = g_new0(struct service, 1);
3099 service->conn = bt_shell_get_env("DBUS_CONNECTION");
3100 service->proxy = proxy;
3101 service->path = g_strdup_printf("%s/service%u", APP_PATH,
3102 g_list_length(local_services));
3103 service->uuid = g_strdup(uuid);
3104 service->primary = primary;
3106 if (g_dbus_register_interface(service->conn, service->path,
3107 SERVICE_INTERFACE, NULL, NULL,
3108 service_properties, service,
3109 service_free) == FALSE) {
3110 bt_shell_printf("Failed to register service object\n");
3111 service_free(service);
3112 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3115 print_service(service, COLORED_NEW);
3117 local_services = g_list_append(local_services, service);
3122 static void clone_device(struct GDBusProxy *proxy)
3126 for (l = services; l; l = g_list_next(l)) {
3127 struct GDBusProxy *p = l->data;
3129 if (g_str_has_prefix(g_dbus_proxy_get_path(p),
3130 g_dbus_proxy_get_path(proxy)))
3135 static void service_clone(const char *input, void *user_data)
3137 struct GDBusProxy *proxy = user_data;
3139 if (!strcmp(input, "yes"))
3140 return clone_service(proxy);
3141 else if (!strcmp(input, "no"))
3142 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3143 else if (!strcmp(input, "all"))
3144 return clone_device(proxy);
3146 bt_shell_printf("Invalid option: %s\n", input);
3148 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3151 static void device_clone(const char *input, void *user_data)
3153 struct GDBusProxy *proxy = user_data;
3155 if (!strcmp(input, "yes"))
3156 return clone_device(proxy);
3157 else if (!strcmp(input, "no"))
3158 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3160 bt_shell_printf("Invalid option: %s\n", input);
3162 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3165 static const char *proxy_get_name(struct GDBusProxy *proxy)
3167 DBusMessageIter iter;
3171 if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
3174 dbus_message_iter_get_basic(&iter, &uuid);
3176 str = bt_uuidstr_to_str(uuid);
3178 return str ? str : uuid;
3181 static const char *proxy_get_alias(struct GDBusProxy *proxy)
3183 DBusMessageIter iter;
3186 if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == FALSE)
3189 dbus_message_iter_get_basic(&iter, &alias);
3194 void gatt_clone_attribute(GDBusProxy *proxy, int argc, char *argv[])
3196 GDBusProxy *service = NULL;
3199 proxy = gatt_select_attribute(proxy, argv[1]);
3201 bt_shell_printf("Unable to find attribute %s\n",
3203 return bt_shell_noninteractive_quit(EXIT_FAILURE);
3207 if (!strcmp(g_dbus_proxy_get_interface(proxy), DEVICE_INTERFACE)) {
3208 bt_shell_prompt_input(proxy_get_alias(proxy),
3210 device_clone, proxy);
3213 /* Only clone services */
3214 service = select_service(proxy);
3216 bt_shell_prompt_input(proxy_get_name(proxy),
3217 "Clone (yes/no/all):",
3218 service_clone, service);
3222 return bt_shell_noninteractive_quit(EXIT_FAILURE);