2003-03-31 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   DBusConnection **connection; /**< Pointer to bus_connections entry */
55 } BusData;
56
57 /** The slot we have reserved to store BusData
58  */
59 static int bus_data_slot = -1;
60 /** Number of connections using the slot
61  */
62 static int bus_data_slot_refcount = 0;
63
64 /**
65  * Lock for bus_data_slot and bus_data_slot_refcount
66  */
67 _DBUS_DEFINE_GLOBAL_LOCK (bus);
68
69
70 static dbus_bool_t
71 data_slot_ref (void)
72 {
73   _DBUS_LOCK (bus);
74
75   if (bus_data_slot < 0)
76     {
77       bus_data_slot = dbus_connection_allocate_data_slot ();
78       
79       if (bus_data_slot < 0)
80         {
81           _DBUS_UNLOCK (bus);
82           return FALSE;
83         }
84
85       _dbus_assert (bus_data_slot_refcount == 0);
86     }
87
88   bus_data_slot_refcount += 1;
89
90   _DBUS_UNLOCK (bus);
91
92   return TRUE;
93 }
94
95 static void
96 data_slot_unref (void)
97 {
98   _DBUS_LOCK (bus);
99
100   _dbus_assert (bus_data_slot_refcount > 0);
101   _dbus_assert (bus_data_slot >= 0);
102
103   bus_data_slot_refcount -= 1;
104
105   if (bus_data_slot_refcount == 0)
106     {
107       dbus_connection_free_data_slot (bus_data_slot);
108       bus_data_slot = -1;
109     }
110
111   _DBUS_UNLOCK (bus);
112 }
113
114 static void
115 bus_data_free (void *data)
116 {
117   BusData *bd = data;
118
119   if (bd->connection)
120     *bd->connection = NULL;
121   
122   dbus_free (bd->base_service);
123   dbus_free (bd);
124
125   data_slot_unref ();
126 }
127
128 static BusData*
129 ensure_bus_data (DBusConnection *connection)
130 {
131   BusData *bd;
132
133   if (!data_slot_ref ())
134     return NULL;
135
136   bd = dbus_connection_get_data (connection, bus_data_slot);
137   if (bd == NULL)
138     {      
139       bd = dbus_new0 (BusData, 1);
140       if (bd == NULL)
141         {
142           data_slot_unref ();
143           return NULL;
144         }
145       
146       if (!dbus_connection_set_data (connection, bus_data_slot, bd,
147                                      bus_data_free))
148         {
149           dbus_free (bd);
150           data_slot_unref ();
151           return NULL;
152         }
153
154       /* Data slot refcount now held by the BusData */
155     }
156   else
157     {
158       data_slot_unref ();
159     }
160
161   return bd;
162 }
163
164 /** @} */ /* end of implementation details docs */
165
166 /**
167  * @addtogroup DBusBus
168  * @{
169  */
170
171 /** Number of bus types */
172 #define BUS_TYPES 2
173
174 static DBusConnection *bus_connections[BUS_TYPES];
175
176 /**
177  * Connects to a bus daemon and registers the client with it.
178  * If a connection to the bus already exists, then that connection is returned.
179  *
180  * @todo alex thinks we should nullify the connection when we get a disconnect-message.
181  *
182  * @param type bus type
183  * @param error address where an error can be returned.
184  * @returns a DBusConnection
185  */
186 DBusConnection *
187 dbus_bus_get (DBusBusType  type,
188               DBusError   *error)
189 {
190   const char *name, *value;
191   DBusConnection *connection;
192   BusData *bd;
193
194   if (type <= 0 || type >= BUS_TYPES)
195     {
196       _dbus_assert_not_reached ("Invalid bus type specified.");
197
198       return NULL;
199     }
200
201   _DBUS_LOCK (bus);
202   
203   if (bus_connections[type] != NULL)
204     {
205       connection = bus_connections[type];
206       dbus_connection_ref (connection);
207       
208       _DBUS_UNLOCK (bus);
209       return connection;
210     }
211
212   switch (type)
213     {
214     case DBUS_BUS_SESSION:
215       name = "DBUS_SESSION_BUS_ADDRESS";
216       break;
217     case DBUS_BUS_SYSTEM:
218       name = "DBUS_SYSTEM_BUS_ADDRESS";
219       break;
220     }
221
222   value = _dbus_getenv (name);
223
224   if (type == DBUS_BUS_SYSTEM &&
225       (value == NULL || *value == '\0'))
226     {
227       /* Use default system bus address if none set */
228       value = "unix:path=" DBUS_SYSTEM_BUS_PATH;
229     }
230   
231   if (value == NULL || *value == '\0')
232     {
233       dbus_set_error (error, DBUS_ERROR_FAILED,
234                       "Environment variable %s not set, address of message bus unknown",
235                       name);
236       _DBUS_UNLOCK (bus);
237       
238       return NULL;
239     }
240
241   connection = dbus_connection_open (value, error);
242   
243   if (!connection)
244     {
245       _DBUS_UNLOCK (bus);
246       return NULL;
247     }
248   
249   if (!dbus_bus_register (connection, error))
250     {
251       dbus_connection_disconnect (connection);
252       dbus_connection_unref (connection);
253
254       _DBUS_UNLOCK (bus);
255       return NULL;
256     }
257
258   bus_connections[type] = connection;
259   bd = ensure_bus_data (connection);
260   _dbus_assert (bd != NULL);
261
262   bd->connection = &bus_connections[type];
263
264   _DBUS_UNLOCK (bus);  
265   return connection;
266 }
267
268
269 /**
270  * Registers a connection with the bus. This must be the first
271  * thing an application does when connecting to the message bus.
272  * If registration succeeds, the base service name will be set,
273  * and can be obtained using dbus_bus_get_base_service().
274  *
275  * @todo if we get an error reply, it has to be converted into
276  * DBusError and returned
277  * 
278  * @param connection the connection
279  * @param error place to store errors
280  * @returns #TRUE on success
281  */
282 dbus_bool_t
283 dbus_bus_register (DBusConnection *connection,
284                    DBusError      *error)
285 {
286   DBusMessage *message, *reply;
287   char *name;
288   BusData *bd;
289
290   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
291   
292   bd = ensure_bus_data (connection);
293   if (bd == NULL)
294     {
295       _DBUS_SET_OOM (error);
296       return FALSE;
297     }
298
299   if (bd->base_service != NULL)
300     {
301       _dbus_warn ("Attempt to register the same DBusConnection with the message bus, but it is already registered\n");
302       /* This isn't an error, it's a programming bug. We'll be nice
303        * and not _dbus_assert_not_reached()
304        */
305       return TRUE;
306     }
307   
308   message = dbus_message_new (DBUS_SERVICE_DBUS,
309                               DBUS_MESSAGE_HELLO);
310
311   if (!message)
312     {
313       _DBUS_SET_OOM (error);
314       return FALSE;
315     }
316   
317   reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
318
319   dbus_message_unref (message);
320   
321   if (reply == NULL)
322     {
323       _DBUS_ASSERT_ERROR_IS_SET (error);
324       return FALSE;
325     }
326
327   if (!dbus_message_get_args (reply, error,
328                               DBUS_TYPE_STRING, &name,
329                               0))
330     {
331       _DBUS_ASSERT_ERROR_IS_SET (error);
332       return FALSE;
333     }
334
335   bd->base_service = name;
336   
337   return TRUE;
338 }
339
340
341 /**
342  * Sets the base service name of the connection.
343  * Can only be used if you registered with the
344  * bus manually (i.e. if you did not call
345  * dbus_bus_register()). Can only be called
346  * once per connection.
347  *
348  * @param connection the connection
349  * @param base_service the base service name
350  * @returns #FALSE if not enough memory
351  */
352 dbus_bool_t
353 dbus_bus_set_base_service (DBusConnection *connection,
354                            const char     *base_service)
355 {
356   BusData *bd;
357
358   bd = ensure_bus_data (connection);
359   if (bd == NULL)
360     return FALSE;
361
362   _dbus_assert (bd->base_service == NULL);
363   _dbus_assert (base_service != NULL);
364   
365   bd->base_service = _dbus_strdup (base_service);
366   return bd->base_service != NULL;
367 }
368
369 /**
370  * Gets the base service name of the connection.
371  * Only possible after the connection has been registered
372  * with the message bus.
373  *
374  * @param connection the connection
375  * @returns the base service name
376  */
377 const char*
378 dbus_bus_get_base_service (DBusConnection *connection)
379 {
380   BusData *bd;
381
382   bd = ensure_bus_data (connection);
383   if (bd == NULL)
384     return NULL;
385   
386   return bd->base_service;
387 }
388
389 /**
390  * Asks the bus to try to acquire a certain service.
391  *
392  * @todo these docs are not complete, need to document the
393  * return value and flags
394  * 
395  * @todo if we get an error reply, it has to be converted into
396  * DBusError and returned
397  *
398  * @param connection the connection
399  * @param service_name the service name
400  * @param flags flags
401  * @param error location to store the error
402  * @returns a result code, -1 if error is set
403  */ 
404 int
405 dbus_bus_acquire_service (DBusConnection *connection,
406                           const char     *service_name,
407                           unsigned int    flags,
408                           DBusError      *error)
409 {
410   DBusMessage *message, *reply;
411   int service_result;
412   
413   message = dbus_message_new (DBUS_SERVICE_DBUS,
414                               DBUS_MESSAGE_ACQUIRE_SERVICE);
415
416   if (message == NULL)
417     {
418       _DBUS_SET_OOM (error);
419       return -1;
420     }
421  
422   if (!dbus_message_append_args (message,
423                                  DBUS_TYPE_STRING, service_name,
424                                  DBUS_TYPE_UINT32, flags,
425                                  0))
426     {
427       dbus_message_unref (message);
428       _DBUS_SET_OOM (error);
429       return -1;
430     }
431   
432   reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
433                                                      error);
434   
435   dbus_message_unref (message);
436   
437   if (reply == NULL)
438     {
439       _DBUS_ASSERT_ERROR_IS_SET (error);
440       return -1;
441     }
442
443   if (!dbus_message_get_args (reply, error,
444                               DBUS_TYPE_UINT32, &service_result,
445                               0))
446     {
447       _DBUS_ASSERT_ERROR_IS_SET (error);
448       return -1;
449     }
450
451   return service_result;
452 }
453
454 /**
455  * Checks whether a certain service exists.
456  *
457  * @todo the SERVICE_EXISTS message should use BOOLEAN not UINT32
458  *
459  * @param connection the connection
460  * @param service_name the service name
461  * @param error location to store any errors
462  * @returns #TRUE if the service exists, #FALSE if not or on error
463  */
464 dbus_bool_t
465 dbus_bus_service_exists (DBusConnection *connection,
466                          const char     *service_name,
467                          DBusError      *error)
468 {
469   DBusMessage *message, *reply;
470   unsigned int exists;
471
472   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
473   
474   message = dbus_message_new (DBUS_SERVICE_DBUS,
475                               DBUS_MESSAGE_SERVICE_EXISTS);
476   if (message == NULL)
477     {
478       _DBUS_SET_OOM (error);
479       return FALSE;
480     }
481   
482   if (!dbus_message_append_args (message,
483                                  DBUS_TYPE_STRING, service_name,
484                                  0))
485     {
486       dbus_message_unref (message);
487       _DBUS_SET_OOM (error);
488       return FALSE;
489     }
490   
491   reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
492   dbus_message_unref (message);
493
494   if (reply == NULL)
495     {
496       _DBUS_ASSERT_ERROR_IS_SET (error);
497       return FALSE;
498     }
499
500   if (!dbus_message_get_args (reply, error,
501                               DBUS_TYPE_UINT32, &exists,
502                               0))
503     {
504       _DBUS_ASSERT_ERROR_IS_SET (error);
505       return FALSE;
506     }
507   
508   return (exists != FALSE);
509 }
510
511 /** @} */