Home | History | Annotate | Download | only in gdbus
      1 /*
      2  *
      3  *  D-Bus helper library
      4  *
      5  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel (at) holtmann.org>
      6  *
      7  *
      8  *  This program is free software; you can redistribute it and/or modify
      9  *  it under the terms of the GNU General Public License as published by
     10  *  the Free Software Foundation; either version 2 of the License, or
     11  *  (at your option) any later version.
     12  *
     13  *  This program is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *  GNU General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU General Public License
     19  *  along with this program; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #ifdef HAVE_CONFIG_H
     25 #include <config.h>
     26 #endif
     27 
     28 #include <stdio.h>
     29 #include <string.h>
     30 
     31 #include <glib.h>
     32 #include <dbus/dbus.h>
     33 
     34 #include "gdbus.h"
     35 
     36 #define info(fmt...)
     37 #define error(fmt...)
     38 #define debug(fmt...)
     39 
     40 static DBusHandlerResult message_filter(DBusConnection *connection,
     41 					DBusMessage *message, void *user_data);
     42 
     43 static guint listener_id = 0;
     44 static GSList *listeners = NULL;
     45 
     46 struct filter_callback {
     47 	GDBusWatchFunction conn_func;
     48 	GDBusWatchFunction disc_func;
     49 	GDBusSignalFunction signal_func;
     50 	GDBusDestroyFunction destroy_func;
     51 	void *user_data;
     52 	guint id;
     53 };
     54 
     55 struct filter_data {
     56 	DBusConnection *connection;
     57 	DBusHandleMessageFunction handle_func;
     58 	char *sender;
     59 	char *path;
     60 	char *interface;
     61 	char *member;
     62 	char *argument;
     63 	GSList *callbacks;
     64 	GSList *processed;
     65 	gboolean lock;
     66 	gboolean registered;
     67 };
     68 
     69 static struct filter_data *filter_data_find(DBusConnection *connection,
     70 							const char *sender,
     71 							const char *path,
     72 							const char *interface,
     73 							const char *member,
     74 							const char *argument)
     75 {
     76 	GSList *current;
     77 
     78 	for (current = listeners;
     79 			current != NULL; current = current->next) {
     80 		struct filter_data *data = current->data;
     81 
     82 		if (connection != data->connection)
     83 			continue;
     84 
     85 		if (sender && data->sender &&
     86 				g_str_equal(sender, data->sender) == FALSE)
     87 			continue;
     88 
     89 		if (path && data->path &&
     90 				g_str_equal(path, data->path) == FALSE)
     91 			continue;
     92 
     93 		if (interface && data->interface &&
     94 				g_str_equal(interface, data->interface) == FALSE)
     95 			continue;
     96 
     97 		if (member && data->member &&
     98 				g_str_equal(member, data->member) == FALSE)
     99 			continue;
    100 
    101 		if (argument && data->argument &&
    102 				g_str_equal(argument, data->argument) == FALSE)
    103 			continue;
    104 
    105 		return data;
    106 	}
    107 
    108 	return NULL;
    109 }
    110 
    111 static void format_rule(struct filter_data *data, char *rule, size_t size)
    112 {
    113 	int offset;
    114 
    115 	offset = snprintf(rule, size, "type='signal'");
    116 
    117 	if (data->sender)
    118 		offset += snprintf(rule + offset, size - offset,
    119 				",sender='%s'", data->sender);
    120 	if (data->path)
    121 		offset += snprintf(rule + offset, size - offset,
    122 				",path='%s'", data->path);
    123 	if (data->interface)
    124 		offset += snprintf(rule + offset, size - offset,
    125 				",interface='%s'", data->interface);
    126 	if (data->member)
    127 		offset += snprintf(rule + offset, size - offset,
    128 				",member='%s'", data->member);
    129 	if (data->argument)
    130 		snprintf(rule + offset, size - offset,
    131 				",arg0='%s'", data->argument);
    132 }
    133 
    134 static gboolean add_match(struct filter_data *data,
    135 				DBusHandleMessageFunction filter)
    136 {
    137 	DBusError err;
    138 	char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
    139 
    140 	format_rule(data, rule, sizeof(rule));
    141 	dbus_error_init(&err);
    142 
    143 	dbus_bus_add_match(data->connection, rule, &err);
    144 	if (dbus_error_is_set(&err)) {
    145 		error("Adding match rule \"%s\" failed: %s", rule,
    146 				err.message);
    147 		dbus_error_free(&err);
    148 		return FALSE;
    149 	}
    150 
    151 	data->handle_func = filter;
    152 	data->registered = TRUE;
    153 
    154 	return TRUE;
    155 }
    156 
    157 static gboolean remove_match(struct filter_data *data)
    158 {
    159 	DBusError err;
    160 	char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
    161 
    162 	format_rule(data, rule, sizeof(rule));
    163 
    164 	dbus_error_init(&err);
    165 
    166 	dbus_bus_remove_match(data->connection, rule, &err);
    167 	if (dbus_error_is_set(&err)) {
    168 		error("Removing owner match rule for %s failed: %s",
    169 				rule, err.message);
    170 		dbus_error_free(&err);
    171 		return FALSE;
    172 	}
    173 
    174 	return TRUE;
    175 }
    176 
    177 static struct filter_data *filter_data_get(DBusConnection *connection,
    178 					DBusHandleMessageFunction filter,
    179 					const char *sender,
    180 					const char *path,
    181 					const char *interface,
    182 					const char *member,
    183 					const char *argument)
    184 {
    185 	struct filter_data *data;
    186 
    187 	if (!filter_data_find(connection, NULL, NULL, NULL, NULL, NULL)) {
    188 		if (!dbus_connection_add_filter(connection,
    189 					message_filter, NULL, NULL)) {
    190 			error("dbus_connection_add_filter() failed");
    191 			return NULL;
    192 		}
    193 	}
    194 
    195 	data = filter_data_find(connection, sender, path, interface, member,
    196 					argument);
    197 	if (data)
    198 		return data;
    199 
    200 	data = g_new0(struct filter_data, 1);
    201 
    202 	data->connection = dbus_connection_ref(connection);
    203 	data->sender = g_strdup(sender);
    204 	data->path = g_strdup(path);
    205 	data->interface = g_strdup(interface);
    206 	data->member = g_strdup(member);
    207 	data->argument = g_strdup(argument);
    208 
    209 	if (!add_match(data, filter)) {
    210 		g_free(data);
    211 		return NULL;
    212 	}
    213 
    214 	listeners = g_slist_append(listeners, data);
    215 
    216 	return data;
    217 }
    218 
    219 static struct filter_callback *filter_data_find_callback(
    220 						struct filter_data *data,
    221 						guint id)
    222 {
    223 	GSList *l;
    224 
    225 	for (l = data->callbacks; l; l = l->next) {
    226 		struct filter_callback *cb = l->data;
    227 		if (cb->id == id)
    228 			return cb;
    229 	}
    230 	for (l = data->processed; l; l = l->next) {
    231 		struct filter_callback *cb = l->data;
    232 		if (cb->id == id)
    233 			return cb;
    234 	}
    235 
    236 	return NULL;
    237 }
    238 
    239 static void filter_data_free(struct filter_data *data)
    240 {
    241 	GSList *l;
    242 
    243 	for (l = data->callbacks; l != NULL; l = l->next)
    244 		g_free(l->data);
    245 
    246 	g_slist_free(data->callbacks);
    247 	g_free(data->sender);
    248 	g_free(data->path);
    249 	g_free(data->interface);
    250 	g_free(data->member);
    251 	g_free(data->argument);
    252 	dbus_connection_unref(data->connection);
    253 	g_free(data);
    254 }
    255 
    256 static void filter_data_call_and_free(struct filter_data *data)
    257 {
    258 	GSList *l;
    259 
    260 	for (l = data->callbacks; l != NULL; l = l->next) {
    261 		struct filter_callback *cb = l->data;
    262 		if (cb->disc_func)
    263 			cb->disc_func(data->connection, cb->user_data);
    264 		if (cb->destroy_func)
    265 			cb->destroy_func(cb->user_data);
    266 		g_free(cb);
    267 	}
    268 
    269 	filter_data_free(data);
    270 }
    271 
    272 static struct filter_callback *filter_data_add_callback(
    273 						struct filter_data *data,
    274 						GDBusWatchFunction connect,
    275 						GDBusWatchFunction disconnect,
    276 						GDBusSignalFunction signal,
    277 						GDBusDestroyFunction destroy,
    278 						void *user_data)
    279 {
    280 	struct filter_callback *cb = NULL;
    281 
    282 	cb = g_new(struct filter_callback, 1);
    283 
    284 	cb->conn_func = connect;
    285 	cb->disc_func = disconnect;
    286 	cb->signal_func = signal;
    287 	cb->destroy_func = destroy;
    288 	cb->user_data = user_data;
    289 	cb->id = ++listener_id;
    290 
    291 	if (data->lock)
    292 		data->processed = g_slist_append(data->processed, cb);
    293 	else
    294 		data->callbacks = g_slist_append(data->callbacks, cb);
    295 
    296 	return cb;
    297 }
    298 
    299 static gboolean filter_data_remove_callback(struct filter_data *data,
    300 						struct filter_callback *cb)
    301 {
    302 	DBusConnection *connection;
    303 
    304 	data->callbacks = g_slist_remove(data->callbacks, cb);
    305 	data->processed = g_slist_remove(data->processed, cb);
    306 
    307 	if (cb->destroy_func)
    308 		cb->destroy_func(cb->user_data);
    309 
    310 	g_free(cb);
    311 
    312 	/* Don't remove the filter if other callbacks exist or data is lock
    313 	 * processing callbacks */
    314 	if (data->callbacks || data->lock)
    315 		return TRUE;
    316 
    317 	if (data->registered && !remove_match(data))
    318 		return FALSE;
    319 
    320 	connection = dbus_connection_ref(data->connection);
    321 	listeners = g_slist_remove(listeners, data);
    322 	filter_data_free(data);
    323 
    324 	/* Remove filter if there are no listeners left for the connection */
    325 	data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL);
    326 	if (!data)
    327 		dbus_connection_remove_filter(connection, message_filter,
    328 						NULL);
    329 
    330 	dbus_connection_unref(connection);
    331 
    332 	return TRUE;
    333 }
    334 
    335 static DBusHandlerResult signal_filter(DBusConnection *connection,
    336 					DBusMessage *message, void *user_data)
    337 {
    338 	struct filter_data *data = user_data;
    339 	struct filter_callback *cb;
    340 
    341 	while (data->callbacks) {
    342 		cb = data->callbacks->data;
    343 
    344 		if (cb->signal_func && !cb->signal_func(connection, message,
    345 							cb->user_data)) {
    346 			filter_data_remove_callback(data, cb);
    347 			continue;
    348 		}
    349 
    350 		/* Check if the watch was removed/freed by the callback
    351 		 * function */
    352 		if (!g_slist_find(data->callbacks, cb))
    353 			continue;
    354 
    355 		data->callbacks = g_slist_remove(data->callbacks, cb);
    356 		data->processed = g_slist_append(data->processed, cb);
    357 	}
    358 
    359 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    360 }
    361 
    362 static DBusHandlerResult service_filter(DBusConnection *connection,
    363 					DBusMessage *message, void *user_data)
    364 {
    365 	struct filter_data *data = user_data;
    366 	struct filter_callback *cb;
    367 	char *name, *old, *new;
    368 
    369 	if (!dbus_message_get_args(message, NULL,
    370 				DBUS_TYPE_STRING, &name,
    371 				DBUS_TYPE_STRING, &old,
    372 				DBUS_TYPE_STRING, &new,
    373 				DBUS_TYPE_INVALID)) {
    374 		error("Invalid arguments for NameOwnerChanged signal");
    375 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    376 	}
    377 
    378 	while (data->callbacks) {
    379 		cb = data->callbacks->data;
    380 
    381 		if (*new == '\0') {
    382 			if (cb->disc_func)
    383 				cb->disc_func(connection, cb->user_data);
    384 		} else {
    385 			if (cb->conn_func)
    386 				cb->conn_func(connection, cb->user_data);
    387 		}
    388 
    389 		/* Check if the watch was removed/freed by the callback
    390 		 * function */
    391 		if (!g_slist_find(data->callbacks, cb))
    392 			continue;
    393 
    394 		data->callbacks = g_slist_remove(data->callbacks, cb);
    395 
    396 		if (!cb->conn_func || !cb->disc_func) {
    397 			g_free(cb);
    398 			continue;
    399 		}
    400 
    401 		data->processed = g_slist_append(data->processed, cb);
    402 	}
    403 
    404 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    405 }
    406 
    407 
    408 static DBusHandlerResult message_filter(DBusConnection *connection,
    409 					DBusMessage *message, void *user_data)
    410 {
    411 	struct filter_data *data;
    412 	const char *sender, *path, *iface, *member, *arg = NULL;
    413 
    414 	/* Only filter signals */
    415 	if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
    416 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    417 
    418 	sender = dbus_message_get_sender(message);
    419 	path = dbus_message_get_path(message);
    420 	iface = dbus_message_get_interface(message);
    421 	member = dbus_message_get_member(message);
    422 	dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
    423 
    424 	data = filter_data_find(connection, sender, path, iface, member, arg);
    425 	if (!data) {
    426 		error("Got %s.%s signal which has no listeners", iface, member);
    427 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    428 	}
    429 
    430 	if (data->handle_func) {
    431 		data->lock = TRUE;
    432 
    433 		data->handle_func(connection, message, data);
    434 
    435 		data->callbacks = data->processed;
    436 		data->processed = NULL;
    437 		data->lock = FALSE;
    438 	}
    439 
    440 	if (data->callbacks)
    441 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    442 
    443 	remove_match(data);
    444 
    445 	listeners = g_slist_remove(listeners, data);
    446 	filter_data_free(data);
    447 
    448 	/* Remove filter if there no listener left for the connection */
    449 	data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL);
    450 	if (!data)
    451 		dbus_connection_remove_filter(connection, message_filter,
    452 						NULL);
    453 
    454 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    455 }
    456 
    457 struct service_data {
    458 	DBusConnection *conn;
    459 	GDBusWatchFunction conn_func;
    460 	void *user_data;
    461 };
    462 
    463 static void service_reply(DBusPendingCall *call, void *user_data)
    464 {
    465 	struct service_data *data = user_data;
    466 	DBusMessage *reply;
    467 	DBusError error;
    468 	dbus_bool_t has_owner;
    469 
    470 	reply = dbus_pending_call_steal_reply(call);
    471 	if (reply == NULL)
    472 		return;
    473 
    474 	dbus_error_init(&error);
    475 
    476 	if (dbus_message_get_args(reply, &error,
    477 					DBUS_TYPE_BOOLEAN, &has_owner,
    478 						DBUS_TYPE_INVALID) == FALSE) {
    479 		if (dbus_error_is_set(&error) == TRUE) {
    480 			error("%s", error.message);
    481 			dbus_error_free(&error);
    482 		} else {
    483 			error("Wrong arguments for NameHasOwner reply");
    484 		}
    485 		goto done;
    486 	}
    487 
    488 	if (has_owner && data->conn_func)
    489 		data->conn_func(data->conn, data->user_data);
    490 
    491 done:
    492 	dbus_message_unref(reply);
    493 }
    494 
    495 static void check_service(DBusConnection *connection, const char *name,
    496 				GDBusWatchFunction connect, void *user_data)
    497 {
    498 	DBusMessage *message;
    499 	DBusPendingCall *call;
    500 	struct service_data *data;
    501 
    502 	data = g_try_malloc0(sizeof(*data));
    503 	if (data == NULL) {
    504 		error("Can't allocate data structure");
    505 		return;
    506 	}
    507 
    508 	data->conn = connection;
    509 	data->conn_func = connect;
    510 	data->user_data = user_data;
    511 
    512 	message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
    513 			DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "NameHasOwner");
    514 	if (message == NULL) {
    515 		error("Can't allocate new message");
    516 		g_free(data);
    517 		return;
    518 	}
    519 
    520 	dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
    521 							DBUS_TYPE_INVALID);
    522 
    523 	if (dbus_connection_send_with_reply(connection, message,
    524 							&call, -1) == FALSE) {
    525 		error("Failed to execute method call");
    526 		g_free(data);
    527 		goto done;
    528 	}
    529 
    530 	if (call == NULL) {
    531 		error("D-Bus connection not available");
    532 		g_free(data);
    533 		goto done;
    534 	}
    535 
    536 	dbus_pending_call_set_notify(call, service_reply, data, NULL);
    537 
    538 	dbus_pending_call_unref(call);
    539 
    540 done:
    541 	dbus_message_unref(message);
    542 }
    543 
    544 guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
    545 				GDBusWatchFunction connect,
    546 				GDBusWatchFunction disconnect,
    547 				void *user_data, GDBusDestroyFunction destroy)
    548 {
    549 	struct filter_data *data;
    550 	struct filter_callback *cb;
    551 
    552 	if (!name)
    553 		return 0;
    554 
    555 	data = filter_data_get(connection, service_filter, NULL, NULL,
    556 				DBUS_INTERFACE_DBUS, "NameOwnerChanged",
    557 				name);
    558 	if (!data)
    559 		return 0;
    560 
    561 	cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL,
    562 					user_data);
    563 	if (!cb)
    564 		return 0;
    565 
    566 	if (connect)
    567 		check_service(connection, name, connect, user_data);
    568 
    569 	return cb->id;
    570 }
    571 
    572 guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
    573 				GDBusWatchFunction func,
    574 				void *user_data, GDBusDestroyFunction destroy)
    575 {
    576 	return g_dbus_add_service_watch(connection, name, NULL, func,
    577 							user_data, destroy);
    578 }
    579 
    580 guint g_dbus_add_signal_watch(DBusConnection *connection,
    581 				const char *sender, const char *path,
    582 				const char *interface, const char *member,
    583 				GDBusSignalFunction function, void *user_data,
    584 				GDBusDestroyFunction destroy)
    585 {
    586 	struct filter_data *data;
    587 	struct filter_callback *cb;
    588 
    589 	data = filter_data_get(connection, signal_filter, sender, path,
    590 				interface, member, NULL);
    591 	if (!data)
    592 		return 0;
    593 
    594 	cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
    595 					user_data);
    596 	if (!cb)
    597 		return 0;
    598 
    599 	return cb->id;
    600 }
    601 
    602 gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
    603 {
    604 	struct filter_data *data;
    605 	struct filter_callback *cb;
    606 	GSList *ldata;
    607 
    608 	if (id == 0)
    609 		return FALSE;
    610 
    611 	for (ldata = listeners; ldata; ldata = ldata->next) {
    612 		data = ldata->data;
    613 
    614 		cb = filter_data_find_callback(data, id);
    615 		if (cb) {
    616 			filter_data_remove_callback(data, cb);
    617 			return TRUE;
    618 		}
    619 	}
    620 
    621 	return FALSE;
    622 }
    623 
    624 void g_dbus_remove_all_watches(DBusConnection *connection)
    625 {
    626 	struct filter_data *data;
    627 
    628 	while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL))) {
    629 		listeners = g_slist_remove(listeners, data);
    630 		filter_data_call_and_free(data);
    631 	}
    632 
    633 	dbus_connection_remove_filter(connection, message_filter, NULL);
    634 }
    635