diff options
| -rw-r--r-- | common/glib-ectomy.c | 18 | ||||
| -rw-r--r-- | common/list.c | 10 | ||||
| -rw-r--r-- | common/list.h | 2 | ||||
| -rw-r--r-- | hcid/dbus-security.c | 127 | ||||
| -rw-r--r-- | hcid/dbus.c | 12 | ||||
| -rw-r--r-- | hcid/dbus.h | 38 | ||||
| -rw-r--r-- | 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(); | 
