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