17ab078a82b11e84cb288cd35fcda86a9b04e66d
[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
449 /**
450  * g_bus_unwatch_proxy:
451  * @watcher_id: An identifier obtained from g_bus_watch_proxy()
452  *
453  * Stops watching proxy.
454  *
455  * Since: 2.26
456  */
457 void
458 g_bus_unwatch_proxy (guint watcher_id)
459 {
460   Client *client;
461
462   g_return_if_fail (watcher_id > 0);
463
464   client = NULL;
465
466   G_LOCK (lock);
467   if (watcher_id == 0 ||
468       map_id_to_client == NULL ||
469       (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL)
470     {
471       g_warning ("Invalid id %d passed to g_bus_unwatch_proxy()", watcher_id);
472       goto out;
473     }
474
475   g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id)));
476
477  out:
478   G_UNLOCK (lock);
479
480   if (client != NULL)
481     {
482       g_bus_unwatch_name (client->name_watcher_id);
483       client->name_watcher_id = 0;
484       client_unref (client);
485     }
486 }
487
488 #define __G_DBUS_PROXY_WATCHING_C__
489 #include "gioaliasdef.c"