daemon: jmcore support in virtualizer
[ladish.git] / dbus / object_path.c
blobd853ce0d6f3048d0a9413a21e1eec82241d5cc69
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008, 2009, 2010 Nedko Arnaudov <nedko@arnaudov.name>
6 * Copyright (C) 2008 Juuso Alasuutari <juuso.alasuutari@gmail.com>
8 **************************************************************************
9 * This file contains D-Bus object path helpers
10 **************************************************************************
12 * Licensed under the Academic Free License version 2.1
14 * LADI Session Handler is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * LADI Session Handler is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
26 * or write to the Free Software Foundation, Inc.,
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include "../common.h"
31 #include "helpers.h"
32 #include "error.h" /* lash_dbus_error() */
34 struct dbus_object_path_interface
36 const struct dbus_interface_descriptor * iface;
37 void * iface_context;
40 struct dbus_object_path
42 char * name;
43 DBusMessage * introspection;
44 struct dbus_object_path_interface * ifaces;
47 #define write_buf(args...) buf_ptr += sprintf(buf_ptr, ## args)
49 DBusMessage *
50 introspection_new(struct dbus_object_path * opath_ptr)
52 char *xml_data, *buf_ptr;
53 const struct dbus_object_path_interface * iface_ptr;
54 const struct dbus_method_descriptor * method_ptr;
55 const struct dbus_method_arg_descriptor * method_arg_ptr;
56 const struct dbus_signal_descriptor * signal_ptr;
57 const struct dbus_signal_arg_descriptor * signal_arg_ptr;
58 DBusMessage * msg;
59 DBusMessageIter iter;
61 log_debug("Creating introspection message");
64 * Create introspection XML data.
67 /* TODO: we assume that 16 KiB is enough to hold introspection xml.
68 * If it gets larger, memory corruption will occur.
69 * Use realloc-like algorithm instead */
70 xml_data = malloc(16384);
71 if (xml_data == NULL)
73 return NULL;
76 buf_ptr = xml_data;
78 write_buf("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
79 " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
80 "<node name=\"%s\">\n", opath_ptr->name);
82 /* Add the object path's interfaces. */
83 for (iface_ptr = opath_ptr->ifaces; iface_ptr->iface != NULL; iface_ptr++)
85 write_buf(" <interface name=\"%s\">\n", iface_ptr->iface->name);
86 if (iface_ptr->iface->methods != NULL)
88 /* Add the interface's methods. */
89 for (method_ptr = iface_ptr->iface->methods; method_ptr->name != NULL; method_ptr++)
91 write_buf(" <method name=\"%s\">\n", method_ptr->name);
92 /* Add the method's arguments. */
93 for (method_arg_ptr = method_ptr->args; method_arg_ptr->name != NULL; method_arg_ptr++)
95 write_buf(
96 " <arg name=\"%s\" type=\"%s\" direction=\"%s\" />\n",
97 method_arg_ptr->name,
98 method_arg_ptr->type,
99 method_arg_ptr->direction_in ? "in" : "out");
101 write_buf(" </method>\n");
104 if (iface_ptr->iface->signals != NULL)
106 /* Add the interface's signals. */
107 for (signal_ptr = iface_ptr->iface->signals; signal_ptr->name != NULL; signal_ptr++)
109 write_buf(" <signal name=\"%s\">\n", signal_ptr->name);
110 /* Add the signal's arguments. */
111 for (signal_arg_ptr = signal_ptr->args; signal_arg_ptr->name != NULL; signal_arg_ptr++)
113 write_buf(" <arg name=\"%s\" type=\"%s\" />\n", signal_arg_ptr->name, signal_arg_ptr->type);
115 write_buf(" </signal>\n");
118 write_buf(" </interface>\n");
120 write_buf("</node>\n");
123 * Create a D-Bus message from the XML data.
126 if ((msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN)))
128 dbus_message_iter_init_append(msg, &iter);
129 if (dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, (const void *) &xml_data))
131 dbus_message_set_no_reply(msg, TRUE);
133 else
135 dbus_message_unref(msg);
136 msg = NULL;
137 log_error("Failed to append data to introspection message");
140 else
142 log_error("Failed to create introspection message");
145 free(xml_data);
146 return msg;
149 #undef write_buf
151 void
152 introspection_destroy(struct dbus_object_path *path)
154 log_debug("Destroying introspection message");
156 if (path && path->introspection) {
157 dbus_message_unref(path->introspection);
158 path->introspection = NULL;
160 #ifdef LADISH_DEBUG
161 else
162 log_debug("Nothing to destroy");
163 #endif
166 static bool introspection_handler(const struct dbus_interface_descriptor * interface, struct dbus_method_call * call_ptr)
168 if (strcmp(call_ptr->method_name, "Introspect") != 0)
170 /* The requested method wasn't "Introspect". */
171 return false;
174 /* Try to construct the instrospection message */
175 call_ptr->reply = dbus_message_copy(call_ptr->iface_context); /* context contains the reply message */
176 if (call_ptr->reply == NULL)
178 log_error("Ran out of memory trying to copy introspection message");
179 goto fail;
182 if (!dbus_message_set_destination(call_ptr->reply, dbus_message_get_sender(call_ptr->message)))
184 log_error("dbus_message_set_destination() failed.");
185 goto unref_reply;
188 if (!dbus_message_set_reply_serial(call_ptr->reply, dbus_message_get_serial(call_ptr->message)))
190 log_error("dbus_message_set_reply_serial() failed.");
191 goto unref_reply;
194 return true;
196 unref_reply:
197 dbus_message_unref(call_ptr->reply);
198 call_ptr->reply = NULL;
200 fail:
201 /* Even after an error we need to return true, because the
202 handler is only supposed to return false if a nonexistent
203 method is requested. */
204 return true;
207 METHOD_ARGS_BEGIN(Introspect, "Get introspection XML")
208 METHOD_ARG_DESCRIBE_OUT("xml_data", "s", "XML description of the object")
209 METHOD_ARGS_END
211 METHODS_BEGIN
212 METHOD_DESCRIBE(Introspect, NULL)
213 METHODS_END
215 INTERFACE_BEGIN(g_dbus_interface_dtor_introspectable, "org.freedesktop.DBus.Introspectable")
216 INTERFACE_HANDLER(introspection_handler)
217 INTERFACE_EXPOSE_METHODS
218 INTERFACE_END
220 dbus_object_path dbus_object_path_new(const char *name, const struct dbus_interface_descriptor * iface1_ptr, ...)
222 struct dbus_object_path * opath_ptr;
223 va_list ap;
224 const struct dbus_interface_descriptor * iface_src_ptr;
225 struct dbus_object_path_interface * iface_dst_ptr;
226 void * iface_context;
227 size_t len;
229 log_debug("Creating object path");
231 opath_ptr = malloc(sizeof(struct dbus_object_path));
232 if (opath_ptr == NULL)
234 log_error("malloc() failed to allocate struct dbus_object_path.");
235 goto fail;
238 opath_ptr->name = strdup(name);
239 if (opath_ptr->name == NULL)
241 log_error("malloc() failed to allocate struct dbus_object_path.");
242 goto free;
245 va_start(ap, iface1_ptr);
246 iface_src_ptr = iface1_ptr;
247 len = 0;
248 while (iface_src_ptr != NULL)
250 iface_context = va_arg(ap, void *);
251 iface_src_ptr = va_arg(ap, const struct dbus_interface_descriptor *);
252 len++;
254 va_end(ap);
256 opath_ptr->ifaces = malloc((len + 2) * sizeof(struct dbus_object_path_interface));
257 if (opath_ptr->ifaces == NULL)
259 log_error("malloc failed to allocate interfaces array");
260 goto free_name;
263 va_start(ap, iface1_ptr);
264 iface_src_ptr = iface1_ptr;
265 iface_dst_ptr = opath_ptr->ifaces;
266 while (iface_src_ptr != NULL)
268 iface_dst_ptr->iface = iface_src_ptr;
269 iface_dst_ptr->iface_context = va_arg(ap, void *);
270 iface_src_ptr = va_arg(ap, const struct dbus_interface_descriptor *);
271 iface_dst_ptr++;
272 len--;
274 va_end(ap);
276 ASSERT(len == 0);
278 iface_dst_ptr->iface = NULL;
279 opath_ptr->introspection = introspection_new(opath_ptr);
280 if (opath_ptr->introspection == NULL)
282 log_error("introspection_new() failed.");
283 goto free_ifaces;
286 iface_dst_ptr->iface = &g_dbus_interface_dtor_introspectable;
287 iface_dst_ptr->iface_context = opath_ptr->introspection;
288 iface_dst_ptr++;
289 iface_dst_ptr->iface = NULL;
291 return (dbus_object_path)opath_ptr;
293 free_ifaces:
294 free(opath_ptr->ifaces);
295 free_name:
296 free(opath_ptr->name);
297 free:
298 free(opath_ptr);
299 fail:
300 return NULL;
303 #define opath_ptr ((struct dbus_object_path *)data)
305 void dbus_object_path_destroy(DBusConnection * connection_ptr, dbus_object_path data)
307 log_debug("Destroying object path");
309 if (connection_ptr != NULL && !dbus_connection_unregister_object_path(connection_ptr, opath_ptr->name))
311 log_error("dbus_connection_unregister_object_path() failed.");
314 introspection_destroy(opath_ptr);
315 free(opath_ptr->ifaces);
316 free(opath_ptr->name);
317 free(opath_ptr);
320 static DBusHandlerResult dbus_object_path_handler(DBusConnection * connection, DBusMessage * message, void * data)
322 const char * iface_name;
323 const struct dbus_object_path_interface * iface_ptr;
324 struct dbus_method_call call;
326 /* Check if the message is a method call. If not, ignore it. */
327 if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
329 goto handled;
332 /* Get the invoked method's name and make sure it's non-NULL. */
333 call.method_name = dbus_message_get_member(message);
334 if (call.method_name == NULL)
336 lash_dbus_error(&call, LASH_DBUS_ERROR_UNKNOWN_METHOD, "Received method call with empty method name");
337 goto send_return;
340 /* Initialize our data. */
341 call.connection = connection;
342 call.message = message;
343 call.iface = NULL; /* To be set by the default interface handler */
344 call.reply = NULL;
346 /* Check if there's an interface specified for this method call. */
347 iface_name = dbus_message_get_interface(message);
348 if (iface_name != NULL)
350 for (iface_ptr = opath_ptr->ifaces; iface_ptr->iface != NULL; iface_ptr++)
352 if (strcmp(iface_name, iface_ptr->iface->name) == 0)
354 call.iface_context = iface_ptr->iface_context;
355 if (!iface_ptr->iface->handler(iface_ptr->iface, &call))
357 /* unknown method */
358 break;
361 goto send_return;
365 else
367 /* No interface was specified so we have to try them all. D-Bus spec states:
369 * Optionally, the message has an INTERFACE field giving the interface the method is a part of.
370 * In the absence of an INTERFACE field, if two interfaces on the same object have a method with
371 * the same name, it is undefined which of the two methods will be invoked.
372 * Implementations may also choose to return an error in this ambiguous case.
373 * However, if a method name is unique implementations must not require an interface field.
375 for (iface_ptr = opath_ptr->ifaces; iface_ptr->iface != NULL; iface_ptr++)
377 call.iface_context = iface_ptr->iface_context;
378 if (!iface_ptr->iface->handler(iface_ptr->iface, &call))
380 /* known method */
381 goto send_return;
386 lash_dbus_error(&call, LASH_DBUS_ERROR_UNKNOWN_METHOD, "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist", call.method_name, dbus_message_get_signature(message), iface_name);
388 send_return:
389 method_return_send(&call);
391 handled:
392 return DBUS_HANDLER_RESULT_HANDLED;
395 static void dbus_object_path_handler_unregister(DBusConnection * connection_ptr, void * data)
397 #ifdef LADISH_DEBUG
398 log_debug("Message handler of object path %s was unregistered", (opath_ptr && path->name) ? opath_ptr->name : "<unknown>");
399 #endif /* LADISH_DEBUG */
402 bool dbus_object_path_register(DBusConnection * connection_ptr, dbus_object_path data)
404 log_debug("Registering object path \"%s\"", opath_ptr->name);
406 DBusObjectPathVTable vtable =
408 dbus_object_path_handler_unregister,
409 dbus_object_path_handler,
410 NULL, NULL, NULL, NULL
413 return dbus_connection_register_object_path(connection_ptr, opath_ptr->name, &vtable, opath_ptr);
416 #undef opath_ptr