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 struct generic_data {
     41 	unsigned int refcount;
     42 	GSList *interfaces;
     43 	char *introspect;
     44 };
     45 
     46 struct interface_data {
     47 	char *name;
     48 	const GDBusMethodTable *methods;
     49 	const GDBusSignalTable *signals;
     50 	const GDBusPropertyTable *properties;
     51 	void *user_data;
     52 	GDBusDestroyFunction destroy;
     53 };
     54 
     55 static void print_arguments(GString *gstr, const char *sig,
     56 						const char *direction)
     57 {
     58 	int i;
     59 
     60 	for (i = 0; sig[i]; i++) {
     61 		char type[32];
     62 		int struct_level, dict_level;
     63 		unsigned int len;
     64 		gboolean complete;
     65 
     66 		complete = FALSE;
     67 		struct_level = dict_level = 0;
     68 		memset(type, 0, sizeof(type));
     69 
     70 		/* Gather enough data to have a single complete type */
     71 		for (len = 0; len < (sizeof(type) - 1) && sig[i]; len++, i++) {
     72 			switch (sig[i]){
     73 			case '(':
     74 				struct_level++;
     75 				break;
     76 			case ')':
     77 				struct_level--;
     78 				if (struct_level <= 0 && dict_level <= 0)
     79 					complete = TRUE;
     80 				break;
     81 			case '{':
     82 				dict_level++;
     83 				break;
     84 			case '}':
     85 				dict_level--;
     86 				if (struct_level <= 0 && dict_level <= 0)
     87 					complete = TRUE;
     88 				break;
     89 			case 'a':
     90 				break;
     91 			default:
     92 				if (struct_level <= 0 && dict_level <= 0)
     93 					complete = TRUE;
     94 				break;
     95 			}
     96 
     97 			type[len] = sig[i];
     98 
     99 			if (complete)
    100 				break;
    101 		}
    102 
    103 
    104 		if (direction)
    105 			g_string_append_printf(gstr,
    106 					"\t\t\t<arg type=\"%s\" direction=\"%s\"/>\n",
    107 					type, direction);
    108 		else
    109 			g_string_append_printf(gstr,
    110 					"\t\t\t<arg type=\"%s\"/>\n",
    111 					type);
    112 	}
    113 }
    114 
    115 static void generate_interface_xml(GString *gstr, struct interface_data *iface)
    116 {
    117 	const GDBusMethodTable *method;
    118 	const GDBusSignalTable *signal;
    119 
    120 	for (method = iface->methods; method && method->name; method++) {
    121 		if (!strlen(method->signature) && !strlen(method->reply))
    122 			g_string_append_printf(gstr, "\t\t<method name=\"%s\"/>\n",
    123 								method->name);
    124 		else {
    125 			g_string_append_printf(gstr, "\t\t<method name=\"%s\">\n",
    126 								method->name);
    127 			print_arguments(gstr, method->signature, "in");
    128 			print_arguments(gstr, method->reply, "out");
    129 			g_string_append_printf(gstr, "\t\t</method>\n");
    130 		}
    131 	}
    132 
    133 	for (signal = iface->signals; signal && signal->name; signal++) {
    134 		if (!strlen(signal->signature))
    135 			g_string_append_printf(gstr, "\t\t<signal name=\"%s\"/>\n",
    136 								signal->name);
    137 		else {
    138 			g_string_append_printf(gstr, "\t\t<signal name=\"%s\">\n",
    139 								signal->name);
    140 			print_arguments(gstr, signal->signature, NULL);
    141 			g_string_append_printf(gstr, "\t\t</signal>\n");
    142 		}
    143 	}
    144 }
    145 
    146 static void generate_introspection_xml(DBusConnection *conn,
    147 				struct generic_data *data, const char *path)
    148 {
    149 	GSList *list;
    150 	GString *gstr;
    151 	char **children;
    152 	int i;
    153 
    154 	g_free(data->introspect);
    155 
    156 	gstr = g_string_new(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
    157 
    158 	g_string_append_printf(gstr, "<node name=\"%s\">\n", path);
    159 
    160 	for (list = data->interfaces; list; list = list->next) {
    161 		struct interface_data *iface = list->data;
    162 
    163 		g_string_append_printf(gstr, "\t<interface name=\"%s\">\n",
    164 								iface->name);
    165 
    166 		generate_interface_xml(gstr, iface);
    167 
    168 		g_string_append_printf(gstr, "\t</interface>\n");
    169 	}
    170 
    171 	if (!dbus_connection_list_registered(conn, path, &children))
    172 		goto done;
    173 
    174 	for (i = 0; children[i]; i++)
    175 		g_string_append_printf(gstr, "\t<node name=\"%s\"/>\n",
    176 								children[i]);
    177 
    178 	dbus_free_string_array(children);
    179 
    180 done:
    181 	g_string_append_printf(gstr, "</node>\n");
    182 
    183 	data->introspect = g_string_free(gstr, FALSE);
    184 }
    185 
    186 static DBusMessage *introspect(DBusConnection *connection,
    187 				DBusMessage *message, void *user_data)
    188 {
    189 	struct generic_data *data = user_data;
    190 	DBusMessage *reply;
    191 
    192 	if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
    193 		error("Unexpected signature to introspect call");
    194 		return NULL;
    195 	}
    196 
    197 	if (!data->introspect)
    198 		generate_introspection_xml(connection, data,
    199 						dbus_message_get_path(message));
    200 
    201 	reply = dbus_message_new_method_return(message);
    202 	if (!reply)
    203 		return NULL;
    204 
    205 	dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
    206 					DBUS_TYPE_INVALID);
    207 
    208 	return reply;
    209 }
    210 
    211 static void generic_unregister(DBusConnection *connection, void *user_data)
    212 {
    213 	struct generic_data *data = user_data;
    214 
    215 	g_free(data->introspect);
    216 	g_free(data);
    217 }
    218 
    219 static struct interface_data *find_interface(GSList *interfaces,
    220 						const char *name)
    221 {
    222 	GSList *list;
    223 
    224 	if (!name)
    225 		return NULL;
    226 
    227 	for (list = interfaces; list; list = list->next) {
    228 		struct interface_data *iface = list->data;
    229 		if (!strcmp(name, iface->name))
    230 			return iface;
    231 	}
    232 
    233 	return NULL;
    234 }
    235 
    236 static DBusHandlerResult generic_message(DBusConnection *connection,
    237 					DBusMessage *message, void *user_data)
    238 {
    239 	struct generic_data *data = user_data;
    240 	struct interface_data *iface;
    241 	const GDBusMethodTable *method;
    242 	const char *interface;
    243 
    244 	interface = dbus_message_get_interface(message);
    245 
    246 	iface = find_interface(data->interfaces, interface);
    247 	if (!iface)
    248 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    249 
    250 	for (method = iface->methods; method &&
    251 			method->name && method->function; method++) {
    252 		DBusMessage *reply;
    253 
    254 		if (dbus_message_is_method_call(message, iface->name,
    255 							method->name) == FALSE)
    256 			continue;
    257 
    258 		if (dbus_message_has_signature(message,
    259 						method->signature) == FALSE)
    260 			continue;
    261 
    262 		reply = method->function(connection, message, iface->user_data);
    263 
    264 		if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
    265 			if (reply != NULL)
    266 				dbus_message_unref(reply);
    267 			return DBUS_HANDLER_RESULT_HANDLED;
    268 		}
    269 
    270 		if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
    271 			if (reply == NULL)
    272 				return DBUS_HANDLER_RESULT_HANDLED;
    273 		}
    274 
    275 		if (reply == NULL)
    276 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
    277 
    278 		dbus_connection_send(connection, reply, NULL);
    279 		dbus_message_unref(reply);
    280 
    281 		return DBUS_HANDLER_RESULT_HANDLED;
    282 	}
    283 
    284 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    285 }
    286 
    287 static DBusObjectPathVTable generic_table = {
    288 	.unregister_function	= generic_unregister,
    289 	.message_function	= generic_message,
    290 };
    291 
    292 static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
    293 {
    294 	struct generic_data *data = NULL;
    295 	char *parent_path, *slash;
    296 
    297 	parent_path = g_strdup(child_path);
    298 	slash = strrchr(parent_path, '/');
    299 	if (!slash)
    300 		goto done;
    301 
    302 	if (slash == parent_path && parent_path[1] != '\0')
    303 		parent_path[1] = '\0';
    304 	else
    305 		*slash = '\0';
    306 
    307 	if (!strlen(parent_path))
    308 		goto done;
    309 
    310 	if (!dbus_connection_get_object_path_data(conn, parent_path,
    311 							(void *) &data)) {
    312 		invalidate_parent_data(conn, parent_path);
    313 		goto done;
    314 	}
    315 
    316 	if (!data)
    317 		goto done;
    318 
    319 	g_free(data->introspect);
    320 	data->introspect = NULL;
    321 
    322 done:
    323 	g_free(parent_path);
    324 }
    325 
    326 static GDBusMethodTable introspect_methods[] = {
    327 	{ "Introspect",	"",	"s", introspect	},
    328 	{ }
    329 };
    330 
    331 static void add_interface(struct generic_data *data, const char *name,
    332 				const GDBusMethodTable *methods,
    333 				const GDBusSignalTable *signals,
    334 				const GDBusPropertyTable *properties,
    335 				void *user_data,
    336 				GDBusDestroyFunction destroy)
    337 {
    338 	struct interface_data *iface;
    339 
    340 	iface = g_new0(struct interface_data, 1);
    341 	iface->name = g_strdup(name);
    342 	iface->methods = methods;
    343 	iface->signals = signals;
    344 	iface->properties = properties;
    345 	iface->user_data = user_data;
    346 	iface->destroy = destroy;
    347 
    348 	data->interfaces = g_slist_append(data->interfaces, iface);
    349 }
    350 
    351 static struct generic_data *object_path_ref(DBusConnection *connection,
    352 							const char *path)
    353 {
    354 	struct generic_data *data;
    355 
    356 	if (dbus_connection_get_object_path_data(connection, path,
    357 						(void *) &data) == TRUE) {
    358 		if (data != NULL) {
    359 			data->refcount++;
    360 			return data;
    361 		}
    362 	}
    363 
    364 	data = g_new0(struct generic_data, 1);
    365 
    366 	data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
    367 
    368 	data->refcount = 1;
    369 
    370 	if (!dbus_connection_register_object_path(connection, path,
    371 						&generic_table, data)) {
    372 		g_free(data->introspect);
    373 		g_free(data);
    374 		return NULL;
    375 	}
    376 
    377 	invalidate_parent_data(connection, path);
    378 
    379 	add_interface(data, DBUS_INTERFACE_INTROSPECTABLE,
    380 			introspect_methods, NULL, NULL, data, NULL);
    381 
    382 	return data;
    383 }
    384 
    385 static gboolean remove_interface(struct generic_data *data, const char *name)
    386 {
    387 	struct interface_data *iface;
    388 
    389 	iface = find_interface(data->interfaces, name);
    390 	if (!iface)
    391 		return FALSE;
    392 
    393 	data->interfaces = g_slist_remove(data->interfaces, iface);
    394 
    395 	if (iface->destroy)
    396 		iface->destroy(iface->user_data);
    397 
    398 	g_free(iface->name);
    399 	g_free(iface);
    400 
    401 	return TRUE;
    402 }
    403 
    404 static void object_path_unref(DBusConnection *connection, const char *path)
    405 {
    406 	struct generic_data *data = NULL;
    407 
    408 	if (dbus_connection_get_object_path_data(connection, path,
    409 						(void *) &data) == FALSE)
    410 		return;
    411 
    412 	if (data == NULL)
    413 		return;
    414 
    415 	data->refcount--;
    416 
    417 	if (data->refcount > 0)
    418 		return;
    419 
    420 	remove_interface(data, DBUS_INTERFACE_INTROSPECTABLE);
    421 
    422 	invalidate_parent_data(connection, path);
    423 
    424 	dbus_connection_unregister_object_path(connection, path);
    425 }
    426 
    427 static gboolean check_signal(DBusConnection *conn, const char *path,
    428 				const char *interface, const char *name,
    429 				const char **args)
    430 {
    431 	struct generic_data *data = NULL;
    432 	struct interface_data *iface;
    433 	const GDBusSignalTable *signal;
    434 
    435 	*args = NULL;
    436 	if (!dbus_connection_get_object_path_data(conn, path,
    437 					(void *) &data) || !data) {
    438 		error("dbus_connection_emit_signal: path %s isn't registered",
    439 				path);
    440 		return FALSE;
    441 	}
    442 
    443 	iface = find_interface(data->interfaces, interface);
    444 	if (!iface) {
    445 		error("dbus_connection_emit_signal: %s does not implement %s",
    446 				path, interface);
    447 		return FALSE;
    448 	}
    449 
    450 	for (signal = iface->signals; signal && signal->name; signal++) {
    451 		if (!strcmp(signal->name, name)) {
    452 			*args = signal->signature;
    453 			break;
    454 		}
    455 	}
    456 
    457 	if (!*args) {
    458 		error("No signal named %s on interface %s", name, interface);
    459 		return FALSE;
    460 	}
    461 
    462 	return TRUE;
    463 }
    464 
    465 static dbus_bool_t emit_signal_valist(DBusConnection *conn,
    466 						const char *path,
    467 						const char *interface,
    468 						const char *name,
    469 						int first,
    470 						va_list var_args)
    471 {
    472 	DBusMessage *signal;
    473 	dbus_bool_t ret;
    474 	const char *signature, *args;
    475 
    476 	if (!check_signal(conn, path, interface, name, &args))
    477 		return FALSE;
    478 
    479 	signal = dbus_message_new_signal(path, interface, name);
    480 	if (!signal) {
    481 		error("Unable to allocate new %s.%s signal", interface,  name);
    482 		return FALSE;
    483 	}
    484 
    485 	ret = dbus_message_append_args_valist(signal, first, var_args);
    486 	if (!ret)
    487 		goto fail;
    488 
    489 	signature = dbus_message_get_signature(signal);
    490 	if (strcmp(args, signature) != 0) {
    491 		error("%s.%s: expected signature'%s' but got '%s'",
    492 				interface, name, args, signature);
    493 		ret = FALSE;
    494 		goto fail;
    495 	}
    496 
    497 	ret = dbus_connection_send(conn, signal, NULL);
    498 
    499 fail:
    500 	dbus_message_unref(signal);
    501 
    502 	return ret;
    503 }
    504 
    505 gboolean g_dbus_register_interface(DBusConnection *connection,
    506 					const char *path, const char *name,
    507 					const GDBusMethodTable *methods,
    508 					const GDBusSignalTable *signals,
    509 					const GDBusPropertyTable *properties,
    510 					void *user_data,
    511 					GDBusDestroyFunction destroy)
    512 {
    513 	struct generic_data *data;
    514 
    515 	data = object_path_ref(connection, path);
    516 	if (data == NULL)
    517 		return FALSE;
    518 
    519 	if (find_interface(data->interfaces, name)) {
    520 		object_path_unref(connection, path);
    521 		return FALSE;
    522 	}
    523 
    524 	add_interface(data, name, methods, signals,
    525 			properties, user_data, destroy);
    526 
    527 	g_free(data->introspect);
    528 	data->introspect = NULL;
    529 
    530 	return TRUE;
    531 }
    532 
    533 gboolean g_dbus_unregister_interface(DBusConnection *connection,
    534 					const char *path, const char *name)
    535 {
    536 	struct generic_data *data = NULL;
    537 
    538 	if (!path)
    539 		return FALSE;
    540 
    541 	if (dbus_connection_get_object_path_data(connection, path,
    542 						(void *) &data) == FALSE)
    543 		return FALSE;
    544 
    545 	if (data == NULL)
    546 		return FALSE;
    547 
    548 	if (remove_interface(data, name) == FALSE)
    549 		return FALSE;
    550 
    551 	g_free(data->introspect);
    552 	data->introspect = NULL;
    553 
    554 	object_path_unref(connection, path);
    555 
    556 	return TRUE;
    557 }
    558 
    559 DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
    560 					const char *format, va_list args)
    561 {
    562 	char str[1024];
    563 
    564 	vsnprintf(str, sizeof(str), format, args);
    565 
    566 	return dbus_message_new_error(message, name, str);
    567 }
    568 
    569 DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
    570 						const char *format, ...)
    571 {
    572 	va_list args;
    573 	DBusMessage *reply;
    574 
    575 	va_start(args, format);
    576 
    577 	reply = g_dbus_create_error_valist(message, name, format, args);
    578 
    579 	va_end(args);
    580 
    581 	return reply;
    582 }
    583 
    584 DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
    585 						int type, va_list args)
    586 {
    587 	DBusMessage *reply;
    588 
    589 	reply = dbus_message_new_method_return(message);
    590 	if (reply == NULL)
    591 		return NULL;
    592 
    593 	if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
    594 		dbus_message_unref(reply);
    595 		return NULL;
    596 	}
    597 
    598 	return reply;
    599 }
    600 
    601 DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
    602 {
    603 	va_list args;
    604 	DBusMessage *reply;
    605 
    606 	va_start(args, type);
    607 
    608 	reply = g_dbus_create_reply_valist(message, type, args);
    609 
    610 	va_end(args);
    611 
    612 	return reply;
    613 }
    614 
    615 gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
    616 {
    617 	dbus_bool_t result;
    618 
    619 	if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
    620 		dbus_message_set_no_reply(message, TRUE);
    621 
    622 	result = dbus_connection_send(connection, message, NULL);
    623 
    624 	dbus_message_unref(message);
    625 
    626 	return result;
    627 }
    628 
    629 gboolean g_dbus_send_reply_valist(DBusConnection *connection,
    630 				DBusMessage *message, int type, va_list args)
    631 {
    632 	DBusMessage *reply;
    633 
    634 	reply = dbus_message_new_method_return(message);
    635 	if (reply == NULL)
    636 		return FALSE;
    637 
    638 	if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
    639 		dbus_message_unref(reply);
    640 		return FALSE;
    641 	}
    642 
    643 	return g_dbus_send_message(connection, reply);
    644 }
    645 
    646 gboolean g_dbus_send_reply(DBusConnection *connection,
    647 				DBusMessage *message, int type, ...)
    648 {
    649 	va_list args;
    650 	gboolean result;
    651 
    652 	va_start(args, type);
    653 
    654 	result = g_dbus_send_reply_valist(connection, message, type, args);
    655 
    656 	va_end(args);
    657 
    658 	return result;
    659 }
    660 
    661 gboolean g_dbus_emit_signal(DBusConnection *connection,
    662 				const char *path, const char *interface,
    663 				const char *name, int type, ...)
    664 {
    665 	va_list args;
    666 	gboolean result;
    667 
    668 	va_start(args, type);
    669 
    670 	result = emit_signal_valist(connection, path, interface,
    671 							name, type, args);
    672 
    673 	va_end(args);
    674 
    675 	return result;
    676 }
    677 
    678 gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
    679 				const char *path, const char *interface,
    680 				const char *name, int type, va_list args)
    681 {
    682 	return emit_signal_valist(connection, path, interface,
    683 							name, type, args);
    684 }
    685