From be52c05a25c0cbadcd605b24d66d0c0b682aee18 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 20 Aug 2006 19:05:10 +0000 Subject: First round of Passkey Release method support --- common/glib-ectomy.c | 18 +++----- common/list.c | 10 ++++ common/list.h | 2 + hcid/dbus-security.c | 127 +++++++++++++++++++++++++++++++++++++++++---------- hcid/dbus.c | 12 ++++- hcid/dbus.h | 38 ++++++++------- hcid/main.c | 4 +- 7 files changed, 155 insertions(+), 56 deletions(-) diff --git a/common/glib-ectomy.c b/common/glib-ectomy.c index fb737642..214ee571 100644 --- a/common/glib-ectomy.c +++ b/common/glib-ectomy.c @@ -161,14 +161,6 @@ guint g_io_add_watch(GIOChannel *channel, GIOCondition condition, func, user_data, NULL); } -static void timeout_free(void *data, void *user_data) -{ - struct timeout *t = data; - - if (t) - free (t); -} - static GMainContext *g_main_context_default() { if (default_context) @@ -342,16 +334,19 @@ void g_main_loop_run(GMainLoop *loop) void g_main_loop_quit(GMainLoop *loop) { - struct watch *w; + struct watch *w, *next; loop->bail = 1; - for (w = watch_head.next; w; w = w->next) { + for (w = watch_head.next; w; w = next) { + next = w->next; if (w->destroy) w->destroy(w->user_data); watch_head.next = w->next; free(w); } + + watch_head.next = NULL; } void g_main_loop_unref(GMainLoop *loop) @@ -359,9 +354,10 @@ void g_main_loop_unref(GMainLoop *loop) if (!loop->context) return; - slist_foreach(loop->context->ltimeout, timeout_free, NULL); + slist_foreach(loop->context->ltimeout, (slist_func_t)free, NULL); slist_free(loop->context->ltimeout); free(loop->context); + loop->context = NULL; } guint g_timeout_add(guint interval, GSourceFunc function, gpointer data) diff --git a/common/list.c b/common/list.c index 31ae9d35..42d377b7 100644 --- a/common/list.c +++ b/common/list.c @@ -99,6 +99,16 @@ struct slist *slist_find(struct slist *list, const void *data, return NULL; } +int slist_length(struct slist *list) +{ + int len; + + for (len = 0; list != NULL; list = list->next) + len++; + + return len; +} + void slist_foreach(struct slist *list, slist_func_t func, void *user_data) { while (list) { diff --git a/common/list.h b/common/list.h index 026d470c..c43756a9 100644 --- a/common/list.h +++ b/common/list.h @@ -40,6 +40,8 @@ struct slist *slist_remove(struct slist *list, void *data); struct slist *slist_find(struct slist *list, const void *data, cmp_func_t cmp_func); +int slist_length(struct slist *list); + void slist_foreach(struct slist *list, slist_func_t func, void *user_data); void slist_free(struct slist *list); diff --git a/hcid/dbus-security.c b/hcid/dbus-security.c index 1495e44a..91a03e5d 100644 --- a/hcid/dbus-security.c +++ b/hcid/dbus-security.c @@ -43,6 +43,8 @@ static struct passkey_agent *default_agent = NULL; +static void release_agent(struct passkey_agent *agent); + static void passkey_agent_free(struct passkey_agent *agent) { struct slist *l; @@ -50,13 +52,6 @@ static void passkey_agent_free(struct passkey_agent *agent) if (!agent) return; - if (agent->name) - free(agent->name); - if (agent->path) - free(agent->path); - if (agent->addr) - free(agent->addr); - for (l = agent->pending_requests; l != NULL; l = l->next) { struct pending_agent_request *req = l->data; @@ -66,10 +61,21 @@ static void passkey_agent_free(struct passkey_agent *agent) free(req->path); dbus_pending_call_cancel(req->call); dbus_pending_call_unref(req->call); - dbus_connection_unref(req->conn); free(req); } + if (!agent->exited) + release_agent(agent); + + if (agent->name) + free(agent->name); + if (agent->path) + free(agent->path); + if (agent->addr) + free(agent->addr); + if (agent->conn) + dbus_connection_unref(agent->conn); + slist_free(agent->pending_requests); free(agent); @@ -85,12 +91,15 @@ static void default_agent_exited(const char *name, void *data) return; } + default_agent->exited = 1; + passkey_agent_free(default_agent); default_agent = NULL; } -static struct passkey_agent *passkey_agent_new(const char *name, - const char *path, const char *addr) +static struct passkey_agent *passkey_agent_new(struct hci_dbus_data *pdata, DBusConnection *conn, + const char *name, const char *path, + const char *addr) { struct passkey_agent *agent; @@ -100,6 +109,8 @@ static struct passkey_agent *passkey_agent_new(const char *name, memset(agent, 0, sizeof(struct passkey_agent)); + agent->pdata = pdata; + agent->name = strdup(name); if (!agent->name) goto mem_fail; @@ -114,9 +125,13 @@ static struct passkey_agent *passkey_agent_new(const char *name, goto mem_fail; } + agent->conn = dbus_connection_ref(conn); + return agent; mem_fail: + /* So passkey_agent_free doesn't try to call Relese */ + agent->exited = 1; passkey_agent_free(agent); return NULL; } @@ -166,6 +181,8 @@ static void agent_exited(const char *name, struct hci_dbus_data *adapter) if (strcmp(agent->name, name)) continue; + agent->exited = 1; + adapter->passkey_agents = slist_remove(adapter->passkey_agents, agent); passkey_agent_free(agent); } @@ -199,12 +216,13 @@ static DBusHandlerResult register_agent(DBusConnection *conn, if (slist_find(adapter->passkey_agents, &ref, (cmp_func_t)agent_cmp)) return error_passkey_agent_already_exists(conn, msg); - agent = passkey_agent_new(ref.name, path, addr); + agent = passkey_agent_new(adapter, conn, ref.name, path, addr); if (!agent) return DBUS_HANDLER_RESULT_NEED_MEMORY; reply = dbus_message_new_method_return(msg); if (!reply) { + agent->exited = 1; passkey_agent_free(agent); return DBUS_HANDLER_RESULT_NEED_MEMORY; } @@ -252,6 +270,8 @@ static DBusHandlerResult unregister_agent(DBusConnection *conn, agent = match->data; + agent->exited = 1; + adapter->passkey_agents = slist_remove(adapter->passkey_agents, agent); passkey_agent_free(agent); @@ -282,7 +302,7 @@ static DBusHandlerResult register_default_agent(DBusConnection *conn, DBUS_TYPE_INVALID)) return error_invalid_arguments(conn, msg); - default_agent = passkey_agent_new(dbus_message_get_sender(msg), path, NULL); + default_agent = passkey_agent_new(NULL, conn, dbus_message_get_sender(msg), path, NULL); if (!default_agent) goto need_memory; @@ -354,6 +374,7 @@ static struct service_data sec_services[] = { static void passkey_agent_reply(DBusPendingCall *call, void *user_data) { struct pending_agent_request *req = user_data; + struct passkey_agent *agent = req->agent; pin_code_reply_cp pr; DBusMessage *message; DBusError err; @@ -407,13 +428,17 @@ done: if (message) dbus_message_unref(message); - req->agent->pending_requests = slist_remove(req->agent->pending_requests, req); - + agent->pending_requests = slist_remove(agent->pending_requests, req); dbus_pending_call_cancel(req->call); dbus_pending_call_unref(req->call); - dbus_connection_unref(req->conn); free(req->path); free(req); + + if (agent != default_agent) { + agent->pdata->passkey_agents = slist_remove(agent->pdata->passkey_agents, + agent); + passkey_agent_free(agent); + } } static int call_passkey_agent(DBusConnection *conn, @@ -451,7 +476,6 @@ static int call_passkey_agent(DBusConnection *conn, bacpy(&req->sba, sba); bacpy(&req->bda, dba); req->agent = agent; - req->conn = dbus_connection_ref(conn); req->path = strdup(path); if (!req->path) goto failed; @@ -480,7 +504,6 @@ failed: dbus_message_unref(message); if (req) { - dbus_connection_unref(req->conn); free(req->path); free(req); } @@ -522,6 +545,8 @@ int handle_passkey_request(DBusConnection *conn, int dev, const char *path, for (l = adapter->passkey_agents; l != NULL; l = l->next) { struct passkey_agent *a = l->data; + if (a != default_agent && slist_length(a->pending_requests) >= 1) + continue; if (!strcmp(a->addr, addr)) { agent = a; break; @@ -532,12 +557,12 @@ done: return call_passkey_agent(conn, agent, dev, path, sba, dba); } -static void send_cancel_request(struct passkey_agent *agent, struct pending_agent_request *req) +static void send_cancel_request(struct pending_agent_request *req) { DBusMessage *message; char address[18], *ptr = address; - message = dbus_message_new_method_call(agent->name, agent->path, + message = dbus_message_new_method_call(req->agent->name, req->agent->path, "org.bluez.PasskeyAgent", "Cancel"); if (message == NULL) { error("Couldn't allocate D-Bus message"); @@ -553,18 +578,69 @@ static void send_cancel_request(struct passkey_agent *agent, struct pending_agen dbus_message_set_no_reply(message, TRUE); - send_reply_and_unref(req->conn, message); + send_reply_and_unref(req->agent->conn, message); debug("PasskeyAgent.Request(%s, %s) was canceled", req->path, address); dbus_pending_call_cancel(req->call); dbus_pending_call_unref(req->call); - dbus_connection_unref(req->conn); free(req->path); free(req); } -void cancel_passkey_agent_requests(struct slist *agents, const char *path, bdaddr_t *addr) +static void release_agent(struct passkey_agent *agent) +{ + DBusMessage *message; + + debug("Releasing agent %s, %s", agent->name, agent->path); + + message = dbus_message_new_method_call(agent->name, agent->path, + "org.bluez.PasskeyAgent", "Cancel"); + if (message == NULL) { + error("Couldn't allocate D-Bus message"); + return; + } + + dbus_message_set_no_reply(message, TRUE); + + send_reply_and_unref(agent->conn, message); +} + +void release_default_agent(void) +{ + if (!default_agent) + return; + + name_listener_remove(default_agent->conn, default_agent->name, + (name_cb_t)default_agent_exited, NULL); + + passkey_agent_free(default_agent); + default_agent = NULL; +} + +void release_passkey_agents(struct hci_dbus_data *pdata, bdaddr_t *bda) +{ + struct slist *l, *next; + + for (l = pdata->passkey_agents; l != NULL; l = next) { + struct passkey_agent *agent = l->data; + next = l->next; + + if (bda && agent->addr) { + bdaddr_t tmp; + str2ba(agent->addr, &tmp); + if (bacmp(&tmp, bda)) + continue; + } + + name_listener_remove(agent->conn, agent->name, (name_cb_t)agent_exited, pdata); + passkey_agent_free(agent); + pdata->passkey_agents = slist_remove(pdata->passkey_agents, agent); + } +} + +void cancel_passkey_agent_requests(struct slist *agents, const char *path, + bdaddr_t *addr) { struct slist *l, *next; @@ -573,20 +649,21 @@ void cancel_passkey_agent_requests(struct slist *agents, const char *path, bdadd struct pending_agent_request *req = l->data; next = l->next; if (!strcmp(path, req->path) && (!addr || !bacmp(addr, &req->bda))) { - send_cancel_request(default_agent, req); + send_cancel_request(req); default_agent->pending_requests = slist_remove(default_agent->pending_requests, req); } } /* and then the adapter specific agents */ - for (;agents != NULL; agents = agents->next) { + for (; agents != NULL; agents = agents->next) { struct passkey_agent *agent = agents->data; + for (l = agent->pending_requests; l != NULL; l = next) { struct pending_agent_request *req = l->data; next = l->next; if (!strcmp(path, req->path) && (!addr || !bacmp(addr, &req->bda))) { - send_cancel_request(agent, req); + send_cancel_request(req); agent->pending_requests = slist_remove(agent->pending_requests, req); } } diff --git a/hcid/dbus.c b/hcid/dbus.c index ec79a932..269d255d 100644 --- a/hcid/dbus.c +++ b/hcid/dbus.c @@ -396,6 +396,8 @@ static int unregister_dbus_path(const char *path) cancel_passkey_agent_requests(pdata->passkey_agents, path, NULL); + release_passkey_agents(pdata, NULL); + if (pdata->requestor_name) { name_listener_remove(connection, pdata->requestor_name, (name_cb_t)create_bond_req_exit, pdata); @@ -614,7 +616,7 @@ failed: int hcid_dbus_stop_device(uint16_t id) { char path[MAX_PATH_LENGTH]; - struct hci_dbus_data* pdata; + struct hci_dbus_data *pdata; const char *scan_mode = MODE_OFF; DBusMessage *message; @@ -642,6 +644,8 @@ int hcid_dbus_stop_device(uint16_t id) cancel_passkey_agent_requests(pdata->passkey_agents, path, NULL); + release_passkey_agents(pdata, NULL); + if (pdata->requestor_name) { name_listener_remove(connection, pdata->requestor_name, (name_cb_t)create_bond_req_exit, pdata); @@ -774,6 +778,8 @@ void hcid_dbus_bonding_process_complete(bdaddr_t *local, bdaddr_t *peer, const u } } + release_passkey_agents(pdata, peer); + if (!pdata->bonding || bacmp(&pdata->bonding->bdaddr, peer)) goto failed; /* skip: no bonding req pending */ @@ -1412,6 +1418,7 @@ void hcid_dbus_disconn_complete(bdaddr_t *local, uint8_t status, uint16_t handle } cancel_passkey_agent_requests(pdata->passkey_agents, path, &dev->bdaddr); + release_passkey_agents(pdata, &dev->bdaddr); /* Sent the remote device disconnected signal */ message = dev_signal_factory(pdata->dev_id, "RemoteDeviceDisconnected", @@ -1637,6 +1644,8 @@ void hcid_dbus_exit(void) if (!dbus_connection_get_is_connected(connection)) return; + release_default_agent(); + /* Unregister all paths in Adapter path hierarchy */ if (!dbus_connection_list_registered(connection, BASE_PATH, &children)) goto done; @@ -1983,6 +1992,7 @@ void create_bond_req_exit(const char *name, struct hci_dbus_data *pdata) debug("CreateConnection requestor at %s exited before bonding was completed", name); cancel_passkey_agent_requests(pdata->passkey_agents, path, &pdata->bonding->bdaddr); + release_passkey_agents(pdata, &pdata->bonding->bdaddr); if (pdata->bonding->disconnect) { struct slist *l; diff --git a/hcid/dbus.h b/hcid/dbus.h index 1399bf92..4aae9164 100644 --- a/hcid/dbus.h +++ b/hcid/dbus.h @@ -95,23 +95,6 @@ struct active_conn_info { uint16_t handle; }; -struct passkey_agent { - char *addr; - char *name; - char *path; - struct slist *pending_requests; -}; - -struct pending_agent_request { - struct passkey_agent *agent; - int dev; - bdaddr_t sba; - bdaddr_t bda; - char *path; - DBusConnection *conn; - DBusPendingCall *call; -}; - struct hci_dbus_data { uint16_t dev_id; int up; @@ -129,6 +112,25 @@ struct hci_dbus_data { struct slist *pending_bondings; }; +struct passkey_agent { + struct hci_dbus_data *pdata; + DBusConnection *conn; + char *addr; + char *name; + char *path; + struct slist *pending_requests; + int exited; +}; + +struct pending_agent_request { + struct passkey_agent *agent; + int dev; + bdaddr_t sba; + bdaddr_t bda; + char *path; + DBusPendingCall *call; +}; + typedef int register_function_t(DBusConnection *conn, uint16_t id); typedef int unregister_function_t(DBusConnection *conn, uint16_t id); @@ -194,6 +196,8 @@ service_handler_func_t find_service_handler(struct service_data *services, DBusM void create_bond_req_exit(const char *name, struct hci_dbus_data *pdata); int handle_passkey_request(DBusConnection *conn, int dev, const char *path, bdaddr_t *sba, bdaddr_t *dba); +void release_default_agent(void); +void release_passkey_agents(struct hci_dbus_data *pdata, bdaddr_t *bda); void cancel_passkey_agent_requests(struct slist *agents, const char *path, bdaddr_t *dba); static inline DBusHandlerResult send_reply_and_unref(DBusConnection *conn, DBusMessage *reply) diff --git a/hcid/main.c b/hcid/main.c index 3aea1a89..5aadca01 100644 --- a/hcid/main.c +++ b/hcid/main.c @@ -730,8 +730,6 @@ int main(int argc, char *argv[]) /* Start event processor */ g_main_run(event_loop); - g_main_unref(event_loop); - if (sdp) stop_sdp_server(); @@ -739,6 +737,8 @@ int main(int argc, char *argv[]) hcid_dbus_exit(); + g_main_unref(event_loop); + info("Exit"); stop_logging(); -- cgit