Fix signal watch when a service name is given
[framework/connectivity/connman.git] / gdbus / watch.c
1 /*
2  *
3  *  D-Bus helper library
4  *
5  *  Copyright (C) 2004-2010  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 static DBusHandlerResult message_filter(DBusConnection *connection,
41                                         DBusMessage *message, void *user_data);
42
43 static guint listener_id = 0;
44 static GSList *listeners = NULL;
45
46 struct filter_callback {
47         GDBusWatchFunction conn_func;
48         GDBusWatchFunction disc_func;
49         GDBusSignalFunction signal_func;
50         GDBusDestroyFunction destroy_func;
51         void *user_data;
52         guint id;
53 };
54
55 struct filter_data {
56         DBusConnection *connection;
57         DBusHandleMessageFunction handle_func;
58         char *name;
59         char *owner;
60         char *path;
61         char *interface;
62         char *member;
63         char *argument;
64         GSList *callbacks;
65         GSList *processed;
66         guint name_watch;
67         gboolean lock;
68         gboolean registered;
69 };
70
71 static struct filter_data *filter_data_find(DBusConnection *connection,
72                                                         const char *name,
73                                                         const char *owner,
74                                                         const char *path,
75                                                         const char *interface,
76                                                         const char *member,
77                                                         const char *argument)
78 {
79         GSList *current;
80
81         for (current = listeners;
82                         current != NULL; current = current->next) {
83                 struct filter_data *data = current->data;
84
85                 if (connection != data->connection)
86                         continue;
87
88                 if (name && data->name &&
89                                 g_str_equal(name, data->name) == FALSE)
90                         continue;
91
92                 if (owner && data->owner &&
93                                 g_str_equal(owner, data->owner) == FALSE)
94                         continue;
95
96                 if (path && data->path &&
97                                 g_str_equal(path, data->path) == FALSE)
98                         continue;
99
100                 if (interface && data->interface &&
101                                 g_str_equal(interface, data->interface) == FALSE)
102                         continue;
103
104                 if (member && data->member &&
105                                 g_str_equal(member, data->member) == FALSE)
106                         continue;
107
108                 if (argument && data->argument &&
109                                 g_str_equal(argument, data->argument) == FALSE)
110                         continue;
111
112                 return data;
113         }
114
115         return NULL;
116 }
117
118 static void format_rule(struct filter_data *data, char *rule, size_t size)
119 {
120         const char *sender;
121         int offset;
122
123         offset = snprintf(rule, size, "type='signal'");
124         sender = data->name ? : data->owner;
125
126         if (sender)
127                 offset += snprintf(rule + offset, size - offset,
128                                 ",sender='%s'", sender);
129         if (data->path)
130                 offset += snprintf(rule + offset, size - offset,
131                                 ",path='%s'", data->path);
132         if (data->interface)
133                 offset += snprintf(rule + offset, size - offset,
134                                 ",interface='%s'", data->interface);
135         if (data->member)
136                 offset += snprintf(rule + offset, size - offset,
137                                 ",member='%s'", data->member);
138         if (data->argument)
139                 snprintf(rule + offset, size - offset,
140                                 ",arg0='%s'", data->argument);
141 }
142
143 static gboolean add_match(struct filter_data *data,
144                                 DBusHandleMessageFunction filter)
145 {
146         DBusError err;
147         char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
148
149         format_rule(data, rule, sizeof(rule));
150         dbus_error_init(&err);
151
152         dbus_bus_add_match(data->connection, rule, &err);
153         if (dbus_error_is_set(&err)) {
154                 error("Adding match rule \"%s\" failed: %s", rule,
155                                 err.message);
156                 dbus_error_free(&err);
157                 return FALSE;
158         }
159
160         data->handle_func = filter;
161         data->registered = TRUE;
162
163         return TRUE;
164 }
165
166 static gboolean remove_match(struct filter_data *data)
167 {
168         DBusError err;
169         char rule[DBUS_MAXIMUM_MATCH_RULE_LENGTH];
170
171         format_rule(data, rule, sizeof(rule));
172
173         dbus_error_init(&err);
174
175         dbus_bus_remove_match(data->connection, rule, &err);
176         if (dbus_error_is_set(&err)) {
177                 error("Removing owner match rule for %s failed: %s",
178                                 rule, err.message);
179                 dbus_error_free(&err);
180                 return FALSE;
181         }
182
183         return TRUE;
184 }
185
186 static struct filter_data *filter_data_get(DBusConnection *connection,
187                                         DBusHandleMessageFunction filter,
188                                         const char *sender,
189                                         const char *path,
190                                         const char *interface,
191                                         const char *member,
192                                         const char *argument)
193 {
194         struct filter_data *data;
195         const char *name = NULL, *owner = NULL;
196
197         if (!filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
198                                 NULL)) {
199                 if (!dbus_connection_add_filter(connection,
200                                         message_filter, NULL, NULL)) {
201                         error("dbus_connection_add_filter() failed");
202                         return NULL;
203                 }
204         }
205
206         if (sender == NULL)
207                 goto proceed;
208
209         if (sender[0] == ':')
210                 owner = sender;
211         else
212                 name = sender;
213
214 proceed:
215         data = filter_data_find(connection, name, owner, path, interface,
216                                         member, argument);
217         if (data)
218                 return data;
219
220         data = g_new0(struct filter_data, 1);
221
222         data->connection = dbus_connection_ref(connection);
223         data->name = name ? g_strdup(name) : NULL;
224         data->owner = owner ? g_strdup(owner) : NULL;
225         data->path = g_strdup(path);
226         data->interface = g_strdup(interface);
227         data->member = g_strdup(member);
228         data->argument = g_strdup(argument);
229
230         if (!add_match(data, filter)) {
231                 g_free(data);
232                 return NULL;
233         }
234
235         listeners = g_slist_append(listeners, data);
236
237         return data;
238 }
239
240 static struct filter_callback *filter_data_find_callback(
241                                                 struct filter_data *data,
242                                                 guint id)
243 {
244         GSList *l;
245
246         for (l = data->callbacks; l; l = l->next) {
247                 struct filter_callback *cb = l->data;
248                 if (cb->id == id)
249                         return cb;
250         }
251         for (l = data->processed; l; l = l->next) {
252                 struct filter_callback *cb = l->data;
253                 if (cb->id == id)
254                         return cb;
255         }
256
257         return NULL;
258 }
259
260 static void filter_data_free(struct filter_data *data)
261 {
262         GSList *l;
263
264         for (l = data->callbacks; l != NULL; l = l->next)
265                 g_free(l->data);
266
267         g_slist_free(data->callbacks);
268         g_dbus_remove_watch(data->connection, data->name_watch);
269         g_free(data->name);
270         g_free(data->owner);
271         g_free(data->path);
272         g_free(data->interface);
273         g_free(data->member);
274         g_free(data->argument);
275         dbus_connection_unref(data->connection);
276         g_free(data);
277 }
278
279 static void filter_data_call_and_free(struct filter_data *data)
280 {
281         GSList *l;
282
283         for (l = data->callbacks; l != NULL; l = l->next) {
284                 struct filter_callback *cb = l->data;
285                 if (cb->disc_func)
286                         cb->disc_func(data->connection, cb->user_data);
287                 if (cb->destroy_func)
288                         cb->destroy_func(cb->user_data);
289                 g_free(cb);
290         }
291
292         filter_data_free(data);
293 }
294
295 static struct filter_callback *filter_data_add_callback(
296                                                 struct filter_data *data,
297                                                 GDBusWatchFunction connect,
298                                                 GDBusWatchFunction disconnect,
299                                                 GDBusSignalFunction signal,
300                                                 GDBusDestroyFunction destroy,
301                                                 void *user_data)
302 {
303         struct filter_callback *cb = NULL;
304
305         cb = g_new(struct filter_callback, 1);
306
307         cb->conn_func = connect;
308         cb->disc_func = disconnect;
309         cb->signal_func = signal;
310         cb->destroy_func = destroy;
311         cb->user_data = user_data;
312         cb->id = ++listener_id;
313
314         if (data->lock)
315                 data->processed = g_slist_append(data->processed, cb);
316         else
317                 data->callbacks = g_slist_append(data->callbacks, cb);
318
319         return cb;
320 }
321
322 static gboolean filter_data_remove_callback(struct filter_data *data,
323                                                 struct filter_callback *cb)
324 {
325         DBusConnection *connection;
326
327         data->callbacks = g_slist_remove(data->callbacks, cb);
328         data->processed = g_slist_remove(data->processed, cb);
329
330         if (cb->destroy_func)
331                 cb->destroy_func(cb->user_data);
332
333         g_free(cb);
334
335         /* Don't remove the filter if other callbacks exist or data is lock
336          * processing callbacks */
337         if (data->callbacks || data->lock)
338                 return TRUE;
339
340         if (data->registered && !remove_match(data))
341                 return FALSE;
342
343         connection = dbus_connection_ref(data->connection);
344         listeners = g_slist_remove(listeners, data);
345         filter_data_free(data);
346
347         /* Remove filter if there are no listeners left for the connection */
348         data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
349                                         NULL);
350         if (!data)
351                 dbus_connection_remove_filter(connection, message_filter,
352                                                 NULL);
353
354         dbus_connection_unref(connection);
355
356         return TRUE;
357 }
358
359 static DBusHandlerResult signal_filter(DBusConnection *connection,
360                                         DBusMessage *message, void *user_data)
361 {
362         struct filter_data *data = user_data;
363         struct filter_callback *cb;
364
365         while (data->callbacks) {
366                 cb = data->callbacks->data;
367
368                 if (cb->signal_func && !cb->signal_func(connection, message,
369                                                         cb->user_data)) {
370                         filter_data_remove_callback(data, cb);
371                         continue;
372                 }
373
374                 /* Check if the watch was removed/freed by the callback
375                  * function */
376                 if (!g_slist_find(data->callbacks, cb))
377                         continue;
378
379                 data->callbacks = g_slist_remove(data->callbacks, cb);
380                 data->processed = g_slist_append(data->processed, cb);
381         }
382
383         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
384 }
385
386 static void update_name_cache(const char *name, const char *owner)
387 {
388         GSList *l;
389
390         for (l = listeners; l != NULL; l = l->next) {
391                 struct filter_data *data = l->data;
392
393                 if (g_strcmp0(data->name, name) != 0)
394                         continue;
395
396                 g_free(data->owner);
397                 data->owner = g_strdup(owner);
398         }
399 }
400
401 static const char *check_name_cache(const char *name)
402 {
403         GSList *l;
404
405         for (l = listeners; l != NULL; l = l->next) {
406                 struct filter_data *data = l->data;
407
408                 if (g_strcmp0(data->name, name) != 0)
409                         continue;
410
411                 return data->owner;
412         }
413
414         return NULL;
415 }
416
417 static DBusHandlerResult service_filter(DBusConnection *connection,
418                                         DBusMessage *message, void *user_data)
419 {
420         struct filter_data *data = user_data;
421         struct filter_callback *cb;
422         char *name, *old, *new;
423
424         if (!dbus_message_get_args(message, NULL,
425                                 DBUS_TYPE_STRING, &name,
426                                 DBUS_TYPE_STRING, &old,
427                                 DBUS_TYPE_STRING, &new,
428                                 DBUS_TYPE_INVALID)) {
429                 error("Invalid arguments for NameOwnerChanged signal");
430                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
431         }
432
433         update_name_cache(name, new);
434
435         while (data->callbacks) {
436                 cb = data->callbacks->data;
437
438                 if (*new == '\0') {
439                         if (cb->disc_func)
440                                 cb->disc_func(connection, cb->user_data);
441                 } else {
442                         if (cb->conn_func)
443                                 cb->conn_func(connection, cb->user_data);
444                 }
445
446                 /* Only auto remove if it is a bus name watch */
447                 if (data->argument[0] == ':' &&
448                                 (!cb->conn_func || !cb->disc_func)) {
449                         filter_data_remove_callback(data, cb);
450                         continue;
451                 }
452
453                 /* Check if the watch was removed/freed by the callback
454                  * function */
455                 if (!g_slist_find(data->callbacks, cb))
456                         continue;
457
458                 data->callbacks = g_slist_remove(data->callbacks, cb);
459                 data->processed = g_slist_append(data->processed, cb);
460         }
461
462         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
463 }
464
465
466 static DBusHandlerResult message_filter(DBusConnection *connection,
467                                         DBusMessage *message, void *user_data)
468 {
469         struct filter_data *data;
470         const char *sender, *path, *iface, *member, *arg = NULL;
471
472         /* Only filter signals */
473         if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL)
474                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
475
476         sender = dbus_message_get_sender(message);
477         path = dbus_message_get_path(message);
478         iface = dbus_message_get_interface(message);
479         member = dbus_message_get_member(message);
480         dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
481
482         /* Sender is always bus name */
483         data = filter_data_find(connection, NULL, sender, path, iface, member,
484                                         arg);
485         if (!data) {
486                 error("Got %s.%s signal which has no listeners", iface, member);
487                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
488         }
489
490         if (data->handle_func) {
491                 data->lock = TRUE;
492
493                 data->handle_func(connection, message, data);
494
495                 data->callbacks = data->processed;
496                 data->processed = NULL;
497                 data->lock = FALSE;
498         }
499
500         if (data->callbacks)
501                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
502
503         remove_match(data);
504
505         listeners = g_slist_remove(listeners, data);
506         filter_data_free(data);
507
508         /* Remove filter if there no listener left for the connection */
509         data = filter_data_find(connection, NULL, NULL, NULL, NULL, NULL,
510                                         NULL);
511         if (!data)
512                 dbus_connection_remove_filter(connection, message_filter,
513                                                 NULL);
514
515         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
516 }
517
518 struct service_data {
519         DBusConnection *conn;
520         char *name;
521         const char *owner;
522         GDBusWatchFunction conn_func;
523         void *user_data;
524 };
525
526 static void service_data_free(struct service_data *data)
527 {
528         dbus_connection_unref(data->conn);
529         g_free(data->name);
530         g_free(data);
531 }
532
533 static gboolean update_service(void *user_data)
534 {
535         struct service_data *data = user_data;
536
537         update_name_cache(data->name, data->owner);
538         if (data->conn_func)
539                 data->conn_func(data->conn, data->user_data);
540
541         service_data_free(data);
542
543         return FALSE;
544 }
545
546 static void service_reply(DBusPendingCall *call, void *user_data)
547 {
548         struct service_data *data = user_data;
549         DBusMessage *reply;
550         DBusError err;
551
552         reply = dbus_pending_call_steal_reply(call);
553         if (reply == NULL)
554                 return;
555
556         dbus_error_init(&err);
557
558         if (dbus_set_error_from_message(&err, reply))
559                 goto fail;
560
561         if (dbus_message_get_args(reply, &err,
562                                         DBUS_TYPE_STRING, &data->owner,
563                                                 DBUS_TYPE_INVALID) == FALSE)
564                 goto fail;
565
566         update_service(data);
567
568         goto done;
569
570 fail:
571         error("%s", err.message);
572         dbus_error_free(&err);
573         service_data_free(data);
574 done:
575         dbus_message_unref(reply);
576 }
577
578 static void check_service(DBusConnection *connection, const char *name,
579                                 GDBusWatchFunction connect, void *user_data)
580 {
581         DBusMessage *message;
582         DBusPendingCall *call;
583         struct service_data *data;
584
585         data = g_try_malloc0(sizeof(*data));
586         if (data == NULL) {
587                 error("Can't allocate data structure");
588                 return;
589         }
590
591         data->conn = dbus_connection_ref(connection);
592         data->name = g_strdup(name);
593         data->conn_func = connect;
594         data->user_data = user_data;
595
596         data->owner = check_name_cache(name);
597         if (data->owner != NULL) {
598                 g_idle_add(update_service, data);
599                 return;
600         }
601
602         message = dbus_message_new_method_call(DBUS_SERVICE_DBUS,
603                         DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner");
604         if (message == NULL) {
605                 error("Can't allocate new message");
606                 g_free(data);
607                 return;
608         }
609
610         dbus_message_append_args(message, DBUS_TYPE_STRING, &name,
611                                                         DBUS_TYPE_INVALID);
612
613         if (dbus_connection_send_with_reply(connection, message,
614                                                         &call, -1) == FALSE) {
615                 error("Failed to execute method call");
616                 g_free(data);
617                 goto done;
618         }
619
620         if (call == NULL) {
621                 error("D-Bus connection not available");
622                 g_free(data);
623                 goto done;
624         }
625
626         dbus_pending_call_set_notify(call, service_reply, data, g_free);
627
628         dbus_pending_call_unref(call);
629
630 done:
631         dbus_message_unref(message);
632 }
633
634 guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
635                                 GDBusWatchFunction connect,
636                                 GDBusWatchFunction disconnect,
637                                 void *user_data, GDBusDestroyFunction destroy)
638 {
639         struct filter_data *data;
640         struct filter_callback *cb;
641
642         if (!name)
643                 return 0;
644
645         data = filter_data_get(connection, service_filter, NULL, NULL,
646                                 DBUS_INTERFACE_DBUS, "NameOwnerChanged",
647                                 name);
648         if (!data)
649                 return 0;
650
651         cb = filter_data_add_callback(data, connect, disconnect, NULL, NULL,
652                                         user_data);
653         if (!cb)
654                 return 0;
655
656         if (connect)
657                 check_service(connection, name, connect, user_data);
658
659         return cb->id;
660 }
661
662 guint g_dbus_add_disconnect_watch(DBusConnection *connection, const char *name,
663                                 GDBusWatchFunction func,
664                                 void *user_data, GDBusDestroyFunction destroy)
665 {
666         return g_dbus_add_service_watch(connection, name, NULL, func,
667                                                         user_data, destroy);
668 }
669
670 guint g_dbus_add_signal_watch(DBusConnection *connection,
671                                 const char *sender, const char *path,
672                                 const char *interface, const char *member,
673                                 GDBusSignalFunction function, void *user_data,
674                                 GDBusDestroyFunction destroy)
675 {
676         struct filter_data *data;
677         struct filter_callback *cb;
678
679         data = filter_data_get(connection, signal_filter, sender, path,
680                                 interface, member, NULL);
681         if (!data)
682                 return 0;
683
684         cb = filter_data_add_callback(data, NULL, NULL, function, destroy,
685                                         user_data);
686         if (!cb)
687                 return 0;
688
689         if (data->name != NULL && data->name_watch == 0)
690                 data->name_watch = g_dbus_add_service_watch(connection,
691                                                         data->name, NULL,
692                                                         NULL, NULL, NULL);
693
694         return cb->id;
695 }
696
697 gboolean g_dbus_remove_watch(DBusConnection *connection, guint id)
698 {
699         struct filter_data *data;
700         struct filter_callback *cb;
701         GSList *ldata;
702
703         if (id == 0)
704                 return FALSE;
705
706         for (ldata = listeners; ldata; ldata = ldata->next) {
707                 data = ldata->data;
708
709                 cb = filter_data_find_callback(data, id);
710                 if (cb) {
711                         filter_data_remove_callback(data, cb);
712                         return TRUE;
713                 }
714         }
715
716         return FALSE;
717 }
718
719 void g_dbus_remove_all_watches(DBusConnection *connection)
720 {
721         struct filter_data *data;
722
723         while ((data = filter_data_find(connection, NULL, NULL, NULL, NULL,
724                                         NULL, NULL))) {
725                 listeners = g_slist_remove(listeners, data);
726                 filter_data_call_and_free(data);
727         }
728
729         dbus_connection_remove_filter(connection, message_filter, NULL);
730 }