diff options
| author | Johan Hedberg <johan.hedberg@nokia.com> | 2006-03-09 19:10:42 +0000 | 
|---|---|---|
| committer | Johan Hedberg <johan.hedberg@nokia.com> | 2006-03-09 19:10:42 +0000 | 
| commit | 45df23c4593250e58e355bf3cde15abb2d6d4c3c (patch) | |
| tree | 38ee3f63cb37ab5687dbb7e3abfe1c4814dabe40 | |
| parent | 7297697c324a6113371fd60b3668320e5a4817a7 (diff) | |
D-Bus name lifetime tracking support
| -rw-r--r-- | hcid/Makefile.am | 3 | ||||
| -rw-r--r-- | hcid/dbus-common.c | 251 | ||||
| -rw-r--r-- | hcid/dbus.h | 11 | 
3 files changed, 264 insertions, 1 deletions
| diff --git a/hcid/Makefile.am b/hcid/Makefile.am index 1cc46c9f..aa3b2cc1 100644 --- a/hcid/Makefile.am +++ b/hcid/Makefile.am @@ -36,7 +36,8 @@ hcid_SOURCES = main.c security.c device.c storage.c hcid.h \  hcid_LDADD = $(dbus_hcid_libs) @BLUEZ_LIBS@ \  		$(top_builddir)/common/libglib-ectomy.a \ -		$(top_builddir)/common/libtextfile.a +		$(top_builddir)/common/libtextfile.a \ +		$(top_builddir)/common/liblist.a  AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ $(dbus_hcid_cflags) diff --git a/hcid/dbus-common.c b/hcid/dbus-common.c index f3bf47c4..35807330 100644 --- a/hcid/dbus-common.c +++ b/hcid/dbus-common.c @@ -26,4 +26,255 @@  #endif  #include <stdio.h> +#include <stdlib.h> +#include <string.h>  #include <errno.h> +#include <syslog.h> + +#include <dbus/dbus.h> + +#include "list.h" +#include "dbus.h" + +static int name_listener_initialized = 0; + +struct name_callback { +	name_cb_t func; +	void *user_data; +}; + +struct name_data { +	char *name; +	struct slist *callbacks; +}; + +static struct slist *name_listeners = NULL; + +static struct name_data *name_data_find(const char *name) +{ +	struct slist *current; + +	for (current = name_listeners; current != NULL; current = current->next) { +		struct name_data *data = current->data; +		if (strcmp(name, data->name) == 0) +			return data; +	} + +	return NULL; +} + +static struct name_callback *name_callback_find(struct slist *callbacks, +						name_cb_t func, void *user_data) +{ +	struct slist *current; + +	for (current = callbacks; current != NULL; current = current->next) { +		struct name_callback *cb = current->data; +		if (cb->func == func && cb->user_data == user_data) +			return cb; +	} + +	return NULL; +} + +static int name_data_add(const char *name, name_cb_t func, void *user_data) +{ +	int first = 0; +	struct name_data *data; +	struct name_callback *cb; + +	cb = malloc(sizeof(struct name_callback)); +	if (!cb) +		goto out; + +	cb->func = func; +	cb->user_data = user_data; + +	data = name_data_find(name); +	if (!data) { +		data = malloc(sizeof(struct name_data)); +		if (!data) { +			free(cb); +			goto out; +		} + +		data->name = strdup(name); +		if (!data->name) { +			free(cb); +			goto out; +		} + +		data->callbacks = NULL; +		name_listeners = slist_append(name_listeners, data); +		first = 1; +	} + +	data->callbacks = slist_append(data->callbacks, cb); + +out: +	return first; +} + +static void name_data_free(struct name_data *data) +{ +	free(data->name); +	free(data); +} + +static void name_data_remove(const char *name, name_cb_t func, void *user_data) +{ +	struct name_data *data; +	struct name_callback *cb = NULL; + +	data = name_data_find(name); +	if (!data) +		return; + +	cb = name_callback_find(data->callbacks, func, user_data); +	if (!cb) +		return; + +	data->callbacks = slist_remove(data->callbacks, cb); +	free(cb); + +	if (!data->callbacks) { +		name_listeners = slist_remove(name_listeners, data); +		name_data_free(data); +	} +} + +static DBusHandlerResult name_exit_filter(DBusConnection *connection, +						DBusMessage *message, +						void *user_data) +{ +	struct slist *l; +	struct name_data *data; +	char *name, *old, *new; + +	if (!dbus_message_is_signal(message, +				DBUS_INTERFACE_DBUS, +				"NameOwnerChanged")) +		goto out; + +	if (!dbus_message_get_args(message, NULL, +				DBUS_TYPE_STRING, &name, +				DBUS_TYPE_STRING, &old, +				DBUS_TYPE_STRING, &new, +				DBUS_TYPE_INVALID)) { +		syslog(LOG_ERR, "Invalid arguments for NameOwnerChanged signal"); +		goto out; +	} + +	/* We are not interested of service creations */ +	if (*new != '\0') +		goto out; + +	data = name_data_find(name); +	if (!data) { +		syslog(LOG_ERR, "Got NameOwnerChanged signal for %s which has no listeners", +				name); +		goto out; +	} + +	for (l = data->callbacks; l != NULL; l = l->next) { +		struct name_callback *cb = l->data; +		cb->func(name, cb->user_data); +	} + +	name_listeners = slist_remove(name_listeners, data); +	name_data_free(data); + +out: +	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + + +int name_listener_add(DBusConnection *connection, const char *name, +			name_cb_t func, void *user_data) +{ +	DBusError err; +	char match_string[128]; +	int first; + +	syslog(LOG_DEBUG, "name_listener_add(%s)", name); + +	if (!name_listener_initialized) { +		if (!dbus_connection_add_filter(connection, name_exit_filter, NULL, NULL)) { +			syslog(LOG_ERR, "dbus_connection_add_filter() failed"); +			return -1; +		} +		name_listener_initialized = 1; +	} + +	first = name_data_add(name, func, user_data); +	/* The filter is already added if this is not the first callback +	 * registration for the name */ +	if (!first) +		return 0; + +	snprintf(match_string, sizeof(match_string), +			"interface=%s,member=NameOwnerChanged,arg0=%s", +			DBUS_INTERFACE_DBUS, name); + +	dbus_error_init(&err); +	dbus_bus_add_match(connection, match_string, &err); + +	if (dbus_error_is_set(&err)) { +		syslog(LOG_ERR, "Adding owner match rule for %s failed: %s", +				name, err.message); +		dbus_error_free(&err); +		name_data_remove(name, func, user_data); +		return -1; +	} + +	return 0; +} + +int name_listener_remove(DBusConnection *connection, const char *name, +				name_cb_t func, void *user_data) +{ +	struct name_data *data; +	struct name_callback *cb; +	DBusError err; +	char match_string[128]; + +	syslog(LOG_DEBUG, "name_listener_remove(%s)", name); + +	data = name_data_find(name); +	if (!data) { +		syslog(LOG_ERR, "remove_name_listener: no listener for %s", +				name); +		return -1; +	} + +	cb = name_callback_find(data->callbacks, func, user_data); +	if (!cb) { +		syslog(LOG_ERR, "No matching callback found for %s", name); +		return -1; +	} + +	data->callbacks = slist_remove(data->callbacks, cb); + +	/* Don't remove the filter if other callbacks exist */ +	if (data->callbacks) +		return 0; + +	snprintf(match_string, sizeof(match_string), +			"interface=%s,member=NameOwnerChanged,arg0=%s", +			DBUS_INTERFACE_DBUS, name); + +	dbus_error_init(&err); +	dbus_bus_remove_match(connection, match_string, &err); + +	if (dbus_error_is_set(&err)) { +		syslog(LOG_ERR, "Removing owner match rule for %s failed: %s", +				name, err.message); +		dbus_error_free(&err); +		return -1; +	} + +	name_data_remove(name, func, user_data); + +	return 0; +} + diff --git a/hcid/dbus.h b/hcid/dbus.h index 7c29e0b0..5c96589e 100644 --- a/hcid/dbus.h +++ b/hcid/dbus.h @@ -24,6 +24,8 @@  #ifndef __H_BLUEZ_DBUS_H__  #define __H_BLUEZ_DBUS_H__ +#include <stdint.h> +  #define __END_SIG__ DBUS_TYPE_INVALID_AS_STRING  #define BASE_PATH		"/org/bluez" @@ -98,6 +100,15 @@ DBusMessage *error_not_connected(DBusMessage *msg);  DBusMessage *error_bonding_in_progress(DBusMessage *msg); + +typedef void (*name_cb_t)(const char *name, void *user_data); + +int name_listener_add(DBusConnection *connection, const char *name, +				name_cb_t func, void *user_data); +int name_listener_remove(DBusConnection *connection, const char *name, +				name_cb_t func, void *user_data); + +  /*========================================================================       BlueZ D-Bus Manager service definitions "/org/bluez/Manager"   *========================================================================*/ | 
