2003-09-22 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / glib / dbus-gproxy.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-gcall.c convenience routines for calling methods, etc.
3  *
4  * Copyright (C) 2003  Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "dbus-glib.h"
24
25 /**
26  * @addtogroup DBusGLibInternals
27  *
28  * @{
29  */
30
31 /**
32  * DBusGProxyManager's primary task is to route signals to the proxies
33  * those signals are emitted on. In order to do this it also has to
34  * track the owners of the services proxies are bound to.
35  */
36 typedef struct
37 {
38   GStaticMutex lock; /**< Thread lock */
39   int refcount;      /**< Reference count */
40   DBusConnection *connection; /**< Connection we're associated with. */
41   
42
43 } DBusGProxyManager;
44
45 /** Lock the DBusGProxyManager */
46 #define LOCK_MANAGER(mgr)   (g_static_mutex_lock (&(mgr)->lock))
47 /** Unlock the DBusGProxyManager */
48 #define UNLOCK_MANAGER(mgr) (g_static_mutex_unlock (&(mgr)->lock))
49
50 static int gproxy_manager_slot = -1;
51
52 /* Lock controlling get/set manager as data on each connection */
53 static GStaticMutex connection_gproxy_lock = G_STATIC_MUTEX_INIT;
54
55 static void dbus_gproxy_manager_ref (DBusGProxyManager *manager);
56
57 static DBusGProxyManager*
58 dbus_gproxy_manager_get (DBusConnection *connection)
59 {
60   DBusGProxyManager *manager;
61
62   dbus_connection_allocate_data_slot (&gproxy_manager_slot);
63   if (gproxy_manager_slot < 0)
64     g_error ("out of memory");
65   
66   g_static_mutex_lock (&connection_gproxy_lock);
67   
68   manager = dbus_connection_get_data (connection, gproxy_manager_slot);
69   if (manager != NULL)
70     {
71       dbus_connection_free_data_slot (&gproxy_manager_slot);
72       dbus_gproxy_manager_ref (manager);
73       g_static_mutex_unlock (&connection_gproxy_lock);
74       return manager;
75     }
76   
77   manager = g_new0 (DBusGProxyManager, 1);
78
79   manager->refcount = 1;
80   manager->connection = connection;
81   
82   g_static_mutex_init (&manager->lock);
83
84   /* Proxy managers keep the connection alive, which means that
85    * DBusGProxy indirectly does. To free a connection you have to free
86    * all the proxies referring to it.
87    */
88   dbus_connection_ref (manager->connection);
89
90   dbus_connection_set_data (connection, gproxy_manager_slot,
91                             manager, NULL);
92
93   g_static_mutex_unlock (&connection_gproxy_lock);
94   
95   return manager;
96 }
97
98 static void
99 dbus_gproxy_manager_ref (DBusGProxyManager *manager)
100 {
101   g_assert (manager != NULL);
102   g_assert (manager->refcount > 0);
103
104   LOCK_MANAGER (manager);
105   
106   manager->refcount += 1;
107
108   UNLOCK_MANAGER (manager);
109 }
110
111 static void
112 dbus_gproxy_manager_unref (DBusGProxyManager *manager)
113 {
114   g_assert (manager != NULL);
115   g_assert (manager->refcount > 0);
116
117   LOCK_MANAGER (manager);
118   manager->refcount -= 1;
119   if (manager->refcount == 0)
120     {
121       UNLOCK_MANAGER (manager);
122       
123       g_static_mutex_free (&manager->lock);
124
125       g_static_mutex_lock (&connection_gproxy_lock);
126       
127       dbus_connection_set_data (manager->connection,
128                                 gproxy_manager_slot,
129                                 NULL, NULL);
130
131       g_static_mutex_unlock (&connection_gproxy_lock);
132       
133       dbus_connection_unref (manager->connection);
134       g_free (manager);
135
136       dbus_connection_free_data_slot (&gproxy_manager_slot);
137     }
138   else
139     {
140       UNLOCK_MANAGER (manager);
141     }
142 }
143
144 /**
145  * Internals of DBusGProxy
146  */
147 struct DBusGProxy
148 {
149   GStaticMutex lock; /**< Thread lock */
150   int refcount;      /**< Reference count */
151   DBusGProxyManager *manager; /**< Proxy manager */
152   char *service;             /**< Service messages go to or NULL */
153   char *path;                /**< Path messages go to or NULL */
154   char *interface;           /**< Interface messages go to or NULL */
155 };
156
157 /** Lock the DBusGProxy */
158 #define LOCK_PROXY(proxy)   (g_static_mutex_lock (&(proxy)->lock))
159 /** Unlock the DBusGProxy */
160 #define UNLOCK_PROXY(proxy) (g_static_mutex_unlock (&(proxy)->lock))
161
162 static DBusGProxy*
163 _dbus_gproxy_new (DBusConnection *connection)
164 {
165   DBusGProxy *proxy;
166
167   proxy = g_new0 (DBusGProxy, 1);
168   
169   proxy->refcount = 1;
170   proxy->manager = dbus_gproxy_manager_get (connection);
171
172   g_static_mutex_init (&proxy->lock);
173   
174   return proxy;
175 }
176
177 /** @} End of DBusGLibInternals */
178
179 /** @addtogroup DBusGLib
180  * @{
181  */
182
183 /**
184  * Creates a new proxy for a remote interface. Method calls and signal
185  * connections over this proxy will go to the service owner; the
186  * service owner is expected to support the given interface name. THE
187  * SERVICE OWNER MAY CHANGE OVER TIME, for example between two
188  * different method calls. If you need a fixed owner, you need to
189  * request the current owner and bind a proxy to that rather than to
190  * the generic service name; see dbus_gproxy_new_for_service_owner().
191  *
192  * A service-associated proxy only makes sense with a message bus,
193  * not for app-to-app direct dbus connections.
194  *
195  * @param connection the connection to the remote bus or app
196  * @param service_name name of the service on the message bus
197  * @param path_name name of the object inside the service to call methods on
198  * @param interface_name name of the interface to call methods on
199  * @returns new proxy object
200  */
201 DBusGProxy*
202 dbus_gproxy_new_for_service (DBusConnection *connection,
203                              const char     *service_name,
204                              const char     *path_name,
205                              const char     *interface_name)
206 {
207   DBusGProxy *proxy;
208
209   g_return_val_if_fail (connection != NULL, NULL);
210   g_return_val_if_fail (service_name != NULL, NULL);
211   g_return_val_if_fail (path_name != NULL, NULL);
212   g_return_val_if_fail (interface_name != NULL, NULL);
213   
214   proxy = _dbus_gproxy_new (connection);
215
216   proxy->service = g_strdup (service_name);
217   proxy->path = g_strdup (path_name);
218   proxy->interface = g_strdup (interface_name);
219
220   return proxy;
221 }
222
223 /**
224  * Increment reference count on proxy object.
225  *
226  * @todo use GAtomic to avoid locking
227  * 
228  * @param proxy the proxy
229  */
230 void
231 dbus_gproxy_ref (DBusGProxy *proxy)
232 {
233   g_return_if_fail (proxy != NULL);
234
235   LOCK_PROXY (proxy);
236   
237   proxy->refcount += 1;
238
239   UNLOCK_PROXY (proxy);
240 }
241
242 /**
243  * Decrement reference count on proxy object.
244  * 
245  * @todo use GAtomic to avoid locking
246  *
247  * @param proxy the proxy
248  */
249 void
250 dbus_gproxy_unref (DBusGProxy *proxy)
251 {
252   g_return_if_fail (proxy != NULL);
253
254   LOCK_PROXY (proxy);
255   
256   proxy->refcount -= 1;  
257   
258   if (proxy->refcount == 0)
259     {
260       UNLOCK_PROXY (proxy);
261       
262       dbus_gproxy_manager_unref (proxy->manager);
263       g_free (proxy->service);
264       g_free (proxy->path);
265       g_free (proxy->interface);
266       g_static_mutex_free (&proxy->lock);
267       g_free (proxy);
268     }
269   else
270     {
271       UNLOCK_PROXY (proxy);
272     }
273 }
274
275 /**
276  * Invokes a method on a remote interface. This function does not
277  * block; instead it returns an opaque #DBusPendingCall object that
278  * tracks the pending call.  The method call will not be sent over the
279  * wire until the application returns to the main loop, or blocks in
280  * dbus_connection_flush() to write out pending data.  The call will
281  * be completed after a timeout, or when a reply is received.
282  * To collect the results of the call (which may be an error,
283  * or a reply), use dbus_gproxy_end_call().
284  *
285  * @todo this particular function shouldn't die on out of memory,
286  * since you should be able to do a call with large arguments.
287  * 
288  * @param proxy a proxy for a remote interface
289  * @param method the name of the method to invoke
290  * @param first_arg_type type of the first argument
291  *
292  * @returns opaque pending call object
293  *  */
294 DBusPendingCall*
295 dbus_gproxy_begin_call (DBusGProxy *proxy,
296                         const char *method,
297                         int         first_arg_type,
298                         ...)
299 {
300   DBusPendingCall *pending;
301   DBusMessage *message;
302   va_list args;
303   
304   g_return_val_if_fail (proxy != NULL, NULL);
305   LOCK_PROXY (proxy);
306
307   message = dbus_message_new_method_call (proxy->service,
308                                           proxy->path,
309                                           proxy->interface,
310                                           method);
311   if (message == NULL)
312     goto oom;
313
314   va_start (args, first_arg_type);
315   if (!dbus_message_append_args_valist (message, first_arg_type,
316                                         args))
317     goto oom;
318   va_end (args);
319
320   if (!dbus_connection_send_with_reply (proxy->manager->connection,
321                                         message,
322                                         &pending,
323                                         -1))
324     goto oom;
325   
326   UNLOCK_PROXY (proxy);
327
328   return pending;
329
330  oom:
331   /* FIXME we should create a pending call that's
332    * immediately completed with an error status without
333    * ever going on the wire.
334    */
335   
336   g_error ("Out of memory");
337   return NULL;
338 }
339
340 /**
341  * Collects the results of a method call. The method call was normally
342  * initiated with dbus_gproxy_end_call(). This function will block if
343  * the results haven't yet been received; use
344  * dbus_pending_call_set_notify() to be notified asynchronously that a
345  * pending call has been completed. Use
346  * dbus_pending_call_get_completed() to check whether a call has been
347  * completed. If it's completed, it will not block.
348  *
349  * If the call results in an error, the error is set as normal for
350  * GError and the function returns #FALSE.
351  *
352  * Otherwise, the "out" parameters and return value of the
353  * method are stored in the provided varargs list.
354  * The list should be terminated with DBUS_TYPE_INVALID.
355  *
356  * This function doesn't affect the reference count of the
357  * #DBusPendingCall, the caller of dbus_gproxy_begin_call() still owns
358  * a reference.
359  *
360  * @param proxy a proxy for a remote interface
361  * @param pending the pending call from dbus_gproxy_begin_call()
362  * @param error return location for an error
363  * @param first_arg_type type of first "out" argument
364  * @returns #FALSE if an error is set */
365 gboolean
366 dbus_gproxy_end_call (DBusGProxy          *proxy,
367                       DBusPendingCall     *pending,
368                       GError             **error,
369                       int                  first_arg_type,
370                       ...)
371 {
372   DBusMessage *message;
373   va_list args;
374   DBusError derror;
375   
376   g_return_val_if_fail (proxy != NULL, FALSE);
377   g_return_val_if_fail (pending != NULL, FALSE);
378   
379   LOCK_PROXY (proxy);
380
381   dbus_pending_call_block (pending);
382   message = dbus_pending_call_get_reply (pending);
383
384   g_assert (message != NULL);
385
386   dbus_error_init (&derror);
387   va_start (args, first_arg_type);
388   if (!dbus_message_get_args_valist (message, &derror, first_arg_type, args))
389     {
390       va_end (args);
391       goto error;
392     }
393   va_end (args);
394
395   UNLOCK_PROXY (proxy);
396
397   return TRUE;
398
399  error:
400   dbus_set_g_error (error, &derror);
401   dbus_error_free (&derror);
402   return FALSE;
403 }
404
405 /**
406  * Sends a message to the interface we're proxying for.  Does not
407  * block or wait for a reply. The message is only actually written out
408  * when you return to the main loop or block in
409  * dbus_connection_flush().
410  *
411  * The message is modified to be addressed to the target interface.
412  * That is, a destination service field or whatever is needed will be
413  * added to the message. The basic point of this function is to add
414  * the necessary header fields, otherwise it's equivalent to
415  * dbus_connection_send().
416  *
417  * This function adds a reference to the message, so the caller
418  * still owns its original reference.
419  * 
420  * @param proxy a proxy for a remote interface
421  * @param message the message to address and send
422  * @param client_serial return location for message's serial, or #NULL */
423 void
424 dbus_gproxy_send (DBusGProxy          *proxy,
425                   DBusMessage         *message,
426                   dbus_uint32_t       *client_serial)
427 {
428   g_return_if_fail (proxy != NULL);
429   LOCK_PROXY (proxy);
430   
431   if (proxy->service)
432     {
433       if (!dbus_message_set_destination (message, proxy->service))
434         g_error ("Out of memory");
435     }
436   if (proxy->path)
437     {
438       if (!dbus_message_set_path (message, proxy->path))
439         g_error ("Out of memory");
440     }
441   if (proxy->interface)
442     {
443       if (!dbus_message_set_interface (message, proxy->interface))
444         g_error ("Out of memory");
445     }
446   
447   if (!dbus_connection_send (proxy->manager->connection, message, client_serial))
448     g_error ("Out of memory\n");
449
450   UNLOCK_PROXY (proxy);
451 }
452
453 /** @} End of DBusGLib public */
454
455 #ifdef DBUS_BUILD_TESTS
456
457 /**
458  * @ingroup DBusGLibInternals
459  * Unit test for GLib proxy functions
460  * @returns #TRUE on success.
461  */
462 dbus_bool_t
463 _dbus_gproxy_test (void)
464 {
465
466   return TRUE;
467 }
468
469 #endif /* DBUS_BUILD_TESTS */