282254d467d648980c77a86c733184f758072162
[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 "gdbusnamewatching.h"
35 #include "gcancellable.h"
36
37 #include "glibintl.h"
38 #include "gioalias.h"
39
40 /**
41  * SECTION:gdbusproxywatching
42  * @title: Watching Proxies
43  * @short_description: Simple API for watching proxies
44  * @include: gio/gio.h
45  *
46  * Convenience API for watching bus proxies.
47  *
48  * <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>
49  */
50
51 /* ---------------------------------------------------------------------------------------------------- */
52
53 G_LOCK_DEFINE_STATIC (lock);
54
55 static guint next_global_id = 1;
56 static GHashTable *map_id_to_client = NULL;
57
58 /* ---------------------------------------------------------------------------------------------------- */
59
60 typedef struct
61 {
62   guint                      id;
63   GBusProxyAppearedCallback  proxy_appeared_handler;
64   GBusProxyVanishedCallback  proxy_vanished_handler;
65   gpointer                   user_data;
66   GDestroyNotify             user_data_free_func;
67   GMainContext              *main_context;
68
69   gchar                     *name;
70   gchar                     *name_owner;
71   GDBusConnection           *connection;
72   guint                      name_watcher_id;
73
74   GCancellable              *cancellable;
75
76   gchar                     *object_path;
77   gchar                     *interface_name;
78   GType                      interface_type;
79   GDBusProxyFlags            proxy_flags;
80   GDBusProxy                *proxy;
81
82   gboolean initial_construction;
83 } Client;
84
85 static void
86 client_unref (Client *client)
87 {
88   /* ensure we're only called from g_bus_unwatch_proxy */
89   g_assert (client->name_watcher_id == 0);
90
91   g_free (client->name_owner);
92   if (client->connection != NULL)
93     g_object_unref (client->connection);
94   if (client->proxy != NULL)
95     g_object_unref (client->proxy);
96
97   g_free (client->name);
98   g_free (client->object_path);
99   g_free (client->interface_name);
100
101   if (client->main_context != NULL)
102     g_main_context_unref (client->main_context);
103
104   if (client->user_data_free_func != NULL)
105     client->user_data_free_func (client->user_data);
106   g_free (client);
107 }
108
109 /* ---------------------------------------------------------------------------------------------------- */
110
111 static void
112 proxy_constructed_cb (GObject       *source_object,
113                       GAsyncResult  *res,
114                       gpointer       user_data)
115 {
116   Client *client = user_data;
117   GDBusProxy *proxy;
118   GError *error;
119
120   error = NULL;
121   proxy = g_dbus_proxy_new_finish (res, &error);
122   if (proxy == NULL)
123     {
124       /* g_warning ("error while constructing proxy: %s", error->message); */
125       g_error_free (error);
126
127       /* handle initial construction, send out vanished if the name
128        * is there but we constructing a proxy fails
129        */
130       if (client->initial_construction)
131         {
132           if (client->proxy_vanished_handler != NULL)
133             {
134               client->proxy_vanished_handler (client->connection,
135                                               client->name,
136                                               client->user_data);
137             }
138           client->initial_construction = FALSE;
139         }
140     }
141   else
142     {
143       g_assert (client->proxy == NULL);
144       g_assert (client->cancellable != NULL);
145       client->proxy = G_DBUS_PROXY (proxy);
146
147       g_object_unref (client->cancellable);
148       client->cancellable = NULL;
149
150       /* perform callback */
151       if (client->proxy_appeared_handler != NULL)
152         {
153           client->proxy_appeared_handler (client->connection,
154                                           client->name,
155                                           client->name_owner,
156                                           client->proxy,
157                                           client->user_data);
158         }
159       client->initial_construction = FALSE;
160     }
161 }
162
163 static void
164 on_name_appeared (GDBusConnection *connection,
165                   const gchar *name,
166                   const gchar *name_owner,
167                   gpointer user_data)
168 {
169   Client *client = user_data;
170
171   //g_debug ("\n\nname appeared (owner `%s')", name_owner);
172
173   /* invariants */
174   g_assert (client->name_owner == NULL);
175   g_assert (client->connection == NULL);
176   g_assert (client->cancellable == NULL);
177
178   client->name_owner = g_strdup (name_owner);
179   client->connection = g_object_ref (connection);
180   client->cancellable = g_cancellable_new ();
181
182   g_dbus_proxy_new (client->connection,
183                     client->interface_type,
184                     client->proxy_flags,
185                     NULL, /* GDBusInterfaceInfo */
186                     client->name_owner,
187                     client->object_path,
188                     client->interface_name,
189                     client->cancellable,
190                     proxy_constructed_cb,
191                     client);
192 }
193
194 static void
195 on_name_vanished (GDBusConnection *connection,
196                   const gchar *name,
197                   gpointer user_data)
198 {
199   Client *client = user_data;
200
201   /*g_debug ("\n\nname vanished");*/
202
203   g_free (client->name_owner);
204   if (client->connection != NULL)
205     g_object_unref (client->connection);
206   client->name_owner = NULL;
207   client->connection = NULL;
208
209   /* free the proxy if we have it */
210   if (client->proxy != NULL)
211     {
212       g_assert (client->cancellable == NULL);
213
214       g_object_unref (client->proxy);
215       client->proxy = NULL;
216
217       /* if we have the proxy, it means we last sent out a 'appeared'
218        * callback - so send out a 'vanished' callback
219        */
220       if (client->proxy_vanished_handler != NULL)
221         {
222           client->proxy_vanished_handler (client->connection,
223                                           client->name,
224                                           client->user_data);
225         }
226       client->initial_construction = FALSE;
227     }
228   else
229     {
230       /* otherwise cancel construction of the proxy if applicable */
231       if (client->cancellable != NULL)
232         {
233           g_cancellable_cancel (client->cancellable);
234           g_object_unref (client->cancellable);
235           client->cancellable = NULL;
236         }
237       else
238         {
239           /* handle initial construction, send out vanished if
240            * the name isn't there
241            */
242           if (client->initial_construction)
243             {
244               if (client->proxy_vanished_handler != NULL)
245                 {
246                   client->proxy_vanished_handler (client->connection,
247                                                   client->name,
248                                                   client->user_data);
249                 }
250               client->initial_construction = FALSE;
251             }
252         }
253     }
254 }
255
256 /**
257  * g_bus_watch_proxy:
258  * @bus_type: The type of bus to watch a name on (can't be #G_BUS_TYPE_NONE).
259  * @name: The name (well-known or unique) to watch.
260  * @flags: Flags from the #GBusNameWatcherFlags enumeration.
261  * @object_path: The object path of the remote object to watch.
262  * @interface_name: The D-Bus interface name for the proxy.
263  * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type.
264  * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy.
265  * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the
266  * requested proxy is available.
267  * @proxy_vanished_handler: Handler to invoke when @name is known to not exist
268  * and the previously created proxy is no longer available.
269  * @user_data: User data to pass to handlers.
270  * @user_data_free_func: Function for freeing @user_data or %NULL.
271  *
272  * Starts watching a remote object at @object_path owned by @name on
273  * the bus specified by @bus_type. When the object is available, a
274  * #GDBusProxy (or derived class cf. @interface_type) instance is
275  * constructed for the @interface_name D-Bus interface and then
276  * @proxy_appeared_handler will be called when the proxy is ready and
277  * all properties have been loaded. When @name vanishes,
278  * @proxy_vanished_handler is called.
279  *
280  * This function makes it very simple to write applications that wants
281  * to watch a well-known remote object on a well-known name, see <xref
282  * linkend="gdbus-watching-proxy"/>. Basically, the application simply
283  * starts using the proxy when @proxy_appeared_handler is called and
284  * stops using it when @proxy_vanished_handler is called. Callbacks
285  * will be invoked in the <link
286  * linkend="g-main-context-push-thread-default">thread-default main
287  * loop</link> of the thread you are calling this function from.
288  *
289  * Applications typically use this function to watch the
290  * <quote>manager</quote> object of a well-known name. Upon acquiring
291  * a proxy for the manager object, applications typically construct
292  * additional proxies in response to the result of enumeration methods
293  * on the manager object.
294  *
295  * Many of the comments that apply to g_bus_watch_name() also apply
296  * here. For example, you are guaranteed that one of the handlers will
297  * be invoked (on the main thread) after calling this function and
298  * also that the two handlers alternate. When you are done watching the
299  * proxy, just call g_bus_unwatch_proxy().
300  *
301  * Returns: An identifier (never 0) that can be used with
302  * g_bus_unwatch_proxy() to stop watching the remote object.
303  *
304  * Since: 2.26
305  */
306 guint
307 g_bus_watch_proxy (GBusType                   bus_type,
308                    const gchar               *name,
309                    GBusNameWatcherFlags       flags,
310                    const gchar               *object_path,
311                    const gchar               *interface_name,
312                    GType                      interface_type,
313                    GDBusProxyFlags            proxy_flags,
314                    GBusProxyAppearedCallback  proxy_appeared_handler,
315                    GBusProxyVanishedCallback  proxy_vanished_handler,
316                    gpointer                   user_data,
317                    GDestroyNotify             user_data_free_func)
318 {
319   Client *client;
320
321   g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0);
322   g_return_val_if_fail (g_dbus_is_name (name), 0);
323   g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
324   g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0);
325   g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0);
326
327   G_LOCK (lock);
328
329   client = g_new0 (Client, 1);
330   client->id = next_global_id++; /* TODO: uh oh, handle overflow */
331   client->name = g_strdup (name);
332   client->proxy_appeared_handler = proxy_appeared_handler;
333   client->proxy_vanished_handler = proxy_vanished_handler;
334   client->user_data = user_data;
335   client->user_data_free_func = user_data_free_func;
336   client->main_context = g_main_context_get_thread_default ();
337   if (client->main_context != NULL)
338     g_main_context_ref (client->main_context);
339   client->name_watcher_id = g_bus_watch_name (bus_type,
340                                               name,
341                                               flags,
342                                               on_name_appeared,
343                                               on_name_vanished,
344                                               client,
345                                               NULL);
346
347   client->object_path = g_strdup (object_path);
348   client->interface_name = g_strdup (interface_name);
349   client->interface_type = interface_type;
350   client->proxy_flags = proxy_flags;
351   client->initial_construction = TRUE;
352
353   if (map_id_to_client == NULL)
354     {
355       map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
356     }
357   g_hash_table_insert (map_id_to_client,
358                        GUINT_TO_POINTER (client->id),
359                        client);
360
361   G_UNLOCK (lock);
362
363   return client->id;
364 }
365
366 /**
367  * g_bus_watch_proxy_on_connection:
368  * @connection: A #GDBusConnection that is not closed.
369  * @name: The name (well-known or unique) to watch.
370  * @flags: Flags from the #GBusNameWatcherFlags enumeration.
371  * @object_path: The object path of the remote object to watch.
372  * @interface_name: The D-Bus interface name for the proxy.
373  * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type.
374  * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy.
375  * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the
376  * requested proxy is available.
377  * @proxy_vanished_handler: Handler to invoke when @name is known to not exist
378  * and the previously created proxy is no longer available.
379  * @user_data: User data to pass to handlers.
380  * @user_data_free_func: Function for freeing @user_data or %NULL.
381  *
382  * Like g_bus_watch_proxy() but takes a #GDBusConnection instead of a
383  * #GBusType.
384  *
385  * Returns: An identifier (never 0) that can be used with
386  * g_bus_unwatch_proxy() to stop watching the remote object.
387  *
388  * Since: 2.26
389  */
390 guint
391 g_bus_watch_proxy_on_connection (GDBusConnection           *connection,
392                                  const gchar               *name,
393                                  GBusNameWatcherFlags       flags,
394                                  const gchar               *object_path,
395                                  const gchar               *interface_name,
396                                  GType                      interface_type,
397                                  GDBusProxyFlags            proxy_flags,
398                                  GBusProxyAppearedCallback  proxy_appeared_handler,
399                                  GBusProxyVanishedCallback  proxy_vanished_handler,
400                                  gpointer                   user_data,
401                                  GDestroyNotify             user_data_free_func)
402 {
403   Client *client;
404
405   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0);
406   g_return_val_if_fail (g_dbus_is_name (name), 0);
407   g_return_val_if_fail (g_variant_is_object_path (object_path), 0);
408   g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0);
409   g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0);
410
411   G_LOCK (lock);
412
413   client = g_new0 (Client, 1);
414   client->id = next_global_id++; /* TODO: uh oh, handle overflow */
415   client->name = g_strdup (name);
416   client->proxy_appeared_handler = proxy_appeared_handler;
417   client->proxy_vanished_handler = proxy_vanished_handler;
418   client->user_data = user_data;
419   client->user_data_free_func = user_data_free_func;
420   client->main_context = g_main_context_get_thread_default ();
421   if (client->main_context != NULL)
422     g_main_context_ref (client->main_context);
423   client->name_watcher_id = g_bus_watch_name_on_connection (connection,
424                                                             name,
425                                                             flags,
426                                                             on_name_appeared,
427                                                             on_name_vanished,
428                                                             client,
429                                                             NULL);
430
431   client->object_path = g_strdup (object_path);
432   client->interface_name = g_strdup (interface_name);
433   client->interface_type = interface_type;
434   client->proxy_flags = proxy_flags;
435   client->initial_construction = TRUE;
436
437   if (map_id_to_client == NULL)
438     {
439       map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal);
440     }
441   g_hash_table_insert (map_id_to_client,
442                        GUINT_TO_POINTER (client->id),
443                        client);
444
445   G_UNLOCK (lock);
446
447   return client->id;
448 }
449
450
451 /**
452  * g_bus_unwatch_proxy:
453  * @watcher_id: An identifier obtained from g_bus_watch_proxy()
454  *
455  * Stops watching proxy.
456  *
457  * Since: 2.26
458  */
459 void
460 g_bus_unwatch_proxy (guint watcher_id)
461 {
462   Client *client;
463
464   g_return_if_fail (watcher_id > 0);
465
466   client = NULL;
467
468   G_LOCK (lock);
469   if (watcher_id == 0 ||
470       map_id_to_client == NULL ||
471       (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL)
472     {
473       g_warning ("Invalid id %d passed to g_bus_unwatch_proxy()", watcher_id);
474       goto out;
475     }
476
477   g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id)));
478
479  out:
480   G_UNLOCK (lock);
481
482   if (client != NULL)
483     {
484       g_bus_unwatch_name (client->name_watcher_id);
485       client->name_watcher_id = 0;
486       client_unref (client);
487     }
488 }
489
490 #define __G_DBUS_PROXY_WATCHING_C__
491 #include "gioaliasdef.c"