diff options
| -rw-r--r-- | hcid/dbus-sdp.c | 163 | 
1 files changed, 149 insertions, 14 deletions
| diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c index da0f13b2..3e68c7e0 100644 --- a/hcid/dbus-sdp.c +++ b/hcid/dbus-sdp.c @@ -58,6 +58,8 @@  #include "dbus-sdp.h"  #include "sdp-xml.h" +#define SESSION_TIMEOUT 2000 +  #define MAX_IDENTIFIER_LEN	29	/* "XX:XX:XX:XX:XX:XX/0xYYYYYYYY\0" */  #define DEFAULT_XML_BUF_SIZE	1024 @@ -73,12 +75,15 @@ struct transaction_context {  	DBusConnection *conn;  	DBusMessage *rq;  	sdp_session_t *session; +	GIOChannel *io; +	guint io_id;  	/* Used for internal async get remote service record implementation */  	get_record_data_t *call;  };  typedef int connect_cb_t(struct transaction_context *t); +  struct pending_connect {  	DBusConnection *conn;  	DBusMessage *rq; @@ -98,6 +103,123 @@ typedef struct {  	char            *info_name;  } sdp_service_t; +struct cached_session { +	sdp_session_t *session; +	guint timeout_id; +	guint io_id; +}; + +static GSList *cached_sessions = NULL; + +static gboolean session_timeout(gpointer user_data) +{ +	struct cached_session *s = user_data; + +	debug("sdp session timed out. closing"); + +	cached_sessions = g_slist_remove(cached_sessions, s); + +	g_source_remove(s->io_id); +	sdp_close(s->session); +	g_free(s); + +	return FALSE; +} + +gboolean idle_callback(GIOChannel *io, GIOCondition cond, gpointer user_data) +{ +	struct cached_session *s = user_data; + +	if (cond & G_IO_NVAL) +		return FALSE; + +	if (cond & (G_IO_ERR | G_IO_HUP)) +		debug("idle_callback: session got disconnected"); + +	if (cond & G_IO_IN) +		debug("got unexpected input on idle SDP socket"); + +	cached_sessions = g_slist_remove(cached_sessions, s); + +	g_source_remove(s->timeout_id); +	sdp_close(s->session); +	g_free(s); + +	return FALSE; +} + +static void cache_sdp_session(sdp_session_t *sess, GIOChannel *io) +{ +	struct cached_session *s; + +	s = g_new0(struct cached_session, 1); + +	s->session = sess; +	s->timeout_id = g_timeout_add(SESSION_TIMEOUT, session_timeout, s); +	s->io_id = g_io_add_watch(io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, +					idle_callback, s); + +	cached_sessions = g_slist_append(cached_sessions, s); + +	debug("sdp session added to cache"); +} + +static int get_bdaddrs(int sock, bdaddr_t *sba, bdaddr_t *dba) +{ +	struct sockaddr_l2 a; +	socklen_t len; + +	len = sizeof(a); +	if (getsockname(sock, (struct sockaddr *) &a, &len) < 0) { +		error("getsockname: %s (%d)", strerror(errno), errno); +		return -1; +	} +	 +	bacpy(sba, &a.l2_bdaddr); + +	len = sizeof(a); +	if (getpeername(sock, (struct sockaddr *) &a, &len) < 0) { +		error("getpeername: %s (%d)", strerror(errno), errno); +		return -1; +	} + +	bacpy(dba, &a.l2_bdaddr); + +	return 0; +} + +static sdp_session_t *get_sdp_session(bdaddr_t *src, bdaddr_t *dst) +{ +	GSList *l; + +	for (l = cached_sessions; l != NULL; l = l->next) { +		struct cached_session *s = l->data; +		sdp_session_t *session = s->session; +		int sock = sdp_get_socket(session); +		bdaddr_t sba, dba; + +		if (get_bdaddrs(sock, &sba, &dba) < 0) +			continue; + +		if (bacmp(&sba, src) || bacmp(&dba, dst)) +			continue; + +		debug("found matching session, removing from list"); + +		cached_sessions = g_slist_remove(cached_sessions, s); + +		g_source_remove(s->timeout_id); +		g_source_remove(s->io_id); +		g_free(s); + +		return session; +	} + +	debug("no matching session found. creating a new one"); + +	return sdp_connect(src, dst, SDP_NON_BLOCKING); +} +  static void append_and_grow_string(void *data, const char *str)  {  	sdp_buf_t *buff = (sdp_buf_t *) data; @@ -250,7 +372,7 @@ static int sdp_store_record(const char *src, const char *dst, uint32_t handle, u  	return err;  } -static void transaction_context_free(void *udata) +static void transaction_context_free(void *udata, gboolean cache)  {  	struct transaction_context *ctxt = udata; @@ -263,9 +385,20 @@ static void transaction_context_free(void *udata)  	if (ctxt->rq)  		dbus_message_unref(ctxt->rq); -	if (ctxt->session) +	if (ctxt->session && !ctxt->io)  		sdp_close(ctxt->session); +	if (ctxt->session && ctxt->io) { +		g_source_remove(ctxt->io_id); + +		if (cache) +			cache_sdp_session(ctxt->session, ctxt->io); +		else +			sdp_close(ctxt->session); + +		g_io_channel_unref(ctxt->io); +	} +  	g_free(ctxt);  } @@ -328,7 +461,7 @@ failed:  		} else  			error_failed(ctxt->conn, ctxt->rq, err); -		transaction_context_free(ctxt); +		transaction_context_free(ctxt, FALSE);  	}  	return TRUE; @@ -402,7 +535,7 @@ done:  	send_message_and_unref(ctxt->conn, reply);  failed: -	transaction_context_free(ctxt); +	transaction_context_free(ctxt, TRUE);  }  static void remote_svc_rec_completed_xml_cb(uint8_t type, uint16_t err, @@ -478,7 +611,7 @@ done:  	send_message_and_unref(ctxt->conn, reply);  failed: -	transaction_context_free(ctxt); +	transaction_context_free(ctxt, TRUE);  }  static void remote_svc_handles_completed_cb(uint8_t type, uint16_t err, @@ -552,7 +685,7 @@ done:  	send_message_and_unref(ctxt->conn, reply);  failed: -	transaction_context_free(ctxt); +	transaction_context_free(ctxt, TRUE);  }  static gboolean sdp_client_connect_cb(GIOChannel *chan, @@ -593,8 +726,11 @@ static gboolean sdp_client_connect_cb(GIOChannel *chan,  	}  	/* set the callback responsible for update the transaction data */ -	g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, -			search_process_cb, ctxt); +	ctxt->io_id = g_io_add_watch(chan, +				G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, +				search_process_cb, ctxt); +	ctxt->io = g_io_channel_ref(chan); +  	goto done;  failed: @@ -607,12 +743,10 @@ failed:  		get_record_data_free(c->call);  	if (ctxt) -		transaction_context_free(ctxt); +		transaction_context_free(ctxt, FALSE);  	else  		sdp_close(c->session); -	g_io_channel_unref(chan); -  done:  	pending_connects = g_slist_remove(pending_connects, c);  	pending_connect_free(c); @@ -640,7 +774,7 @@ static struct pending_connect *connect_request(DBusConnection *conn,  	hci_devba(dev_id, &srcba);  	str2ba(dst, &dstba); -	c->session = sdp_connect(&srcba, &dstba, SDP_NON_BLOCKING); +	c->session = get_sdp_session(&srcba, &dstba);  	if (!c->session) {  		if (err)  			*err = errno; @@ -651,6 +785,7 @@ static struct pending_connect *connect_request(DBusConnection *conn,  	chan = g_io_channel_unix_new(sdp_get_socket(c->session));  	g_io_add_watch(chan, G_IO_OUT, sdp_client_connect_cb, c); +	g_io_channel_unref(chan);  	pending_connects = g_slist_append(pending_connects, c);  	return c; @@ -859,7 +994,7 @@ failed:  	get_record_data_free(ctxt->call); -	transaction_context_free(ctxt); +	transaction_context_free(ctxt, TRUE);  }  static int get_rec_with_handle_conn_cb(struct transaction_context *ctxt) @@ -976,7 +1111,7 @@ failed:  	get_record_data_free(d); -	transaction_context_free(ctxt); +	transaction_context_free(ctxt, TRUE);  }  static int get_rec_with_uuid_conn_cb(struct transaction_context *ctxt) | 
