Home | History | Annotate | Download | only in gdbus
      1 /*
      2  *
      3  *  D-Bus helper library
      4  *
      5  *  Copyright (C) 2004-2009  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 	GDBusMethodTable *methods;
     49 	GDBusSignalTable *signals;
     50 	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 	GDBusMethodTable *method;
    118 	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 DBusHandlerResult introspect(DBusConnection *connection,
    187 				DBusMessage *message, struct generic_data *data)
    188 {
    189 	DBusMessage *reply;
    190 
    191 	if (!dbus_message_has_signature(message, DBUS_TYPE_INVALID_AS_STRING)) {
    192 		error("Unexpected signature to introspect call");
    193 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    194 	}
    195 
    196 	if (!data->introspect)
    197 		generate_introspection_xml(connection, data,
    198 						dbus_message_get_path(message));
    199 
    200 	reply = dbus_message_new_method_return(message);
    201 	if (!reply)
    202 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
    203 
    204 	dbus_message_append_args(reply, DBUS_TYPE_STRING, &data->introspect,
    205 					DBUS_TYPE_INVALID);
    206 
    207 	dbus_connection_send(connection, reply, NULL);
    208 
    209 	dbus_message_unref(reply);
    210 
    211 	return DBUS_HANDLER_RESULT_HANDLED;
    212 }
    213 
    214 static void generic_unregister(DBusConnection *connection, void *user_data)
    215 {
    216 	struct generic_data *data = user_data;
    217 
    218 	g_free(data->introspect);
    219 	g_free(data);
    220 }
    221 
    222 static struct interface_data *find_interface(GSList *interfaces,
    223 						const char *name)
    224 {
    225 	GSList *list;
    226 
    227 	if (!name)
    228 		return NULL;
    229 
    230 	for (list = interfaces; list; list = list->next) {
    231 		struct interface_data *iface = list->data;
    232 		if (!strcmp(name, iface->name))
    233 			return iface;
    234 	}
    235 
    236 	return NULL;
    237 }
    238 
    239 static DBusHandlerResult generic_message(DBusConnection *connection,
    240 					DBusMessage *message, void *user_data)
    241 {
    242 	struct generic_data *data = user_data;
    243 	struct interface_data *iface;
    244 	GDBusMethodTable *method;
    245 	const char *interface;
    246 
    247 	if (dbus_message_is_method_call(message,
    248 					DBUS_INTERFACE_INTROSPECTABLE,
    249 								"Introspect"))
    250 		return introspect(connection, message, data);
    251 
    252 	interface = dbus_message_get_interface(message);
    253 
    254 	iface = find_interface(data->interfaces, interface);
    255 	if (!iface)
    256 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    257 
    258 	for (method = iface->methods; method &&
    259 			method->name && method->function; method++) {
    260 		DBusMessage *reply;
    261 
    262 		if (dbus_message_is_method_call(message, iface->name,
    263 							method->name) == FALSE)
    264 			continue;
    265 
    266 		if (dbus_message_has_signature(message,
    267 						method->signature) == FALSE)
    268 			continue;
    269 
    270 		reply = method->function(connection, message, iface->user_data);
    271 
    272 		if (method->flags & G_DBUS_METHOD_FLAG_NOREPLY) {
    273 			if (reply != NULL)
    274 				dbus_message_unref(reply);
    275 			return DBUS_HANDLER_RESULT_HANDLED;
    276 		}
    277 
    278 		if (method->flags & G_DBUS_METHOD_FLAG_ASYNC) {
    279 			if (reply == NULL)
    280 				return DBUS_HANDLER_RESULT_HANDLED;
    281 		}
    282 
    283 		if (reply == NULL)
    284 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
    285 
    286 		dbus_connection_send(connection, reply, NULL);
    287 		dbus_message_unref(reply);
    288 
    289 		return DBUS_HANDLER_RESULT_HANDLED;
    290 	}
    291 
    292 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    293 }
    294 
    295 static DBusObjectPathVTable generic_table = {
    296 	.unregister_function	= generic_unregister,
    297 	.message_function	= generic_message,
    298 };
    299 
    300 static void invalidate_parent_data(DBusConnection *conn, const char *child_path)
    301 {
    302 	struct generic_data *data = NULL;
    303 	char *parent_path, *slash;
    304 
    305 	parent_path = g_strdup(child_path);
    306 	slash = strrchr(parent_path, '/');
    307 	if (!slash)
    308 		goto done;
    309 
    310 	if (slash == parent_path && parent_path[1] != '\0')
    311 		parent_path[1] = '\0';
    312 	else
    313 		*slash = '\0';
    314 
    315 	if (!strlen(parent_path))
    316 		goto done;
    317 
    318 	if (!dbus_connection_get_object_path_data(conn, parent_path,
    319 							(void *) &data))
    320 		goto done;
    321 
    322 	if (!data)
    323 		goto done;
    324 
    325 	g_free(data->introspect);
    326 	data->introspect = NULL;
    327 
    328 done:
    329 	g_free(parent_path);
    330 }
    331 
    332 static struct generic_data *object_path_ref(DBusConnection *connection,
    333 							const char *path)
    334 {
    335 	struct generic_data *data;
    336 
    337 	if (dbus_connection_get_object_path_data(connection, path,
    338 						(void *) &data) == TRUE) {
    339 		if (data != NULL) {
    340 			data->refcount++;
    341 			return data;
    342 		}
    343 	}
    344 
    345 	data = g_new0(struct generic_data, 1);
    346 
    347 	data->introspect = g_strdup(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "<node></node>");
    348 
    349 	data->refcount = 1;
    350 
    351 	if (!dbus_connection_register_object_path(connection, path,
    352 						&generic_table, data)) {
    353 		g_free(data->introspect);
    354 		g_free(data);
    355 		return NULL;
    356 	}
    357 
    358 	invalidate_parent_data(connection, path);
    359 
    360 	return data;
    361 }
    362 
    363 static void object_path_unref(DBusConnection *connection, const char *path)
    364 {
    365 	struct generic_data *data = NULL;
    366 
    367 	if (dbus_connection_get_object_path_data(connection, path,
    368 						(void *) &data) == FALSE)
    369 		return;
    370 
    371 	if (data == NULL)
    372 		return;
    373 
    374 	data->refcount--;
    375 
    376 	if (data->refcount > 0)
    377 		return;
    378 
    379 	invalidate_parent_data(connection, path);
    380 
    381 	dbus_connection_unregister_object_path(connection, path);
    382 }
    383 
    384 static gboolean check_signal(DBusConnection *conn, const char *path,
    385 				const char *interface, const char *name,
    386 				const char **args)
    387 {
    388 	struct generic_data *data = NULL;
    389 	struct interface_data *iface;
    390 	GDBusSignalTable *signal;
    391 
    392 	*args = NULL;
    393 	if (!dbus_connection_get_object_path_data(conn, path,
    394 					(void *) &data) || !data) {
    395 		error("dbus_connection_emit_signal: path %s isn't registered",
    396 				path);
    397 		return FALSE;
    398 	}
    399 
    400 	iface = find_interface(data->interfaces, interface);
    401 	if (!iface) {
    402 		error("dbus_connection_emit_signal: %s does not implement %s",
    403 				path, interface);
    404 		return FALSE;
    405 	}
    406 
    407 	for (signal = iface->signals; signal && signal->name; signal++) {
    408 		if (!strcmp(signal->name, name)) {
    409 			*args = signal->signature;
    410 			break;
    411 		}
    412 	}
    413 
    414 	if (!*args) {
    415 		error("No signal named %s on interface %s", name, interface);
    416 		return FALSE;
    417 	}
    418 
    419 	return TRUE;
    420 }
    421 
    422 static dbus_bool_t emit_signal_valist(DBusConnection *conn,
    423 						const char *path,
    424 						const char *interface,
    425 						const char *name,
    426 						int first,
    427 						va_list var_args)
    428 {
    429 	DBusMessage *signal;
    430 	dbus_bool_t ret;
    431 	const char *signature, *args;
    432 
    433 	if (!check_signal(conn, path, interface, name, &args))
    434 		return FALSE;
    435 
    436 	signal = dbus_message_new_signal(path, interface, name);
    437 	if (!signal) {
    438 		error("Unable to allocate new %s.%s signal", interface,  name);
    439 		return FALSE;
    440 	}
    441 
    442 	ret = dbus_message_append_args_valist(signal, first, var_args);
    443 	if (!ret)
    444 		goto fail;
    445 
    446 	signature = dbus_message_get_signature(signal);
    447 	if (strcmp(args, signature) != 0) {
    448 		error("%s.%s: expected signature'%s' but got '%s'",
    449 				interface, name, args, signature);
    450 		ret = FALSE;
    451 		goto fail;
    452 	}
    453 
    454 	ret = dbus_connection_send(conn, signal, NULL);
    455 
    456 fail:
    457 	dbus_message_unref(signal);
    458 
    459 	return ret;
    460 }
    461 
    462 gboolean g_dbus_register_interface(DBusConnection *connection,
    463 					const char *path, const char *name,
    464 					GDBusMethodTable *methods,
    465 					GDBusSignalTable *signals,
    466 					GDBusPropertyTable *properties,
    467 					void *user_data,
    468 					GDBusDestroyFunction destroy)
    469 {
    470 	struct generic_data *data;
    471 	struct interface_data *iface;
    472 
    473 	data = object_path_ref(connection, path);
    474 	if (data == NULL)
    475 		return FALSE;
    476 
    477 	if (find_interface(data->interfaces, name))
    478 		return FALSE;
    479 
    480 	iface = g_new0(struct interface_data, 1);
    481 
    482 	iface->name = g_strdup(name);
    483 	iface->methods = methods;
    484 	iface->signals = signals;
    485 	iface->properties = properties;
    486 	iface->user_data = user_data;
    487 	iface->destroy = destroy;
    488 
    489 	data->interfaces = g_slist_append(data->interfaces, iface);
    490 
    491 	g_free(data->introspect);
    492 	data->introspect = NULL;
    493 
    494 	return TRUE;
    495 }
    496 
    497 gboolean g_dbus_unregister_interface(DBusConnection *connection,
    498 					const char *path, const char *name)
    499 {
    500 	struct generic_data *data = NULL;
    501 	struct interface_data *iface;
    502 
    503 	if (!path)
    504 		return FALSE;
    505 
    506 	if (dbus_connection_get_object_path_data(connection, path,
    507 						(void *) &data) == FALSE)
    508 		return FALSE;
    509 
    510 	if (data == NULL)
    511 		return FALSE;
    512 
    513 	iface = find_interface(data->interfaces, name);
    514 	if (!iface)
    515 		return FALSE;
    516 
    517 	data->interfaces = g_slist_remove(data->interfaces, iface);
    518 
    519 	if (iface->destroy)
    520 		iface->destroy(iface->user_data);
    521 
    522 	g_free(iface->name);
    523 	g_free(iface);
    524 
    525 	g_free(data->introspect);
    526 	data->introspect = NULL;
    527 
    528 	object_path_unref(connection, path);
    529 
    530 	return TRUE;
    531 }
    532 
    533 DBusMessage *g_dbus_create_error_valist(DBusMessage *message, const char *name,
    534 					const char *format, va_list args)
    535 {
    536 	char str[1024];
    537 
    538 	vsnprintf(str, sizeof(str), format, args);
    539 
    540 	return dbus_message_new_error(message, name, str);
    541 }
    542 
    543 DBusMessage *g_dbus_create_error(DBusMessage *message, const char *name,
    544 						const char *format, ...)
    545 {
    546 	va_list args;
    547 	DBusMessage *reply;
    548 
    549 	va_start(args, format);
    550 
    551 	reply = g_dbus_create_error_valist(message, name, format, args);
    552 
    553 	va_end(args);
    554 
    555 	return reply;
    556 }
    557 
    558 DBusMessage *g_dbus_create_reply_valist(DBusMessage *message,
    559 						int type, va_list args)
    560 {
    561 	DBusMessage *reply;
    562 
    563 	reply = dbus_message_new_method_return(message);
    564 	if (reply == NULL)
    565 		return NULL;
    566 
    567 	if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
    568 		dbus_message_unref(reply);
    569 		return NULL;
    570 	}
    571 
    572 	return reply;
    573 }
    574 
    575 DBusMessage *g_dbus_create_reply(DBusMessage *message, int type, ...)
    576 {
    577 	va_list args;
    578 	DBusMessage *reply;
    579 
    580 	va_start(args, type);
    581 
    582 	reply = g_dbus_create_reply_valist(message, type, args);
    583 
    584 	va_end(args);
    585 
    586 	return reply;
    587 }
    588 
    589 gboolean g_dbus_send_message(DBusConnection *connection, DBusMessage *message)
    590 {
    591 	dbus_bool_t result;
    592 
    593 	if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
    594 		dbus_message_set_no_reply(message, TRUE);
    595 
    596 	result = dbus_connection_send(connection, message, NULL);
    597 
    598 	dbus_message_unref(message);
    599 
    600 	return result;
    601 }
    602 
    603 gboolean g_dbus_send_reply_valist(DBusConnection *connection,
    604 				DBusMessage *message, int type, va_list args)
    605 {
    606 	DBusMessage *reply;
    607 
    608 	reply = dbus_message_new_method_return(message);
    609 	if (reply == NULL)
    610 		return FALSE;
    611 
    612 	if (dbus_message_append_args_valist(reply, type, args) == FALSE) {
    613 		dbus_message_unref(reply);
    614 		return FALSE;
    615 	}
    616 
    617 	return g_dbus_send_message(connection, reply);
    618 }
    619 
    620 gboolean g_dbus_send_reply(DBusConnection *connection,
    621 				DBusMessage *message, int type, ...)
    622 {
    623 	va_list args;
    624 	gboolean result;
    625 
    626 	va_start(args, type);
    627 
    628 	result = g_dbus_send_reply_valist(connection, message, type, args);
    629 
    630 	va_end(args);
    631 
    632 	return result;
    633 }
    634 
    635 gboolean g_dbus_emit_signal(DBusConnection *connection,
    636 				const char *path, const char *interface,
    637 				const char *name, int type, ...)
    638 {
    639 	va_list args;
    640 	gboolean result;
    641 
    642 	va_start(args, type);
    643 
    644 	result = emit_signal_valist(connection, path, interface,
    645 							name, type, args);
    646 
    647 	va_end(args);
    648 
    649 	return result;
    650 }
    651 
    652 gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
    653 				const char *path, const char *interface,
    654 				const char *name, int type, va_list args)
    655 {
    656 	return emit_signal_valist(connection, path, interface,
    657 							name, type, args);
    658 }
    659