2 * Unit, a D-Bus tester for argument marshalling
4 * Copyright © 2008 Movial Creative Technologies Inc
5 * Contact: Movial Creative Technologies Inc, <info@movial.com>
6 * Authors: Kalle Vahlman, <kalle.vahlman@movial.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later
13 * This library 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 Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
24 * The Unit interface has methods that take a certain argument types and
25 * send a reply and a subsequent signal with the received arguments.
27 * To build, execute this:
29 * gcc -o unit unit.c $(pkg-config --cflags --libs dbus-glib-1)
37 #include <dbus/dbus.h>
38 #include <dbus/dbus-glib-lowlevel.h>
40 #define OBJECT_PATH "/org/movial/Unit"
41 #define SERVICE_NAME "org.movial.Unit"
45 static gboolean u_transfer_array(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
46 static gboolean u_transfer_variant(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
47 static gboolean u_transfer_dict(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
48 static gboolean u_transfer_struct(DBusMessageIter *to_iter, DBusMessageIter *from_iter);
51 u_unregister(DBusConnection *connection, void *user_data)
53 g_debug("Object path unregistered");
54 g_main_loop_quit(loop);
58 u_transfer_arg(DBusMessageIter *to, DBusMessageIter *from)
61 int type = dbus_message_iter_get_arg_type(from);
63 if (!dbus_type_is_basic(type))
68 u_transfer_array(to, from);
70 case DBUS_TYPE_VARIANT:
71 u_transfer_variant(to, from);
73 case DBUS_TYPE_DICT_ENTRY:
74 u_transfer_dict(to, from);
76 case DBUS_TYPE_STRUCT:
77 u_transfer_struct(to, from);
80 g_warning("Non-basic type '%c' in variants not yet handled", type);
86 dbus_message_iter_get_basic(from, &value);
87 if (type == DBUS_TYPE_STRING)
89 g_debug("Transferring string arg %s", value);
91 if (type == DBUS_TYPE_INT32)
93 g_debug("Transferring int arg %i", value);
95 if (type == DBUS_TYPE_DOUBLE)
97 g_debug("Transferring double arg %f", value);
99 dbus_message_iter_append_basic(to, type, &value);
103 u_transfer_variant(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
106 DBusMessageIter from;
107 const char *sig = NULL;
109 dbus_message_iter_recurse(from_iter, &from);
110 sig = dbus_message_iter_get_signature(&from);
115 dbus_message_iter_open_container(to_iter, DBUS_TYPE_VARIANT, sig, &to);
118 u_transfer_arg(&to, &from);
119 } while (dbus_message_iter_next(&from));
121 dbus_message_iter_close_container(to_iter, &to);
127 u_transfer_array(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
130 DBusMessageIter from;
134 dbus_message_iter_recurse(from_iter, &from);
136 elem_type = dbus_message_iter_get_element_type(from_iter);
139 case DBUS_TYPE_ARRAY:
141 /* Fetch the element type for this too */
142 sig = g_strdup(dbus_message_iter_get_signature(from_iter)+1);
145 case DBUS_TYPE_DICT_ENTRY:
146 case DBUS_TYPE_STRUCT:
147 g_warning("Element type '%c' in arrays not supported", elem_type);
150 sig = g_strdup_printf("%c", dbus_message_iter_get_element_type(from_iter));
157 dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
160 u_transfer_arg(&to, &from);
161 } while (dbus_message_iter_next(&from));
163 dbus_message_iter_close_container(to_iter, &to);
169 u_transfer_dict(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
172 DBusMessageIter from;
175 dbus_message_iter_recurse(from_iter, &from);
176 sig = g_strdup(dbus_message_iter_get_signature(&from));
181 dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
183 DBusMessageIter subfrom;
184 DBusMessageIter dictiter;
185 dbus_message_iter_open_container(&to, DBUS_TYPE_DICT_ENTRY,
187 dbus_message_iter_recurse(&from, &subfrom);
189 u_transfer_arg(&dictiter, &subfrom);
190 if (dbus_message_iter_next(&subfrom)) {
191 u_transfer_arg(&dictiter, &subfrom);
193 dbus_message_iter_close_container(&to, &dictiter);
194 } while (dbus_message_iter_next(&from));
196 dbus_message_iter_close_container(to_iter, &to);
202 u_transfer_struct(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
205 DBusMessageIter from;
207 dbus_message_iter_recurse(from_iter, &from);
208 dbus_message_iter_open_container(to_iter, DBUS_TYPE_STRUCT, NULL, &to);
211 u_transfer_arg(&to, &from);
212 } while (dbus_message_iter_next(&from));
214 dbus_message_iter_close_container(to_iter, &to);
219 static DBusHandlerResult
220 u_message(DBusConnection *connection, DBusMessage *message, void *user_data)
223 const char *path = dbus_message_get_path(message);
224 const char *interface = dbus_message_get_interface(message);
225 const char *member = dbus_message_get_member(message);
226 const char *signature = dbus_message_get_signature(message);
228 g_debug("Message received, path %s, interface %s, member %s, signature %s",
229 path == NULL ? "not set" : path,
230 interface == NULL ? "not set" : interface,
231 member == NULL ? "not set" : member,
232 signature == NULL ? "not set" : signature);
235 if (!dbus_message_has_path(message, OBJECT_PATH))
236 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
238 if (dbus_message_is_method_call(message, SERVICE_NAME, "start"))
242 /* TODO: start logging */
244 reply = dbus_message_new_method_return(message);
245 dbus_connection_send(connection, reply, NULL);
246 dbus_message_unref(reply);
248 return DBUS_HANDLER_RESULT_HANDLED;
251 if (dbus_message_is_method_call(message, SERVICE_NAME, "end"))
255 /* TODO: stop logging */
257 reply = dbus_message_new_method_return(message);
258 dbus_connection_send(connection, reply, NULL);
259 dbus_message_unref(reply);
261 return DBUS_HANDLER_RESULT_HANDLED;
264 if (strlen(dbus_message_get_signature(message)) == 0)
266 DBusMessage *reply = dbus_message_new_error(message,
267 SERVICE_NAME ".ArgError",
269 dbus_connection_send(connection, reply, NULL);
270 dbus_message_unref(reply);
271 return DBUS_HANDLER_RESULT_HANDLED;
274 #define BASIC_TYPE(method_name, dtype) \
275 if (dbus_message_is_method_call(message, SERVICE_NAME, method_name)) \
277 dbus_uint64_t value = 0; \
279 DBusMessage *reply = NULL; \
280 DBusMessage *signal = NULL; \
281 dbus_error_init(&err); \
282 if (!dbus_message_get_args(message, &err, \
283 DBUS_TYPE_ ## dtype, &value, \
284 DBUS_TYPE_INVALID)) \
286 reply = dbus_message_new_error(message, \
287 SERVICE_NAME ".ArgError", \
289 g_debug(err.message); \
290 g_assert(reply != NULL); \
292 reply = dbus_message_new_method_return(message); \
293 g_assert(reply != NULL); \
294 dbus_message_append_args(reply, \
295 DBUS_TYPE_ ## dtype, &value, \
296 DBUS_TYPE_INVALID); \
297 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, method_name); \
298 g_debug("Appending %llu to signal", value); \
299 dbus_message_append_args(signal, \
300 DBUS_TYPE_ ## dtype, &value, \
301 DBUS_TYPE_INVALID); \
303 dbus_connection_send(connection, reply, NULL); \
304 dbus_message_unref(reply); \
305 if (signal != NULL) \
307 dbus_connection_send(connection, signal, NULL); \
308 dbus_message_unref(signal); \
310 return DBUS_HANDLER_RESULT_HANDLED; \
313 BASIC_TYPE("Boolean", BOOLEAN);
314 BASIC_TYPE("Byte", BYTE);
315 BASIC_TYPE("Int16", INT16);
316 BASIC_TYPE("Int32", INT32);
317 BASIC_TYPE("Int64", INT64);
318 BASIC_TYPE("UInt16", UINT16);
319 BASIC_TYPE("UInt32", UINT32);
320 BASIC_TYPE("UInt64", UINT64);
321 BASIC_TYPE("Double", DOUBLE);
322 BASIC_TYPE("String", STRING);
323 BASIC_TYPE("ObjectPath", OBJECT_PATH);
324 BASIC_TYPE("Signature", SIGNATURE);
326 if (dbus_message_is_method_call(message, SERVICE_NAME, "Array"))
328 DBusMessageIter iter;
329 DBusMessageIter reply_iter;
333 dbus_message_iter_init(message, &iter);
335 reply = dbus_message_new_method_return(message);
337 dbus_message_iter_init_append(reply, &reply_iter);
338 u_transfer_array(&reply_iter, &iter);
340 dbus_message_iter_init(message, &iter);
341 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Array");
342 dbus_message_iter_init_append(signal, &reply_iter);
343 u_transfer_array(&reply_iter, &iter);
345 dbus_connection_send(connection, reply, NULL);
346 dbus_message_unref(reply);
347 dbus_connection_send(connection, signal, NULL);
348 dbus_message_unref(signal);
349 return DBUS_HANDLER_RESULT_HANDLED;
352 if (dbus_message_is_method_call(message, SERVICE_NAME, "Variant"))
354 DBusMessageIter iter;
355 DBusMessageIter reply_iter;
357 DBusMessage *signal = NULL;
359 if (!g_str_equal(dbus_message_get_signature(message), "v"))
361 reply = dbus_message_new_error(message,
362 SERVICE_NAME ".ArgError",
363 "Signature mismatch");
364 dbus_connection_send(connection, reply, NULL);
365 dbus_message_unref(reply);
366 return DBUS_HANDLER_RESULT_HANDLED;
368 dbus_message_iter_init(message, &iter);
370 reply = dbus_message_new_method_return(message);
371 dbus_message_iter_init_append(reply, &reply_iter);
372 u_transfer_variant(&reply_iter, &iter);
374 dbus_message_iter_init(message, &iter);
375 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Variant");
376 dbus_message_iter_init_append(signal, &reply_iter);
377 u_transfer_variant(&reply_iter, &iter);
379 dbus_connection_send(connection, reply, NULL);
380 dbus_message_unref(reply);
381 dbus_connection_send(connection, signal, NULL);
382 dbus_message_unref(signal);
383 return DBUS_HANDLER_RESULT_HANDLED;
386 if (dbus_message_is_method_call(message, SERVICE_NAME, "Dict"))
388 DBusMessageIter iter;
389 DBusMessageIter reply_iter;
393 dbus_message_iter_init(message, &iter);
395 reply = dbus_message_new_method_return(message);
396 dbus_message_iter_init_append(reply, &reply_iter);
398 u_transfer_dict(&reply_iter, &iter);
400 dbus_message_iter_init(message, &iter);
401 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Dict");
402 dbus_message_iter_init_append(signal, &reply_iter);
403 u_transfer_dict(&reply_iter, &iter);
405 dbus_connection_send(connection, reply, NULL);
406 dbus_message_unref(reply);
407 dbus_connection_send(connection, signal, NULL);
408 dbus_message_unref(signal);
410 return DBUS_HANDLER_RESULT_HANDLED;
413 if (dbus_message_is_method_call(message, SERVICE_NAME, "Struct"))
415 DBusMessageIter iter;
416 DBusMessageIter reply_iter;
419 const char *signature = dbus_message_get_signature(message);
421 if (signature[0] != '(')
422 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
424 dbus_message_iter_init(message, &iter);
426 reply = dbus_message_new_method_return(message);
427 dbus_message_iter_init_append(reply, &reply_iter);
429 u_transfer_struct(&reply_iter, &iter);
431 dbus_message_iter_init(message, &iter);
432 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Struct");
433 dbus_message_iter_init_append(signal, &reply_iter);
434 u_transfer_struct(&reply_iter, &iter);
436 dbus_connection_send(connection, reply, NULL);
437 dbus_message_unref(reply);
438 dbus_connection_send(connection, signal, NULL);
439 dbus_message_unref(signal);
441 return DBUS_HANDLER_RESULT_HANDLED;
445 g_debug("Didn't handle the message");
449 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
452 int main(int argc, char **argv)
455 DBusConnection* conn;
456 DBusObjectPathVTable vtable;
459 dbus_error_init(&err);
460 conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
461 if (dbus_error_is_set(&err)) {
462 g_warning("Connection Error (%s)\n", err.message);
463 dbus_error_free(&err);
465 g_assert(conn != NULL);
467 vtable.unregister_function = u_unregister;
468 vtable.message_function = u_message;
470 if (!dbus_connection_register_object_path(conn, OBJECT_PATH,
473 g_error("Could not register object path");
476 ret = dbus_bus_request_name(conn, SERVICE_NAME,
477 DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
478 if (dbus_error_is_set(&err)) {
479 g_warning("Requesting service name failed (%s)\n", err.message);
480 dbus_error_free(&err);
482 g_assert(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER == ret);
484 dbus_bus_add_match(conn, "type='method_call'", &err);
485 if (dbus_error_is_set(&err)) {
486 g_warning("Add match failed (%s)\n", err.message);
487 dbus_error_free(&err);
491 loop = g_main_loop_new(NULL, FALSE);
492 dbus_connection_setup_with_g_main(conn, NULL);
494 g_print("Unit ready to accept method calls\n");
495 g_main_loop_run(loop);