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