2003-03-16 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / dbus / dbus-bus.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-bus.c  Convenience functions for communicating with the bus.
3  *
4  * Copyright (C) 2003  CodeFactory AB
5  * Copyright (C) 2003  Red Hat, Inc.
6  *
7  * Licensed under the Academic Free License version 1.2
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #include "dbus-bus.h"
26 #include "dbus-protocol.h"
27 #include "dbus-internals.h"
28
29 /**
30  * @defgroup DBusBus Message bus APIs
31  * @ingroup DBus
32  * @brief Functions for communicating with the message bus
33  *
34  */
35
36
37 /**
38  * @defgroup DBusBusInternals Message bus APIs internals
39  * @ingroup DBusInternals
40  * @brief Internals of functions for communicating with the message bus
41  *
42  * @{
43  */
44
45 /**
46  * Block of message-bus-related data we attach to each
47  * #DBusConnection used with these convenience functions.
48  *
49  */
50 typedef struct
51 {
52   char *base_service; /**< Base service name of this connection */
53
54 } BusData;
55
56 /** The slot we have reserved to store BusData
57  */
58 static int bus_data_slot = -1;
59 /** Number of connections using the slot
60  */
61 static int bus_data_slot_refcount = 0;
62
63 /**
64  * Lock for bus_data_slot and bus_data_slot_refcount
65  */
66 static DBusMutex *slot_lock;
67
68 /**
69  * Initialize the mutex used for bus_data_slot
70  *
71  * @returns the mutex
72  */
73 DBusMutex *
74 _dbus_bus_init_lock (void)
75 {
76   slot_lock = dbus_mutex_new ();
77   return slot_lock;
78 }
79
80 static dbus_bool_t
81 data_slot_ref (void)
82 {
83   dbus_mutex_lock (slot_lock);
84
85   if (bus_data_slot < 0)
86     {
87       bus_data_slot = dbus_connection_allocate_data_slot ();
88       
89       if (bus_data_slot < 0)
90         {
91           dbus_mutex_unlock (slot_lock);
92           return FALSE;
93         }
94
95       _dbus_assert (bus_data_slot_refcount == 0);
96     }
97
98   bus_data_slot_refcount += 1;
99
100   dbus_mutex_unlock (slot_lock);
101
102   return TRUE;
103 }
104
105 static void
106 data_slot_unref (void)
107 {
108   dbus_mutex_lock (slot_lock);
109
110   _dbus_assert (bus_data_slot_refcount > 0);
111   _dbus_assert (bus_data_slot >= 0);
112
113   bus_data_slot_refcount -= 1;
114
115   if (bus_data_slot_refcount == 0)
116     {
117       dbus_connection_free_data_slot (bus_data_slot);
118       bus_data_slot = -1;
119     }
120
121   dbus_mutex_unlock (slot_lock);
122 }
123
124 static void
125 bus_data_free (void *data)
126 {
127   BusData *bd = data;
128
129   dbus_free (bd->base_service);
130   dbus_free (bd);
131
132   data_slot_unref ();
133 }
134
135 static BusData*
136 ensure_bus_data (DBusConnection *connection)
137 {
138   BusData *bd;
139
140   if (!data_slot_ref ())
141     return NULL;
142
143   bd = dbus_connection_get_data (connection, bus_data_slot);
144   if (bd == NULL)
145     {      
146       bd = dbus_new0 (BusData, 1);
147       if (bd == NULL)
148         {
149           data_slot_unref ();
150           return NULL;
151         }
152       
153       if (!dbus_connection_set_data (connection, bus_data_slot, bd,
154                                      bus_data_free))
155         {
156           dbus_free (bd);
157           data_slot_unref ();
158           return NULL;
159         }
160
161       /* Data slot refcount now held by the BusData */
162     }
163   else
164     {
165       data_slot_unref ();
166     }
167
168   return bd;
169 }
170
171 /** @} */ /* end of implementation details docs */
172
173 /**
174  * @addtogroup DBusBus
175  * @{
176  */
177
178 /**
179  * Registers a connection with the bus. This must be the first
180  * thing an application does when connecting to the message bus.
181  * If registration succeeds, the base service name will be set,
182  * and can be obtained using dbus_bus_get_base_service().
183  *
184  * @todo if we get an error reply, it has to be converted into
185  * DBusError and returned
186  * 
187  * @param connection the connection
188  * @param error place to store errors
189  * @returns #TRUE on success
190  */
191 dbus_bool_t
192 dbus_bus_register (DBusConnection *connection,
193                    DBusError      *error)
194 {
195   DBusMessage *message, *reply;
196   char *name;
197   BusData *bd;
198
199   bd = ensure_bus_data (connection);
200   if (bd == NULL)
201     {
202       _DBUS_SET_OOM (error);
203       return FALSE;
204     }
205
206   if (bd->base_service != NULL)
207     {
208       _dbus_warn ("Attempt to register the same DBusConnection with the message bus, but it is already registered\n");
209       /* This isn't an error, it's a programming bug. We'll be nice
210        * and not _dbus_assert_not_reached()
211        */
212       return TRUE;
213     }
214   
215   message = dbus_message_new (DBUS_SERVICE_DBUS,
216                               DBUS_MESSAGE_HELLO);
217
218   if (!message)
219     {
220       _DBUS_SET_OOM (error);
221       return FALSE;
222     }
223   
224   reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
225
226   dbus_message_unref (message);
227   
228   if (reply == NULL)
229     {
230       _DBUS_ASSERT_ERROR_IS_SET (error);
231       return FALSE;
232     }
233
234   if (!dbus_message_get_args (reply, error,
235                               DBUS_TYPE_STRING, &name,
236                               0))
237     {
238       _DBUS_ASSERT_ERROR_IS_SET (error);
239       return FALSE;
240     }
241
242   bd->base_service = name;
243   
244   return TRUE;
245 }
246
247
248 /**
249  * Sets the base service name of the connection.
250  * Can only be used if you registered with the
251  * bus manually (i.e. if you did not call
252  * dbus_bus_register()). Can only be called
253  * once per connection.
254  *
255  * @param connection the connection
256  * @param base_service the base service name
257  * @returns #FALSE if not enough memory
258  */
259 dbus_bool_t
260 dbus_bus_set_base_service (DBusConnection *connection,
261                            const char     *base_service)
262 {
263   BusData *bd;
264
265   bd = ensure_bus_data (connection);
266   if (bd == NULL)
267     return FALSE;
268
269   _dbus_assert (bd->base_service == NULL);
270   _dbus_assert (base_service != NULL);
271   
272   bd->base_service = _dbus_strdup (base_service);
273   return bd->base_service != NULL;
274 }
275
276 /**
277  * Gets the base service name of the connection.
278  * Only possible after the connection has been registered
279  * with the message bus.
280  *
281  * @param connection the connection
282  * @returns the base service name
283  */
284 const char*
285 dbus_bus_get_base_service (DBusConnection *connection)
286 {
287   BusData *bd;
288
289   bd = ensure_bus_data (connection);
290   if (bd == NULL)
291     return NULL;
292   
293   return bd->base_service;
294 }
295
296 /**
297  * Asks the bus to try to acquire a certain service.
298  *
299  * @todo these docs are not complete, need to document the
300  * return value and flags
301  * 
302  * @todo if we get an error reply, it has to be converted into
303  * DBusError and returned
304  *
305  * @param connection the connection
306  * @param service_name the service name
307  * @param flags flags
308  * @param error location to store the error
309  * @returns a result code, -1 if error is set
310  */ 
311 int
312 dbus_bus_acquire_service (DBusConnection *connection,
313                           const char     *service_name,
314                           unsigned int    flags,
315                           DBusError      *error)
316 {
317   DBusMessage *message, *reply;
318   int service_result;
319   
320   message = dbus_message_new (DBUS_SERVICE_DBUS,
321                               DBUS_MESSAGE_ACQUIRE_SERVICE);
322
323   if (message == NULL)
324     {
325       _DBUS_SET_OOM (error);
326       return -1;
327     }
328  
329   if (!dbus_message_append_args (message,
330                                  DBUS_TYPE_STRING, service_name,
331                                  DBUS_TYPE_UINT32, flags,
332                                  0))
333     {
334       dbus_message_unref (message);
335       _DBUS_SET_OOM (error);
336       return -1;
337     }
338   
339   reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
340                                                      error);
341   
342   dbus_message_unref (message);
343   
344   if (reply == NULL)
345     {
346       _DBUS_ASSERT_ERROR_IS_SET (error);
347       return -1;
348     }
349
350   if (!dbus_message_get_args (reply, error,
351                               DBUS_TYPE_UINT32, &service_result,
352                               0))
353     {
354       _DBUS_ASSERT_ERROR_IS_SET (error);
355       return -1;
356     }
357
358   return service_result;
359 }
360
361 /**
362  * Checks whether a certain service exists.
363  *
364  * @todo the SERVICE_EXISTS message should use BOOLEAN not UINT32
365  *
366  * @param connection the connection
367  * @param service_name the service name
368  * @param error location to store any errors
369  * @returns #TRUE if the service exists, #FALSE if not or on error
370  */
371 dbus_bool_t
372 dbus_bus_service_exists (DBusConnection *connection,
373                          const char     *service_name,
374                          DBusError      *error)
375 {
376   DBusMessage *message, *reply;
377   unsigned int exists;
378   
379   message = dbus_message_new (DBUS_SERVICE_DBUS,
380                               DBUS_MESSAGE_SERVICE_EXISTS);
381   if (message == NULL)
382     {
383       _DBUS_SET_OOM (error);
384       return FALSE;
385     }
386   
387   if (!dbus_message_append_args (message,
388                                  DBUS_TYPE_STRING, service_name,
389                                  0))
390     {
391       dbus_message_unref (message);
392       _DBUS_SET_OOM (error);
393       return FALSE;
394     }
395   
396   reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
397   dbus_message_unref (message);
398
399   if (reply == NULL)
400     {
401       _DBUS_ASSERT_ERROR_IS_SET (error);
402       return FALSE;
403     }
404
405   if (!dbus_message_get_args (reply, error,
406                               DBUS_TYPE_UINT32, &exists,
407                               0))
408     {
409       _DBUS_ASSERT_ERROR_IS_SET (error);
410       return FALSE;
411     }
412   
413   return (exists != FALSE);
414 }
415
416 /** @} */