gatt: Fix not establishing a socket for each device
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Mon, 24 Jul 2023 20:57:34 +0000 (13:57 -0700)
committerAyush Garg <ayush.garg@samsung.com>
Fri, 5 Jan 2024 13:34:03 +0000 (19:04 +0530)
AcquireWrite and AcquireNotify shall establish a socket pair for each
device connected otherwise the application cannot distinct the
operations of each client.

Fixes: https://github.com/bluez/bluez/issues/460

src/gatt-database.c

index d02e1c5..d260708 100644 (file)
@@ -110,6 +110,12 @@ struct external_profile {
        struct queue *profiles; /* btd_profile list */
 };
 
+struct client_io {
+       struct bt_att *att;
+       unsigned int disconn_id;
+       struct io *io;
+};
+
 struct external_chrc {
        struct external_service *service;
        char *path;
@@ -119,8 +125,8 @@ struct external_chrc {
        uint32_t perm;
        uint32_t ccc_perm;
        uint16_t mtu;
-       struct io *write_io;
-       struct io *notify_io;
+       struct queue *write_ios;
+       struct queue *notify_ios;
        struct gatt_db_attribute *attrib;
        struct gatt_db_attribute *ccc;
        struct queue *pending_reads;
@@ -531,12 +537,22 @@ static void cancel_pending_write(void *data)
        op->owner_queue = NULL;
 }
 
+static void client_io_free(void *data)
+{
+       struct client_io *client = data;
+
+       bt_att_unregister_disconnect(client->att, client->disconn_id);
+       bt_att_unref(client->att);
+       io_destroy(client->io);
+       free(client);
+}
+
 static void chrc_free(void *data)
 {
        struct external_chrc *chrc = data;
 
-       io_destroy(chrc->write_io);
-       io_destroy(chrc->notify_io);
+       queue_destroy(chrc->write_ios, client_io_free);
+       queue_destroy(chrc->notify_ios, client_io_free);
 
        queue_destroy(chrc->pending_reads, cancel_pending_read);
        queue_destroy(chrc->pending_writes, cancel_pending_write);
@@ -3042,18 +3058,29 @@ static void flush_pending_writes(GDBusProxy *proxy,
        queue_remove_all(owner_queue, NULL, NULL, NULL);
 }
 
+static bool match_client_io(const void *data, const void *user_data)
+{
+       const struct client_io *client = data;
+       const struct io *io = user_data;
+
+       return client->io == io;
+}
+
 static bool sock_hup(struct io *io, void *user_data)
 {
        struct external_chrc *chrc = user_data;
+       struct client_io *client;
 
        DBG("%p closed\n", io);
 
-       if (io == chrc->write_io)
-               chrc->write_io = NULL;
-       else
-               chrc->notify_io = NULL;
+       client = queue_remove_if(chrc->write_ios, match_client_io, io);
+       if (!client) {
+               client = queue_remove_if(chrc->notify_ios, match_client_io, io);
+               if (!client)
+                       return false;
+       }
 
-       io_destroy(io);
+       client_io_free(client);
 
        return false;
 }
@@ -3112,9 +3139,67 @@ static int sock_io_send(struct io *io, const void *data, size_t len)
 #endif
 }
 
+static void att_disconnect_cb(int err, void *user_data)
+{
+       struct client_io *client = user_data;
+
+       /* If ATT is disconnected shutdown correspondent client IO so sock_hup
+       * is triggered and the server socket is closed.
+       */
+       io_shutdown(client->io);
+}
+
+static struct client_io *
+client_io_new(struct external_chrc *chrc, int fd, struct bt_att *att)
+{
+       struct client_io *client;
+
+       client = new0(struct client_io, 1);
+       client->att = bt_att_ref(att);
+       client->disconn_id = bt_att_register_disconnect(att, att_disconnect_cb,
+                                                       client, NULL);
+       client->io = sock_io_new(fd, chrc);
+
+       return client;
+}
+
+static bool match_client_att(const void *data, const void *user_data)
+{
+       const struct client_io *client = data;
+       const struct bt_att *att = user_data;
+
+       /* Always match if ATT instance is not set since that is used by
+       * clear_cc_state to clear all instances.
+       */
+       if (!att)
+               return true;
+
+       return client->att == att;
+}
+
+static struct client_io *
+client_write_io_get(struct external_chrc *chrc, int fd, struct bt_att *att)
+{
+       struct client_io *client;
+
+       client = queue_find(chrc->write_ios, match_client_att, att);
+       if (client)
+               return client;
+
+       client = client_io_new(chrc, fd, att);
+
+       if (!chrc->write_ios)
+               chrc->write_ios = queue_new();
+
+       queue_push_tail(chrc->write_ios, client);
+
+       return client;
+}
+
 static void acquire_write_reply(DBusMessage *message, void *user_data)
 {
        struct pending_op *op = user_data;
+       struct client_io *client;
        struct external_chrc *chrc;
        DBusError err;
        int fd;
@@ -3160,10 +3245,12 @@ static void acquire_write_reply(DBusMessage *message, void *user_data)
 
        DBG("AcquireWrite success: fd %d MTU %u\n", fd, mtu);
 
-       chrc->write_io = sock_io_new(fd, chrc);
+       client = client_write_io_get(chrc, fd, op->att);
+       if (!client)
+               goto retry;
 
        while ((op = queue_peek_head(chrc->pending_writes)) != NULL) {
-               if (sock_io_send(chrc->write_io, op->data.iov_base,
+               if (sock_io_send(client->io, op->data.iov_base,
                                        op->data.iov_len) < 0)
                        goto retry;
 
@@ -3224,6 +3311,27 @@ static struct pending_op *acquire_write(struct external_chrc *chrc,
        return NULL;
 }
 
+static struct client_io *
+client_notify_io_get(struct external_chrc *chrc, int fd, struct bt_att *att)
+{
+       struct client_io *client;
+
+       client = queue_find(chrc->notify_ios, match_client_att, att);
+       if (client)
+               return client;
+
+       client = client_io_new(chrc, fd, att);
+
+       io_set_read_handler(client->io, sock_io_read, chrc, NULL);
+
+       if (!chrc->notify_ios)
+               chrc->notify_ios = queue_new();
+
+       queue_push_tail(chrc->notify_ios, client);
+
+       return client;
+}
+
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
 static void start_notify_setup(DBusMessageIter *iter, void *user_data)
 {
@@ -3245,6 +3353,7 @@ static void acquire_notify_reply(DBusMessage *message, void *user_data)
 {
        struct pending_op *op = user_data;
        struct external_chrc *chrc = (void *) op->data.iov_base;
+       struct client_io *client;
        DBusError err;
        int fd;
        uint16_t mtu;
@@ -3278,8 +3387,9 @@ static void acquire_notify_reply(DBusMessage *message, void *user_data)
 
        DBG("AcquireNotify success: fd %d MTU %u\n", fd, mtu);
 
-       chrc->notify_io = sock_io_new(fd, chrc);
-       io_set_read_handler(chrc->notify_io, sock_io_read, chrc, NULL);
+       client = client_notify_io_get(chrc, fd, op->att);
+       if (!client)
+               goto retry;
 
        __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
 
@@ -3333,6 +3443,7 @@ static void stop_notify_setup(DBusMessageIter *iter, void *user_data)
 static uint8_t ccc_write_cb(struct pending_op *op, void *user_data)
 {
        struct external_chrc *chrc = user_data;
+       struct client_io *client;
        DBusMessageIter iter;
        uint16_t value;
 
@@ -3345,18 +3456,19 @@ static uint8_t ccc_write_cb(struct pending_op *op, void *user_data)
                if (!chrc->ntfy_cnt)
                        goto done;
 
-               if (__sync_sub_and_fetch(&chrc->ntfy_cnt, 1))
+               client = queue_remove_if(chrc->notify_ios, match_client_att,
+                                                       op ? op->att : NULL);
+               if (client) {
+                       client_io_free(client);
+                       __sync_sub_and_fetch(&chrc->ntfy_cnt, 1);
                        goto done;
-
-               if (chrc->notify_io) {
-                       io_destroy(chrc->notify_io);
-                       chrc->notify_io = NULL;
+               }
+               if (__sync_sub_and_fetch(&chrc->ntfy_cnt, 1))
 #ifdef TIZEN_FEATURE_BLUEZ_MODIFY
                        DBG("HUP is not getting generated so calling StopNotify");
 #else
                        goto done;
 #endif
-               }
 
                /*
                 * Send request to stop notifying. This is best-effort
@@ -3386,7 +3498,8 @@ static uint8_t ccc_write_cb(struct pending_op *op, void *user_data)
                (value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
                return BT_ERROR_CCC_IMPROPERLY_CONFIGURED;
 
-       if (chrc->notify_io) {
+       client = queue_find(chrc->notify_ios, match_client_att, op->att);
+       if (client) {
                __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
                goto done;
        }
@@ -3807,6 +3920,7 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
                                        void *user_data)
 {
        struct external_chrc *chrc = user_data;
+       struct client_io *client;
        struct btd_device *device;
        struct queue *queue;
        DBusMessageIter iter;
@@ -3846,8 +3960,9 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
        if (opcode == BT_ATT_OP_EXEC_WRITE_REQ)
                chrc->prep_authorized = false;
 
-       if (chrc->write_io) {
-               if (sock_io_send(chrc->write_io, value, len) < 0) {
+       client = queue_find(chrc->write_ios, match_client_att, att);
+       if (client) {
+               if (sock_io_send(client->io, value, len) < 0) {
                        error("Unable to write: %s", strerror(errno));
                        goto fail;
                }