ladishd: remove unneeded asignment
[ladish.git] / dbus / object_path.c
blob80b431918292f3e3850de1118138fdb903aa50bc
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;
45 bool registered;
48 #define write_buf(args...) buf_ptr += sprintf(buf_ptr, ## args)
50 DBusMessage *
51 introspection_new(struct dbus_object_path * opath_ptr)
53 char *xml_data, *buf_ptr;
54 const struct dbus_object_path_interface * iface_ptr;
55 const struct dbus_method_descriptor * method_ptr;
56 const struct dbus_method_arg_descriptor * method_arg_ptr;
57 const struct dbus_signal_descriptor * signal_ptr;
58 const struct dbus_signal_arg_descriptor * signal_arg_ptr;
59 DBusMessage * msg;
60 DBusMessageIter iter;
62 log_debug("Creating introspection message");
65 * Create introspection XML data.
68 /* TODO: we assume that 16 KiB is enough to hold introspection xml.
69 * If it gets larger, memory corruption will occur.
70 * Use realloc-like algorithm instead */
71 xml_data = malloc(16384);
72 if (xml_data == NULL)
74 return NULL;
77 buf_ptr = xml_data;
79 write_buf("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
80 " \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
81 "<node name=\"%s\">\n", opath_ptr->name);
83 /* Add the object path's interfaces. */
84 for (iface_ptr = opath_ptr->ifaces; iface_ptr->iface != NULL; iface_ptr++)
86 write_buf(" <interface name=\"%s\">\n", iface_ptr->iface->name);
87 if (iface_ptr->iface->methods != NULL)
89 /* Add the interface's methods. */
90 for (method_ptr = iface_ptr->iface->methods; method_ptr->name != NULL; method_ptr++)
92 write_buf(" <method name=\"%s\">\n", method_ptr->name);
93 /* Add the method's arguments. */
94 for (method_arg_ptr = method_ptr->args; method_arg_ptr->name != NULL; method_arg_ptr++)
96 write_buf(
97 " <arg name=\"%s\" type=\"%s\" direction=\"%s\" />\n",
98 method_arg_ptr->name,
99 method_arg_ptr->type,
100 method_arg_ptr->direction_in ? "in" : "out");
102 write_buf(" </method>\n");
105 if (iface_ptr->iface->signals != NULL)
107 /* Add the interface's signals. */
108 for (signal_ptr = iface_ptr->iface->signals; signal_ptr->name != NULL; signal_ptr++)
110 write_buf(" <signal name=\"%s\">\n", signal_ptr->name);
111 /* Add the signal's arguments. */
112 for (signal_arg_ptr = signal_ptr->args; signal_arg_ptr->name != NULL; signal_arg_ptr++)
114 write_buf(" <arg name=\"%s\" type=\"%s\" />\n", signal_arg_ptr->name, signal_arg_ptr->type);
116 write_buf(" </signal>\n");
119 write_buf(" </interface>\n");
121 write_buf("</node>\n");
124 * Create a D-Bus message from the XML data.
127 if ((msg = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN)))
129 dbus_message_iter_init_append(msg, &iter);
130 if (dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, (const void *) &xml_data))
132 dbus_message_set_no_reply(msg, TRUE);
134 else
136 dbus_message_unref(msg);
137 msg = NULL;
138 log_error("Failed to append data to introspection message");
141 else
143 log_error("Failed to create introspection message");
146 free(xml_data);
147 return msg;
150 #undef write_buf
152 void
153 introspection_destroy(struct dbus_object_path *path)
155 log_debug("Destroying introspection message");
157 if (path && path->introspection) {
158 dbus_message_unref(path->introspection);
159 path->introspection = NULL;
161 #ifdef LADISH_DEBUG
162 else
163 log_debug("Nothing to destroy");
164 #endif
167 static bool introspection_handler(const struct dbus_interface_descriptor * interface, struct dbus_method_call * call_ptr)
169 if (strcmp(call_ptr->method_name, "Introspect") != 0)
171 /* The requested method wasn't "Introspect". */
172 return false;
175 /* Try to construct the instrospection message */
176 call_ptr->reply = dbus_message_copy(call_ptr->iface_context); /* context contains the reply message */
177 if (call_ptr->reply == NULL)
179 log_error("Ran out of memory trying to copy introspection message");
180 goto fail;
183 if (!dbus_message_set_destination(call_ptr->reply, dbus_message_get_sender(call_ptr->message)))
185 log_error("dbus_message_set_destination() failed.");
186 goto unref_reply;
189 if (!dbus_message_set_reply_serial(call_ptr->reply, dbus_message_get_serial(call_ptr->message)))
191 log_error("dbus_message_set_reply_serial() failed.");
192 goto unref_reply;
195 return true;
197 unref_reply:
198 dbus_message_unref(call_ptr->reply);
199 call_ptr->reply = NULL;
201 fail:
202 /* Even after an error we need to return true, because the
203 handler is only supposed to return false if a nonexistent
204 method is requested. */
205 return true;
208 METHOD_ARGS_BEGIN(Introspect, "Get introspection XML")
209 METHOD_ARG_DESCRIBE_OUT("xml_data", "s", "XML description of the object")
210 METHOD_ARGS_END
212 METHODS_BEGIN
213 METHOD_DESCRIBE(Introspect, NULL)
214 METHODS_END
216 INTERFACE_BEGIN(g_dbus_interface_dtor_introspectable, "org.freedesktop.DBus.Introspectable")
217 INTERFACE_HANDLER(introspection_handler)
218 INTERFACE_EXPOSE_METHODS
219 INTERFACE_END
221 dbus_object_path dbus_object_path_new(const char *name, const struct dbus_interface_descriptor * iface1_ptr, ...)
223 struct dbus_object_path * opath_ptr;
224 va_list ap;
225 const struct dbus_interface_descriptor * iface_src_ptr;
226 struct dbus_object_path_interface * iface_dst_ptr;
227 void * iface_context;
228 size_t len;
230 log_debug("Creating object path");
232 opath_ptr = malloc(sizeof(struct dbus_object_path));
233 if (opath_ptr == NULL)
235 log_error("malloc() failed to allocate struct dbus_object_path.");
236 goto fail;
239 opath_ptr->name = strdup(name);
240 if (opath_ptr->name == NULL)
242 log_error("malloc() failed to allocate struct dbus_object_path.");
243 goto free;
246 va_start(ap, iface1_ptr);
247 iface_src_ptr = iface1_ptr;
248 len = 0;
249 while (iface_src_ptr != NULL)
251 iface_context = va_arg(ap, void *);
252 iface_src_ptr = va_arg(ap, const struct dbus_interface_descriptor *);
253 len++;
255 va_end(ap);
257 opath_ptr->ifaces = malloc((len + 2) * sizeof(struct dbus_object_path_interface));
258 if (opath_ptr->ifaces == NULL)
260 log_error("malloc failed to allocate interfaces array");
261 goto free_name;
264 va_start(ap, iface1_ptr);
265 iface_src_ptr = iface1_ptr;
266 iface_dst_ptr = opath_ptr->ifaces;
267 while (iface_src_ptr != NULL)
269 iface_dst_ptr->iface = iface_src_ptr;
270 iface_dst_ptr->iface_context = va_arg(ap, void *);
271 iface_src_ptr = va_arg(ap, const struct dbus_interface_descriptor *);
272 iface_dst_ptr++;
273 len--;
275 va_end(ap);
277 ASSERT(len == 0);
279 iface_dst_ptr->iface = NULL;
280 opath_ptr->introspection = introspection_new(opath_ptr);
281 if (opath_ptr->introspection == NULL)
283 log_error("introspection_new() failed.");
284 goto free_ifaces;
287 iface_dst_ptr->iface = &g_dbus_interface_dtor_introspectable;
288 iface_dst_ptr->iface_context = opath_ptr->introspection;
289 iface_dst_ptr++;
290 iface_dst_ptr->iface = NULL;
292 opath_ptr->registered = false;
294 return (dbus_object_path)opath_ptr;
296 free_ifaces:
297 free(opath_ptr->ifaces);
298 free_name:
299 free(opath_ptr->name);
300 free:
301 free(opath_ptr);
302 fail:
303 return NULL;
306 #define opath_ptr ((struct dbus_object_path *)data)
308 void dbus_object_path_unregister(DBusConnection * connection_ptr, dbus_object_path data)
310 ASSERT(opath_ptr->registered);
312 if (!dbus_connection_unregister_object_path(connection_ptr, opath_ptr->name))
314 log_error("dbus_connection_unregister_object_path() failed.");
318 void dbus_object_path_destroy(DBusConnection * connection_ptr, dbus_object_path data)
320 log_debug("Destroying object path");
322 if (opath_ptr->registered && connection_ptr != NULL && !dbus_connection_unregister_object_path(connection_ptr, opath_ptr->name))
324 log_error("dbus_connection_unregister_object_path() failed.");
327 introspection_destroy(opath_ptr);
328 free(opath_ptr->ifaces);
329 free(opath_ptr->name);
330 free(opath_ptr);
333 static DBusHandlerResult dbus_object_path_handler(DBusConnection * connection, DBusMessage * message, void * data)
335 const char * iface_name;
336 const struct dbus_object_path_interface * iface_ptr;
337 struct dbus_method_call call;
339 /* Check if the message is a method call. If not, ignore it. */
340 if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
342 goto handled;
345 /* Get the invoked method's name and make sure it's non-NULL. */
346 call.method_name = dbus_message_get_member(message);
347 if (call.method_name == NULL)
349 lash_dbus_error(&call, LASH_DBUS_ERROR_UNKNOWN_METHOD, "Received method call with empty method name");
350 goto send_return;
353 /* Initialize our data. */
354 call.connection = connection;
355 call.message = message;
356 call.iface = NULL; /* To be set by the default interface handler */
357 call.reply = NULL;
359 /* Check if there's an interface specified for this method call. */
360 iface_name = dbus_message_get_interface(message);
361 if (iface_name != NULL)
363 for (iface_ptr = opath_ptr->ifaces; iface_ptr->iface != NULL; iface_ptr++)
365 if (strcmp(iface_name, iface_ptr->iface->name) == 0)
367 call.iface_context = iface_ptr->iface_context;
368 if (!iface_ptr->iface->handler(iface_ptr->iface, &call))
370 /* unknown method */
371 break;
374 goto send_return;
378 else
380 /* No interface was specified so we have to try them all. D-Bus spec states:
382 * Optionally, the message has an INTERFACE field giving the interface the method is a part of.
383 * In the absence of an INTERFACE field, if two interfaces on the same object have a method with
384 * the same name, it is undefined which of the two methods will be invoked.
385 * Implementations may also choose to return an error in this ambiguous case.
386 * However, if a method name is unique implementations must not require an interface field.
388 for (iface_ptr = opath_ptr->ifaces; iface_ptr->iface != NULL; iface_ptr++)
390 call.iface_context = iface_ptr->iface_context;
391 if (!iface_ptr->iface->handler(iface_ptr->iface, &call))
393 /* known method */
394 goto send_return;
399 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);
401 send_return:
402 method_return_send(&call);
404 handled:
405 return DBUS_HANDLER_RESULT_HANDLED;
408 static void dbus_object_path_handler_unregister(DBusConnection * connection_ptr, void * data)
410 #ifdef LADISH_DEBUG
411 log_debug("Message handler of object path %s was unregistered", (opath_ptr && path->name) ? opath_ptr->name : "<unknown>");
412 #endif /* LADISH_DEBUG */
415 bool dbus_object_path_register(DBusConnection * connection_ptr, dbus_object_path data)
417 log_debug("Registering object path \"%s\"", opath_ptr->name);
419 ASSERT(!opath_ptr->registered);
421 DBusObjectPathVTable vtable =
423 dbus_object_path_handler_unregister,
424 dbus_object_path_handler,
425 NULL, NULL, NULL, NULL
428 if (!dbus_connection_register_object_path(connection_ptr, opath_ptr->name, &vtable, opath_ptr))
430 return false;
433 opath_ptr->registered = true;
434 return true;
437 #undef opath_ptr