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 dbus_message_iter_append_basic(to, type, &value);
99 u_transfer_variant(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
102 DBusMessageIter from;
103 const char *sig = NULL;
105 dbus_message_iter_recurse(from_iter, &from);
106 sig = dbus_message_iter_get_signature(&from);
111 dbus_message_iter_open_container(to_iter, DBUS_TYPE_VARIANT, sig, &to);
114 u_transfer_arg(&to, &from);
115 } while (dbus_message_iter_next(&from));
117 dbus_message_iter_close_container(to_iter, &to);
123 u_transfer_array(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
126 DBusMessageIter from;
130 dbus_message_iter_recurse(from_iter, &from);
132 elem_type = dbus_message_iter_get_element_type(from_iter);
135 case DBUS_TYPE_ARRAY:
137 /* Fetch the element type for this too */
138 sig = g_strdup(dbus_message_iter_get_signature(from_iter)+1);
141 case DBUS_TYPE_DICT_ENTRY:
142 case DBUS_TYPE_STRUCT:
143 g_warning("Element type '%c' in arrays not supported", elem_type);
146 sig = g_strdup_printf("%c", dbus_message_iter_get_element_type(from_iter));
153 dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
156 u_transfer_arg(&to, &from);
157 } while (dbus_message_iter_next(&from));
159 dbus_message_iter_close_container(to_iter, &to);
165 u_transfer_dict(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
168 DBusMessageIter from;
171 dbus_message_iter_recurse(from_iter, &from);
172 sig = g_strdup(dbus_message_iter_get_signature(&from));
177 dbus_message_iter_open_container(to_iter, DBUS_TYPE_ARRAY, sig, &to);
179 DBusMessageIter subfrom;
180 DBusMessageIter dictiter;
181 dbus_message_iter_open_container(&to, DBUS_TYPE_DICT_ENTRY,
183 dbus_message_iter_recurse(&from, &subfrom);
185 u_transfer_arg(&dictiter, &subfrom);
186 if (dbus_message_iter_next(&subfrom)) {
187 u_transfer_arg(&dictiter, &subfrom);
189 dbus_message_iter_close_container(&to, &dictiter);
190 } while (dbus_message_iter_next(&from));
192 dbus_message_iter_close_container(to_iter, &to);
198 u_transfer_struct(DBusMessageIter *to_iter, DBusMessageIter *from_iter)
201 DBusMessageIter from;
203 dbus_message_iter_recurse(from_iter, &from);
204 dbus_message_iter_open_container(to_iter, DBUS_TYPE_STRUCT, NULL, &to);
207 u_transfer_arg(&to, &from);
208 } while (dbus_message_iter_next(&from));
210 dbus_message_iter_close_container(to_iter, &to);
215 static DBusHandlerResult
216 u_message(DBusConnection *connection, DBusMessage *message, void *user_data)
219 const char *path = dbus_message_get_path(message);
220 const char *interface = dbus_message_get_interface(message);
221 const char *member = dbus_message_get_member(message);
222 const char *signature = dbus_message_get_signature(message);
224 g_debug("Message received, path %s, interface %s, member %s, signature %s",
225 path == NULL ? "not set" : path,
226 interface == NULL ? "not set" : interface,
227 member == NULL ? "not set" : member,
228 signature == NULL ? "not set" : signature);
231 if (!dbus_message_has_path(message, OBJECT_PATH))
232 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
234 if (dbus_message_is_method_call(message, SERVICE_NAME, "start"))
238 /* TODO: start logging */
240 reply = dbus_message_new_method_return(message);
241 dbus_connection_send(connection, reply, NULL);
242 dbus_message_unref(reply);
244 return DBUS_HANDLER_RESULT_HANDLED;
247 if (dbus_message_is_method_call(message, SERVICE_NAME, "end"))
251 /* TODO: stop logging */
253 reply = dbus_message_new_method_return(message);
254 dbus_connection_send(connection, reply, NULL);
255 dbus_message_unref(reply);
257 return DBUS_HANDLER_RESULT_HANDLED;
260 if (strlen(dbus_message_get_signature(message)) == 0)
262 DBusMessage *reply = dbus_message_new_error(message,
263 SERVICE_NAME ".ArgError",
265 dbus_connection_send(connection, reply, NULL);
266 dbus_message_unref(reply);
267 return DBUS_HANDLER_RESULT_HANDLED;
270 #define BASIC_TYPE(method_name, dtype) \
271 if (dbus_message_is_method_call(message, SERVICE_NAME, method_name)) \
273 dbus_uint64_t value = 0; \
275 DBusMessage *reply = NULL; \
276 DBusMessage *signal = NULL; \
277 dbus_error_init(&err); \
278 if (!dbus_message_get_args(message, &err, \
279 DBUS_TYPE_ ## dtype, &value, \
280 DBUS_TYPE_INVALID)) \
282 reply = dbus_message_new_error(message, \
283 SERVICE_NAME ".ArgError", \
285 g_debug(err.message); \
286 g_assert(reply != NULL); \
288 reply = dbus_message_new_method_return(message); \
289 g_assert(reply != NULL); \
290 dbus_message_append_args(reply, \
291 DBUS_TYPE_ ## dtype, &value, \
292 DBUS_TYPE_INVALID); \
293 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, method_name); \
294 g_debug("Appending %llu to signal", value); \
295 dbus_message_append_args(signal, \
296 DBUS_TYPE_ ## dtype, &value, \
297 DBUS_TYPE_INVALID); \
299 dbus_connection_send(connection, reply, NULL); \
300 dbus_message_unref(reply); \
301 if (signal != NULL) \
303 dbus_connection_send(connection, signal, NULL); \
304 dbus_message_unref(signal); \
306 return DBUS_HANDLER_RESULT_HANDLED; \
309 BASIC_TYPE("Boolean", BOOLEAN);
310 BASIC_TYPE("Byte", BYTE);
311 BASIC_TYPE("Int16", INT16);
312 BASIC_TYPE("Int32", INT32);
313 BASIC_TYPE("Int64", INT64);
314 BASIC_TYPE("UInt16", UINT16);
315 BASIC_TYPE("UInt32", UINT32);
316 BASIC_TYPE("UInt64", UINT64);
317 BASIC_TYPE("Double", DOUBLE);
318 BASIC_TYPE("String", STRING);
319 BASIC_TYPE("ObjectPath", OBJECT_PATH);
320 BASIC_TYPE("Signature", SIGNATURE);
322 if (dbus_message_is_method_call(message, SERVICE_NAME, "Array"))
324 DBusMessageIter iter;
325 DBusMessageIter reply_iter;
329 dbus_message_iter_init(message, &iter);
331 reply = dbus_message_new_method_return(message);
333 dbus_message_iter_init_append(reply, &reply_iter);
334 u_transfer_array(&reply_iter, &iter);
336 dbus_message_iter_init(message, &iter);
337 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Array");
338 dbus_message_iter_init_append(signal, &reply_iter);
339 u_transfer_array(&reply_iter, &iter);
341 dbus_connection_send(connection, reply, NULL);
342 dbus_message_unref(reply);
343 dbus_connection_send(connection, signal, NULL);
344 dbus_message_unref(signal);
345 return DBUS_HANDLER_RESULT_HANDLED;
348 if (dbus_message_is_method_call(message, SERVICE_NAME, "Variant"))
350 DBusMessageIter iter;
351 DBusMessageIter reply_iter;
353 DBusMessage *signal = NULL;
355 if (!g_str_equal(dbus_message_get_signature(message), "v"))
357 reply = dbus_message_new_error(message,
358 SERVICE_NAME ".ArgError",
359 "Signature mismatch");
360 dbus_connection_send(connection, reply, NULL);
361 dbus_message_unref(reply);
362 return DBUS_HANDLER_RESULT_HANDLED;
364 dbus_message_iter_init(message, &iter);
366 reply = dbus_message_new_method_return(message);
367 dbus_message_iter_init_append(reply, &reply_iter);
368 u_transfer_variant(&reply_iter, &iter);
370 dbus_message_iter_init(message, &iter);
371 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Variant");
372 dbus_message_iter_init_append(signal, &reply_iter);
373 u_transfer_variant(&reply_iter, &iter);
375 dbus_connection_send(connection, reply, NULL);
376 dbus_message_unref(reply);
377 dbus_connection_send(connection, signal, NULL);
378 dbus_message_unref(signal);
379 return DBUS_HANDLER_RESULT_HANDLED;
382 if (dbus_message_is_method_call(message, SERVICE_NAME, "Dict"))
384 DBusMessageIter iter;
385 DBusMessageIter reply_iter;
389 dbus_message_iter_init(message, &iter);
391 reply = dbus_message_new_method_return(message);
392 dbus_message_iter_init_append(reply, &reply_iter);
394 u_transfer_dict(&reply_iter, &iter);
396 dbus_message_iter_init(message, &iter);
397 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Dict");
398 dbus_message_iter_init_append(signal, &reply_iter);
399 u_transfer_dict(&reply_iter, &iter);
401 dbus_connection_send(connection, reply, NULL);
402 dbus_message_unref(reply);
403 dbus_connection_send(connection, signal, NULL);
404 dbus_message_unref(signal);
406 return DBUS_HANDLER_RESULT_HANDLED;
409 if (dbus_message_is_method_call(message, SERVICE_NAME, "Struct"))
411 DBusMessageIter iter;
412 DBusMessageIter reply_iter;
415 const char *signature = dbus_message_get_signature(message);
417 if (signature[0] != '(')
418 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
420 dbus_message_iter_init(message, &iter);
422 reply = dbus_message_new_method_return(message);
423 dbus_message_iter_init_append(reply, &reply_iter);
425 u_transfer_struct(&reply_iter, &iter);
427 dbus_message_iter_init(message, &iter);
428 signal = dbus_message_new_signal(OBJECT_PATH, SERVICE_NAME, "Struct");
429 dbus_message_iter_init_append(signal, &reply_iter);
430 u_transfer_struct(&reply_iter, &iter);
432 dbus_connection_send(connection, reply, NULL);
433 dbus_message_unref(reply);
434 dbus_connection_send(connection, signal, NULL);
435 dbus_message_unref(signal);
437 return DBUS_HANDLER_RESULT_HANDLED;
441 g_debug("Didn't handle the message");
445 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
448 int main(int argc, char **argv)
451 DBusConnection* conn;
452 DBusObjectPathVTable vtable;
455 dbus_error_init(&err);
456 conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
457 if (dbus_error_is_set(&err)) {
458 g_warning("Connection Error (%s)\n", err.message);
459 dbus_error_free(&err);
461 g_assert(conn != NULL);
463 vtable.unregister_function = u_unregister;
464 vtable.message_function = u_message;
466 if (!dbus_connection_register_object_path(conn, OBJECT_PATH,
469 g_error("Could not register object path");
472 ret = dbus_bus_request_name(conn, SERVICE_NAME,
473 DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
474 if (dbus_error_is_set(&err)) {
475 g_warning("Requesting service name failed (%s)\n", err.message);
476 dbus_error_free(&err);
478 g_assert(DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER == ret);
480 dbus_bus_add_match(conn, "type='method_call'", &err);
481 if (dbus_error_is_set(&err)) {
482 g_warning("Add match failed (%s)\n", err.message);
483 dbus_error_free(&err);
487 loop = g_main_loop_new(NULL, FALSE);
488 dbus_connection_setup_with_g_main(conn, NULL);
490 g_print("Unit ready to accept method calls\n");
491 g_main_loop_run(loop);