48dabd4695300186c4b2d13541ec3798b5b2e863
[platform/upstream/glib.git] / gio / gdbusproxywatching.c
1 /* GDBus - GLib D-Bus Library
2  *
3  * Copyright (C) 2008-2010 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Author: David Zeuthen <davidz@redhat.com>
21  */
22
23 #include "config.h"
24
25 #include <stdlib.h>
26
27 #include "gdbusutils.h"
28 #include "gdbusconnection.h"
29 #include "gdbusnamewatching.h"
30 #include "gdbusproxywatching.h"
31 #include "gdbuserror.h"
32 #include "gdbusprivate.h"
33 #include "gdbusproxy.h"
34 #include "gcancellable.h"
35
36 #include "glibintl.h"
37 #include "gioalias.h"
38
39 /**
40  * SECTION:gdbusproxywatching
41  * @title: Watching Proxies
42  * @short_description: Simple API for watching proxies
43  * @include: gio/gio.h
44  *
45  * Convenience API for watching bus proxies.
46  *
47  * <example id="gdbus-watching-proxy"><title>Simple application watching a proxy</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gdbus-example-watch-proxy.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
48  */
49
50 /* ---------------------------------------------------------------------------------------------------- */
51
52 G_LOCK_DEFINE_STATIC (lock);
53
54 static guint next_global_id = 1;
55 static GHashTable *map_id_to_client = NULL;
56
57 /* ---------------------------------------------------------------------------------------------------- */
58
59 typedef struct
60 {
61   guint                      id;
62   GBusProxyAppearedCallback  proxy_appeared_handler;
63   GBusProxyVanishedCallback  proxy_vanished_handler;
64   gpointer                   user_data;
65   GDestroyNotify             user_data_free_func;
66   GMainContext              *main_context;
67
68   gchar                     *name;
69   gchar                     *name_owner;
70   GDBusConnection           *connection;
71   guint                      name_watcher_id;
72
73   GCancellable              *cancellable;
74
75   gchar                     *object_path;
76   gchar                     *interface_name;
77   GType                      interface_type;
78   GDBusProxyFlags            proxy_flags;
79   GDBusProxy                *proxy;
80
81   gboolean initial_construction;
82 } Client;
83
84 static void
85 client_unref (Client *client)
86 {
87   /* ensure we're only called from g_bus_unwatch_proxy */
88   g_assert (client->name_watcher_id == 0);
89
90   g_free (client->name_owner);
91   if (client->connection != NULL)
92     g_object_unref (client->connection);
93   if (client->proxy != NULL)
94     g_object_unref (client->proxy);
95
96   g_free (client->name);
97   g_free (client->object_path);
98   g_free (client->interface_name);
99
100   if (client->main_context != NULL)
101     g_main_context_unref (client->main_context);
102
103   if (client->user_data_free_func != NULL)
104     client->user_data_free_func (client->user_data);
105   g_free (client);
106 }
107
108 /* ---------------------------------------------------------------------------------------------------- */
109
110 static void
111 proxy_constructed_cb (GObject       *source_object,
112                       GAsyncResult  *res,
113                       gpointer       user_data)
114 {
115   Client *client = user_data;
116   GDBusProxy *proxy;
117   GError *error;
118
119   error = NULL;
120   proxy = g_dbus_proxy_new_finish (res, &error);
121   if (proxy == NULL)
122     {
123       /* g_warning ("error while constructing proxy: %s", error->message); */
124       g_error_free (error);
125
126       /* handle initial construction, send out vanished if the name
127        * is there but we constructing a proxy fails
128        */
129       if (client->initial_construction)
130         {
131           if (client->proxy_vanished_handler != NULL)
132             {
133               client->proxy_vanished_handler (client->connection,
134                                               client->name,
135                                               client->user_data);
136             }
137           client->initial_construction = FALSE;
138         }
139     }
140   else
141     {
142       g_assert (client->proxy == NULL);
143       g_assert (client->cancellable != NULL);
144       client->proxy = G_DBUS_PROXY (proxy);
145
146       g_object_unref (client->cancellable);
147       client->cancellable = NULL;
148
149       /* perform callback */
150       if (client->proxy_appeared_handler != NULL)
151         {
152           client->proxy_appeared_handler (client->connection,
153                                           client->name,
154                                           client->name_owner,
155                                           client->proxy,
156                                           client->user_data);
157         }
158       client->initial_construction = FALSE;
159     }
160 }
161
162 static void
163 on_name_appeared (GDBusConnection *connection,
164                   const gchar *name,
165                   const gchar *name_owner,
166                   gpointer user_data)
167 {
168   Client *client = user_data;
169
170   //g_debug ("\n\nname appeared (owner `%s')", name_owner);
171
172   /* invariants */
173   g_assert (client->name_owner == NULL);
174   g_assert (client->connection == NULL);
175   g_assert (client->cancellable == NULL);
176
177   client->name_owner = g_strdup (name_owner);
178   client->connection = g_object_ref (connection);
179   client->cancellable = g_cancellable_new ();
180
181   g_dbus_proxy_new (client->connection,
182                     client->interface_type,
183                     client->proxy_flags,
184                     NULL, /* GDBusInterfaceInfo */
185                     client->name_owner,
186                     client->object_path,
187                     client->interface_name,
188                     client->cancellable,
189                     proxy_constructed_cb,
190                     client);
191 }
192
193 static void
194 on_name_vanished (GDBusConnection *connection,
195                   const gchar *name,
196                   gpointer user_data)
197 {
198   Client *client = user_data;
199
200   /*g_debug ("\n\nname vanished");*/
201
202   g_free (client->name_owner);
203   if (client->connection != NULL)
204     g_object_unref (client->connection);
205   client->name_owner = NULL;
206   client->connection = NULL;
207
208   /* free the proxy if we have it */
209   if (client->proxy != NULL)
210     {
211       g_assert (client->cancellable == NULL);
212
213       g_object_unref (client->proxy);
214       client->proxy = NULL;
215
216       /* if we have the proxy, it means we last sent out a 'appeared'
217        * callback - so send out a 'vanished' callback
218        */
219       if (client->proxy_vanished_handler != NULL)
220         {
221           client->proxy_vanished_handler (client->connection,
222                                           client->name,
223                                           client->user_data);
224         }
225       client->initial_construction = FALSE;
226     }
227   else
228     {
229       /* otherwise cancel construction of the proxy if applicable */
230       if (client->cancellable != NULL)
231         {
232           g_cancellable_cancel (client->cancellable);
233           g_object_unref (client->cancellable);
234           client->cancellable = NULL;
235         }
236       else
237         {
238           /* handle initial construction, send out vanished if
239            * the name isn't there
240            */
241           if (client->initial_construction)
242             {
243               if (client->proxy_vanished_handler != NULL)
244                 {
245                   client->proxy_vanished_handler (client->connection,
246                                                   client->name,
247                                                   client->user_data);
248                 }
249               client->initial_construction = FALSE;
250             }
251         }
252     }
253 }
254
255 /**
256  * g_bus_watch_proxy:
257  * @bus_type: The type of bus to watch a name on.
258  * @name: The name (well-known or unique) to watch.
259  * @flags: Flags from the #GBusNameWatcherFlags enumeration.
260  * @object_path: The object path of the remote object to watch.
261  * @interface_name: The D-Bus interface name for the proxy.
262  * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type.
263  * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy.
264  * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the
265  * requested proxy is available.
266  * @proxy_vanished_handler: Handler to invoke when @name is known to not exist
267  * and the previously created proxy is no longer available.
268  * @user_data: User data to pass to handlers.
269  * @user_data_free_func: Function for freeing @user_data or %NULL.
270  *
271  * Starts watching a remote object at @object_path owned by @name on
272  * the bus specified by @bus_type. When the object is available, a
273  * #GDBusProxy (or derived class cf. @interface_type) instance is
274  * constructed for the @interface_name D-Bus interface and then
275  * @proxy_appeared_handler will be called when the proxy is ready and
276  * all properties have been loaded. When @name vanishes,
277  * @proxy_vanished_handler is called.
278  *
279  * This function makes it very simple to write applications that wants
280  * to watch a well-known remote object on a well-known name, see <xref
281  * linkend="gdbus-watching-proxy"/>. Basically, the application simply
282  * starts using the proxy when @proxy_appeared_handler is called and
283  * stops using it when @proxy_vanished_handler is called. Callbacks
284  * will be invoked in the <link
285  * linkend="g-main-context-push-thread-default">thread-default main
286  * loop</link> of the thread you are calling this function from.
287  *
288  * Applications typically use this function to watch the
289  * <quote>manager</quote> object of a well-known name. Upon acquiring
290  * a proxy for the manager object, applications typically construct
291  * additional proxies in response to the result of enumeration methods
292  * on the manager object.
293  *
294  * Many of the comments that apply to g_bus_watch_name() also apply
295  * here. For example, you are guaranteed that one of the handlers will
296  * be invoked (on the main thread) after calling this function and
297  * also that the two handlers alternate. When you are done watching the
298  * proxy, just call g_bus_unwatch_proxy().
299  *
300  * Returns: An identifier (never 0) that can be used with
301  * g_bus_unwatch_proxy() to stop watching the remote object.
302  *
303  * Since: 2.26
304  */
305 guint
306 g_bus_watch_proxy (GBusType                   bus_type,
307                    const gchar               *name,
308                    GBusNameWatcherFlags       flags,
309                    const gchar               *object_path,
310                    const gchar               *interface_name,
311                    GType                      interface_type,
312                    GDBusProxyFlags            proxy_flags,
313                    GBusProxyAppearedCallback  proxy_appeared_handler,
314                    GBusProxyVanishedCallback  proxy_vanished_handler,
315                    gpointer                   user_data,
316                    GDestroyNotify             user_data_free_func)
317 {
318   Client *client;
319
320   g_return_val_if_fail (g_dbus_is_name (name), 0);
321   g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
322   g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0);
323   g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0);
324
325   G_LOCK (lock);
326
327   client = g_new0 (Client, 1);
328   client->id = next_global_id++; /* TODO: uh oh, handle overflow */
329   client->name = g_strdup (name);
330   client->proxy_appeared_handler = proxy_appeared_handler;
331   client->proxy_vanished_handler = proxy_vanished_handler;
332   client->user_data = user_data;
333   client->user_data_free_func = user_data_free_func;
334   client->main_context = g_main_context_get_thread_default ();
335   if (client->main_context != NULL)
336     g_main_context_ref (client->main_context);
337   client->name_watcher_id = g_bus_watch_name (bus_type,
338                                               name,
339                                               flags,
340                                               on_name_appeared,
341                                               on_name_vanished,
342                                               client,
343                                               NULL);
344
345   client->object_path = g_strdup (object_path);
346   client->interface_name = g_strdup (interface_name);
347   client->interface_type = interface_type;
348   client->proxy_flags = proxy_flags;
349   client->initial_construction = TRUE;
350
351   if (map_id_to_client == NULL)
352     {
353       map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
354     }
355   g_hash_table_insert (map_id_to_client,
356                        GUINT_TO_POINTER (client->id),
357                        client);
358
359   G_UNLOCK (lock);
360
361   return client->id;
362 }
363
364 /**
365  * g_bus_watch_proxy_on_connection:
366  * @connection: A #GDBusConnection that is not closed.
367  * @name: The name (well-known or unique) to watch.
368  * @flags: Flags from the #GBusNameWatcherFlags enumeration.
369  * @object_path: The object path of the remote object to watch.
370  * @interface_name: The D-Bus interface name for the proxy.
371  * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type.
372  * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy.
373  * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the
374  * requested proxy is available.
375  * @proxy_vanished_handler: Handler to invoke when @name is known to not exist
376  * and the previously created proxy is no longer available.
377  * @user_data: User data to pass to handlers.
378  * @user_data_free_func: Function for freeing @user_data or %NULL.
379  *
380  * Like g_bus_watch_proxy() but takes a #GDBusConnection instead of a
381  * #GBusType.
382  *
383  * Returns: An identifier (never 0) that can be used with
384  * g_bus_unwatch_proxy() to stop watching the remote object.
385  *
386  * Since: 2.26
387  */
388 guint
389 g_bus_watch_proxy_on_connection (GDBusConnection           *connection,
390                                  const gchar               *name,
391                                  GBusNameWatcherFlags       flags,
392                                  const gchar               *object_path,
393                                  const gchar               *interface_name,
394                                  GType                      interface_type,
395                                  GDBusProxyFlags            proxy_flags,
396                                  GBusProxyAppearedCallback  proxy_appeared_handler,
397                                  GBusProxyVanishedCallback  proxy_vanished_handler,
398                                  gpointer                   user_data,
399                                  GDestroyNotify             user_data_free_func)
400 {
401   Client *client;
402
403   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
404   g_return_val_if_fail (g_dbus_is_name (name), 0);
405   g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
406   g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0);
407   g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0);
408
409   G_LOCK (lock);
410
411   client = g_new0 (Client, 1);
412   client->id = next_global_id++; /* TODO: uh oh, handle overflow */
413   client->name = g_strdup (name);
414   client->proxy_appeared_handler = proxy_appeared_handler;
415   client->proxy_vanished_handler = proxy_vanished_handler;
416   client->user_data = user_data;
417   client->user_data_free_func = user_data_free_func;
418   client->main_context = g_main_context_get_thread_default ();
419   if (client->main_context != NULL)
420     g_main_context_ref (client->main_context);
421   client->name_watcher_id = g_bus_watch_name_on_connection (connection,
422                                                             name,
423                                                             flags,
424                                                             on_name_appeared,
425                                                             on_name_vanished,
426                                                             client,
427                                                             NULL);
428
429   client->object_path = g_strdup (object_path);
430   client->interface_name = g_strdup (interface_name);
431   client->interface_type = interface_type;
432   client->proxy_flags = proxy_flags;
433   client->initial_construction = TRUE;
434
435   if (map_id_to_client == NULL)
436     {
437       map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
438     }
439   g_hash_table_insert (map_id_to_client,
440                        GUINT_TO_POINTER (client->id),
441                        client);
442
443   G_UNLOCK (lock);
444
445   return client->id;
446 }
447
448 typedef struct {
449   GClosure *proxy_appeared_closure;
450   GClosure *proxy_vanished_closure;
451 } WatchProxyData;
452
453 static void
454 watch_with_closures_on_proxy_appeared (GDBusConnection *connection,
455                                        const gchar     *name,
456                                        const gchar     *name_owner,
457                                        GDBusProxy      *proxy,
458                                        gpointer         user_data)
459 {
460   WatchProxyData *data = user_data;
461   GValue params[4] = { { 0, }, { 0, }, { 0, }, { 0, } };
462
463   g_value_init (&params[0], G_TYPE_DBUS_CONNECTION);
464   g_value_set_object (&params[0], connection);
465
466   g_value_init (&params[1], G_TYPE_STRING);
467   g_value_set_string (&params[1], name);
468
469   g_value_init (&params[2], G_TYPE_STRING);
470   g_value_set_string (&params[2], name_owner);
471
472   g_value_init (&params[3], G_TYPE_DBUS_PROXY);
473   g_value_set_object (&params[3], proxy);
474
475   g_closure_invoke (data->proxy_appeared_closure, NULL, 4, params, NULL);
476 }
477
478 static void
479 watch_with_closures_on_proxy_vanished (GDBusConnection *connection,
480                                        const gchar     *name,
481                                        gpointer         user_data)
482 {
483   WatchProxyData *data = user_data;
484   GValue params[2] = { { 0, }, { 0, } };
485
486   g_value_init (&params[0], G_TYPE_DBUS_CONNECTION);
487   g_value_set_object (&params[0], connection);
488
489   g_value_init (&params[1], G_TYPE_STRING);
490   g_value_set_string (&params[1], name);
491
492   g_closure_invoke (data->proxy_vanished_closure, NULL, 2, params, NULL);
493 }
494
495 static void
496 bus_watch_proxy_free_func (gpointer user_data)
497 {
498   WatchProxyData *data = user_data;
499
500   if (data->proxy_appeared_closure != NULL)
501     g_closure_unref (data->proxy_appeared_closure);
502
503   if (data->proxy_vanished_closure != NULL)
504     g_closure_unref (data->proxy_vanished_closure);
505
506   g_free (data);
507 }
508
509 /**
510  * g_bus_watch_proxy_with_closures:
511  * @bus_type: The type of bus to watch a name on.
512  * @name: The name (well-known or unique) to watch.
513  * @flags: Flags from the #GBusNameWatcherFlags enumeration.
514  * @object_path: The object path of the remote object to watch.
515  * @interface_name: The D-Bus interface name for the proxy.
516  * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type.
517  * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy.
518  * @proxy_appeared_closure: (allow-none): #GClosure to invoke when @name is
519  * known to exist and the requested proxy is available.
520  * @proxy_vanished_closure: (allow-none): #GClosure to invoke when @name is
521  * known to not exist and the previously created proxy is no longer available.
522  *
523  * Version of g_bus_watch_proxy() using closures instead of callbacks for
524  * easier binding in other languages.
525  *
526  * Returns: An identifier (never 0) that can be used with
527  * g_bus_unwatch_proxy() to stop watching the remote object.
528  *
529  * Rename to: g_bus_watch_proxy
530  *
531  * Since: 2.26
532  */
533 guint
534 g_bus_watch_proxy_with_closures (GBusType                   bus_type,
535                                  const gchar               *name,
536                                  GBusNameWatcherFlags       flags,
537                                  const gchar               *object_path,
538                                  const gchar               *interface_name,
539                                  GType                      interface_type,
540                                  GDBusProxyFlags            proxy_flags,
541                                  GClosure                  *proxy_appeared_closure,
542                                  GClosure                  *proxy_vanished_closure)
543 {
544   WatchProxyData *data;
545
546   data = g_new0 (WatchProxyData, 1);
547
548   if (proxy_appeared_closure != NULL)
549     data->proxy_appeared_closure = g_closure_ref (proxy_appeared_closure);
550
551   if (proxy_vanished_closure != NULL)
552     data->proxy_vanished_closure = g_closure_ref (proxy_vanished_closure);
553
554   return g_bus_watch_proxy (bus_type,
555           name,
556           flags,
557           object_path,
558           interface_name,
559           interface_type,
560           proxy_flags,
561           proxy_appeared_closure != NULL ? watch_with_closures_on_proxy_appeared : NULL,
562           proxy_vanished_closure != NULL ? watch_with_closures_on_proxy_vanished : NULL,
563           data,
564           bus_watch_proxy_free_func);
565 }
566
567 /**
568  * g_bus_watch_proxy_on_connection_with_closures:
569  * @connection: A #GDBusConnection that is not closed.
570  * @name: The name (well-known or unique) to watch.
571  * @flags: Flags from the #GBusNameWatcherFlags enumeration.
572  * @object_path: The object path of the remote object to watch.
573  * @interface_name: The D-Bus interface name for the proxy.
574  * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type.
575  * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy.
576  * @proxy_appeared_closure: (allow-none): #GClosure to invoke when @name is
577  * known to exist and the requested proxy is available.
578  * @proxy_vanished_closure: (allow-none): #GClosure to invoke when @name is
579  * known to not exist and the previously created proxy is no longer available.
580  *
581  * Version of g_bus_watch_proxy_on_connection() using closures instead of
582  * callbacks for easier binding in other languages.
583  *
584  * Returns: An identifier (never 0) that can be used with
585  * g_bus_unwatch_proxy() to stop watching the remote object.
586  *
587  * Rename to: g_bus_watch_proxy_on_connection
588  *
589  * Since: 2.26
590  */
591 guint
592 g_bus_watch_proxy_on_connection_with_closures (
593                                  GDBusConnection           *connection,
594                                  const gchar               *name,
595                                  GBusNameWatcherFlags       flags,
596                                  const gchar               *object_path,
597                                  const gchar               *interface_name,
598                                  GType                      interface_type,
599                                  GDBusProxyFlags            proxy_flags,
600                                  GClosure                  *proxy_appeared_closure,
601                                  GClosure                  *proxy_vanished_closure)
602 {
603   WatchProxyData *data;
604
605   data = g_new0 (WatchProxyData, 1);
606
607   if (proxy_appeared_closure != NULL)
608     data->proxy_appeared_closure = g_closure_ref (proxy_appeared_closure);
609
610   if (proxy_vanished_closure != NULL)
611     data->proxy_vanished_closure = g_closure_ref (proxy_vanished_closure);
612
613   return g_bus_watch_proxy_on_connection (connection,
614           name,
615           flags,
616           object_path,
617           interface_name,
618           interface_type,
619           proxy_flags,
620           proxy_appeared_closure != NULL ? watch_with_closures_on_proxy_appeared : NULL,
621           proxy_vanished_closure != NULL ? watch_with_closures_on_proxy_vanished : NULL,
622           data,
623           bus_watch_proxy_free_func);
624 }
625
626 /**
627  * g_bus_unwatch_proxy:
628  * @watcher_id: An identifier obtained from g_bus_watch_proxy()
629  *
630  * Stops watching proxy.
631  *
632  * Since: 2.26
633  */
634 void
635 g_bus_unwatch_proxy (guint watcher_id)
636 {
637   Client *client;
638
639   g_return_if_fail (watcher_id > 0);
640
641   client = NULL;
642
643   G_LOCK (lock);
644   if (watcher_id == 0 ||
645       map_id_to_client == NULL ||
646       (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL)
647     {
648       g_warning ("Invalid id %d passed to g_bus_unwatch_proxy()", watcher_id);
649       goto out;
650     }
651
652   g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id)));
653
654  out:
655   G_UNLOCK (lock);
656
657   if (client != NULL)
658     {
659       g_bus_unwatch_name (client->name_watcher_id);
660       client->name_watcher_id = 0;
661       client_unref (client);
662     }
663 }
664
665 #define __G_DBUS_PROXY_WATCHING_C__
666 #include "gioaliasdef.c"