2003-04-30 Havoc Pennington <hp@redhat.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 #include <string.h>
29
30 /**
31  * @defgroup DBusBus Message bus APIs
32  * @ingroup DBus
33  * @brief Functions for communicating with the message bus
34  *
35  */
36
37
38 /**
39  * @defgroup DBusBusInternals Message bus APIs internals
40  * @ingroup DBusInternals
41  * @brief Internals of functions for communicating with the message bus
42  *
43  * @{
44  */
45
46 /**
47  * Block of message-bus-related data we attach to each
48  * #DBusConnection used with these convenience functions.
49  *
50  */
51 typedef struct
52 {
53   DBusConnection *connection; /**< Connection we're associated with */
54   char *base_service; /**< Base service name of this connection */
55
56   unsigned int is_well_known : 1; /**< Is one of the well-known connections in our global array */
57 } BusData;
58
59 /** The slot we have reserved to store BusData
60  */
61 static int bus_data_slot = -1;
62 /** Number of connections using the slot
63  */
64 static int bus_data_slot_refcount = 0;
65
66 /** Number of bus types */
67 #define N_BUS_TYPES 3
68
69 static DBusConnection *bus_connections[N_BUS_TYPES];
70 static char *bus_connection_addresses[N_BUS_TYPES] = { NULL, NULL, NULL };
71
72 static DBusBusType activation_bus_type = DBUS_BUS_ACTIVATION;
73
74 static dbus_bool_t initialized = FALSE;
75
76 /**
77  * Lock for globals in this file
78  */
79 _DBUS_DEFINE_GLOBAL_LOCK (bus);
80
81 static void
82 addresses_shutdown_func (void *data)
83 {
84   int i;
85
86   i = 0;
87   while (i < N_BUS_TYPES)
88     {
89       if (bus_connections[i] != NULL)
90         _dbus_warn ("dbus_shutdown() called but connections were still live!");
91       
92       dbus_free (bus_connection_addresses[i]);
93       bus_connection_addresses[i] = NULL;
94       ++i;
95     }
96
97   activation_bus_type = DBUS_BUS_ACTIVATION;
98 }
99
100 static dbus_bool_t
101 get_from_env (char           **connection_p,
102               const char      *env_var)
103 {
104   const char *s;
105   
106   _dbus_assert (*connection_p == NULL);
107   
108   s = _dbus_getenv (env_var);
109   if (s == NULL || *s == '\0')
110     return TRUE; /* successfully didn't use the env var */
111   else
112     {
113       *connection_p = _dbus_strdup (s);
114       return *connection_p != NULL;
115     }
116 }
117
118 static dbus_bool_t
119 init_connections_unlocked (void)
120 {
121   if (!initialized)
122     {
123       const char *s;
124       int i;
125
126       i = 0;
127       while (i < N_BUS_TYPES)
128         {
129           bus_connections[i] = NULL;
130           ++i;
131         }
132
133       /* Don't init these twice, we may run this code twice if
134        * init_connections_unlocked() fails midway through.
135        */
136       
137        if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
138          {
139            if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SYSTEM],
140                               "DBUS_SYSTEM_BUS_ADDRESS"))
141              return FALSE;
142            
143            if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
144              {
145                /* Use default system bus address if none set in environment */
146                bus_connection_addresses[DBUS_BUS_SYSTEM] =
147                  _dbus_strdup ("unix:path=" DBUS_SYSTEM_BUS_PATH);
148                if (bus_connection_addresses[DBUS_BUS_SYSTEM] == NULL)
149                  return FALSE;
150              }
151          }
152           
153       if (bus_connection_addresses[DBUS_BUS_SESSION] == NULL)
154         {
155           if (!get_from_env (&bus_connection_addresses[DBUS_BUS_SESSION],
156                              "DBUS_SESSION_BUS_ADDRESS"))
157             return FALSE;
158         }
159
160       if (bus_connection_addresses[DBUS_BUS_ACTIVATION] == NULL)
161         {
162           if (!get_from_env (&bus_connection_addresses[DBUS_BUS_ACTIVATION],
163                              "DBUS_ACTIVATION_ADDRESS"))
164             return FALSE;
165         }
166
167       s = _dbus_getenv ("DBUS_ACTIVATION_BUS_TYPE");
168
169       if (s != NULL)
170         {
171           if (strcmp (s, "system") == 0)
172             activation_bus_type = DBUS_BUS_SYSTEM;
173           else if (strcmp (s, "session") == 0)
174             activation_bus_type = DBUS_BUS_SESSION;
175         }
176
177       /* If we return FALSE we have to be sure that restarting
178        * the above code will work right
179        */
180       
181       if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", NULL))
182         return FALSE;
183
184       if (!_dbus_setenv ("DBUS_ACTIVATION_BUS_TYPE", NULL))
185         return FALSE;
186       
187       if (!_dbus_register_shutdown_func (addresses_shutdown_func,
188                                          NULL))
189         return FALSE;
190       
191       initialized = TRUE;
192     }
193
194   return initialized;
195 }
196
197 static dbus_bool_t
198 data_slot_ref (void)
199 {
200   _DBUS_LOCK (bus);
201
202   if (bus_data_slot < 0)
203     {
204       bus_data_slot = dbus_connection_allocate_data_slot ();
205       
206       if (bus_data_slot < 0)
207         {
208           _DBUS_UNLOCK (bus);
209           return FALSE;
210         }
211
212       _dbus_assert (bus_data_slot_refcount == 0);
213     }
214
215   bus_data_slot_refcount += 1;
216
217   _DBUS_UNLOCK (bus);
218
219   return TRUE;
220 }
221
222 static void
223 data_slot_unref (void)
224 {
225   _DBUS_LOCK (bus);
226
227   _dbus_assert (bus_data_slot_refcount > 0);
228   _dbus_assert (bus_data_slot >= 0);
229
230   bus_data_slot_refcount -= 1;
231
232   if (bus_data_slot_refcount == 0)
233     {
234       dbus_connection_free_data_slot (bus_data_slot);
235       bus_data_slot = -1;
236     }
237
238   _DBUS_UNLOCK (bus);
239 }
240
241 static void
242 bus_data_free (void *data)
243 {
244   BusData *bd = data;
245   
246   if (bd->is_well_known)
247     {
248       int i;
249       _DBUS_LOCK (bus);
250       /* We may be stored in more than one slot */
251       i = 0;
252       while (i < N_BUS_TYPES)
253         {
254           if (bus_connections[i] == bd->connection)
255             bus_connections[i] = NULL;
256           
257           ++i;
258         }
259       _DBUS_UNLOCK (bus);
260     }
261   
262   dbus_free (bd->base_service);
263   dbus_free (bd);
264
265   data_slot_unref ();
266 }
267
268 static BusData*
269 ensure_bus_data (DBusConnection *connection)
270 {
271   BusData *bd;
272
273   if (!data_slot_ref ())
274     return NULL;
275
276   bd = dbus_connection_get_data (connection, bus_data_slot);
277   if (bd == NULL)
278     {      
279       bd = dbus_new0 (BusData, 1);
280       if (bd == NULL)
281         {
282           data_slot_unref ();
283           return NULL;
284         }
285
286       bd->connection = connection;
287       
288       if (!dbus_connection_set_data (connection, bus_data_slot, bd,
289                                      bus_data_free))
290         {
291           dbus_free (bd);
292           data_slot_unref ();
293           return NULL;
294         }
295
296       /* Data slot refcount now held by the BusData */
297     }
298   else
299     {
300       data_slot_unref ();
301     }
302
303   return bd;
304 }
305
306 /** @} */ /* end of implementation details docs */
307
308 /**
309  * @addtogroup DBusBus
310  * @{
311  */
312
313 /**
314  * Connects to a bus daemon and registers the client with it.
315  * If a connection to the bus already exists, then that connection is returned.
316  *
317  * @todo alex thinks we should nullify the connection when we get a disconnect-message.
318  *
319  * @param type bus type
320  * @param error address where an error can be returned.
321  * @returns a DBusConnection
322  */
323 DBusConnection *
324 dbus_bus_get (DBusBusType  type,
325               DBusError   *error)
326 {
327   const char *address;
328   DBusConnection *connection;
329   BusData *bd;
330   DBusBusType address_type;
331
332   _dbus_return_val_if_fail (type >= 0 && type < N_BUS_TYPES, NULL);
333   _dbus_return_val_if_error_is_set (error, NULL);
334
335   _DBUS_LOCK (bus);
336
337   if (!init_connections_unlocked ())
338     {
339       _DBUS_UNLOCK (bus);
340       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
341       return NULL;
342     }
343
344   /* We want to use the activation address even if the
345    * activating bus is the session or system bus,
346    * per the spec.
347    */
348   address_type = type;
349   
350   /* Use the real type of the activation bus for getting its
351    * connection. (If the activating bus isn't a well-known
352    * bus then activation_bus_type == DBUS_BUS_ACTIVATION)
353    */
354   if (type == DBUS_BUS_ACTIVATION)
355     type = activation_bus_type;
356   
357   if (bus_connections[type] != NULL)
358     {
359       connection = bus_connections[type];
360       dbus_connection_ref (connection);
361       
362       _DBUS_UNLOCK (bus);
363       return connection;
364     }
365
366   address = bus_connection_addresses[address_type];
367   if (address == NULL)
368     {
369       dbus_set_error (error, DBUS_ERROR_FAILED,
370                       "Unable to determine the address of the message bus");
371       _DBUS_UNLOCK (bus);
372       return NULL;
373     }
374
375   connection = dbus_connection_open (address, error);
376   
377   if (!connection)
378     {
379       _DBUS_ASSERT_ERROR_IS_SET (error);
380       _DBUS_UNLOCK (bus);
381       return NULL;
382     }
383   
384   if (!dbus_bus_register (connection, error))
385     {
386       _DBUS_ASSERT_ERROR_IS_SET (error);
387       dbus_connection_disconnect (connection);
388       dbus_connection_unref (connection);
389
390       _DBUS_UNLOCK (bus);
391       return NULL;
392     }
393
394   bus_connections[type] = connection;
395   bd = ensure_bus_data (connection);
396   _dbus_assert (bd != NULL);
397
398   bd->is_well_known = TRUE;
399
400   _DBUS_UNLOCK (bus);
401   return connection;
402 }
403
404
405 /**
406  * Registers a connection with the bus. This must be the first
407  * thing an application does when connecting to the message bus.
408  * If registration succeeds, the base service name will be set,
409  * and can be obtained using dbus_bus_get_base_service().
410  *
411  * @todo if we get an error reply, it has to be converted into
412  * DBusError and returned
413  * 
414  * @param connection the connection
415  * @param error place to store errors
416  * @returns #TRUE on success
417  */
418 dbus_bool_t
419 dbus_bus_register (DBusConnection *connection,
420                    DBusError      *error)
421 {
422   DBusMessage *message, *reply;
423   char *name;
424   BusData *bd;
425   dbus_bool_t retval;
426
427   _dbus_return_val_if_fail (connection != NULL, FALSE);
428   _dbus_return_val_if_error_is_set (error, FALSE);
429
430   retval = FALSE;
431   
432   bd = ensure_bus_data (connection);
433   if (bd == NULL)
434     {
435       _DBUS_SET_OOM (error);
436       return FALSE;
437     }
438
439   if (bd->base_service != NULL)
440     {
441       _dbus_warn ("Attempt to register the same DBusConnection with the message bus, but it is already registered\n");
442       /* This isn't an error, it's a programming bug. We'll be nice
443        * and not _dbus_assert_not_reached()
444        */
445       return TRUE;
446     }
447   
448   message = dbus_message_new (DBUS_MESSAGE_HELLO,
449                               DBUS_SERVICE_DBUS);
450                               
451
452   if (!message)
453     {
454       _DBUS_SET_OOM (error);
455       return FALSE;
456     }
457   
458   reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
459
460   dbus_message_unref (message);
461   
462   if (reply == NULL)
463     goto out;
464   else if (dbus_set_error_from_message (error, reply))
465     goto out;
466   else if (!dbus_message_get_args (reply, error,
467                                    DBUS_TYPE_STRING, &name,
468                                    0))
469     goto out;
470   
471   bd->base_service = name;
472
473   retval = TRUE;
474   
475  out:
476   if (reply)
477     dbus_message_unref (reply);
478
479   if (!retval)
480     _DBUS_ASSERT_ERROR_IS_SET (error);
481   
482   return retval;
483 }
484
485
486 /**
487  * Sets the base service name of the connection.
488  * Can only be used if you registered with the
489  * bus manually (i.e. if you did not call
490  * dbus_bus_register()). Can only be called
491  * once per connection.
492  *
493  * @param connection the connection
494  * @param base_service the base service name
495  * @returns #FALSE if not enough memory
496  */
497 dbus_bool_t
498 dbus_bus_set_base_service (DBusConnection *connection,
499                            const char     *base_service)
500 {
501   BusData *bd;
502
503   _dbus_return_val_if_fail (connection != NULL, FALSE);
504   _dbus_return_val_if_fail (base_service != NULL, FALSE);
505   
506   bd = ensure_bus_data (connection);
507   if (bd == NULL)
508     return FALSE;
509
510   _dbus_assert (bd->base_service == NULL);
511   
512   bd->base_service = _dbus_strdup (base_service);
513   return bd->base_service != NULL;
514 }
515
516 /**
517  * Gets the base service name of the connection.
518  * Only possible after the connection has been registered
519  * with the message bus.
520  *
521  * @param connection the connection
522  * @returns the base service name
523  */
524 const char*
525 dbus_bus_get_base_service (DBusConnection *connection)
526 {
527   BusData *bd;
528
529   _dbus_return_val_if_fail (connection != NULL, NULL);
530   
531   bd = ensure_bus_data (connection);
532   if (bd == NULL)
533     return NULL;
534   
535   return bd->base_service;
536 }
537
538 /**
539  * Asks the bus to try to acquire a certain service.
540  *
541  * @todo these docs are not complete, need to document the
542  * return value and flags
543  * 
544  * @todo if we get an error reply, it has to be converted into
545  * DBusError and returned
546  *
547  * @param connection the connection
548  * @param service_name the service name
549  * @param flags flags
550  * @param error location to store the error
551  * @returns a result code, -1 if error is set
552  */ 
553 int
554 dbus_bus_acquire_service (DBusConnection *connection,
555                           const char     *service_name,
556                           unsigned int    flags,
557                           DBusError      *error)
558 {
559   DBusMessage *message, *reply;
560   dbus_uint32_t service_result;
561
562   _dbus_return_val_if_fail (connection != NULL, 0);
563   _dbus_return_val_if_fail (service_name != NULL, 0);
564   _dbus_return_val_if_error_is_set (error, 0);
565   
566   message = dbus_message_new (DBUS_MESSAGE_ACQUIRE_SERVICE,
567                               DBUS_SERVICE_DBUS);
568
569
570   if (message == NULL)
571     {
572       _DBUS_SET_OOM (error);
573       return -1;
574     }
575  
576   if (!dbus_message_append_args (message,
577                                  DBUS_TYPE_STRING, service_name,
578                                  DBUS_TYPE_UINT32, flags,
579                                  0))
580     {
581       dbus_message_unref (message);
582       _DBUS_SET_OOM (error);
583       return -1;
584     }
585   
586   reply = dbus_connection_send_with_reply_and_block (connection, message, -1,
587                                                      error);
588   
589   dbus_message_unref (message);
590   
591   if (reply == NULL)
592     {
593       _DBUS_ASSERT_ERROR_IS_SET (error);
594       return -1;
595     }  
596
597   if (dbus_set_error_from_message (error, reply))
598     {
599       _DBUS_ASSERT_ERROR_IS_SET (error);
600       dbus_message_unref (reply);
601       return -1;
602     }
603   
604   if (!dbus_message_get_args (reply, error,
605                               DBUS_TYPE_UINT32, &service_result,
606                               0))
607     {
608       _DBUS_ASSERT_ERROR_IS_SET (error);
609       dbus_message_unref (reply);
610       return -1;
611     }
612
613   dbus_message_unref (reply);
614   
615   return service_result;
616 }
617
618 /**
619  * Checks whether a certain service exists.
620  *
621  * @todo the SERVICE_EXISTS message should use BOOLEAN not UINT32
622  *
623  * @param connection the connection
624  * @param service_name the service name
625  * @param error location to store any errors
626  * @returns #TRUE if the service exists, #FALSE if not or on error
627  */
628 dbus_bool_t
629 dbus_bus_service_exists (DBusConnection *connection,
630                          const char     *service_name,
631                          DBusError      *error)
632 {
633   DBusMessage *message, *reply;
634   unsigned int exists;
635
636   _dbus_return_val_if_fail (connection != NULL, FALSE);
637   _dbus_return_val_if_fail (service_name != NULL, FALSE);
638   _dbus_return_val_if_error_is_set (error, FALSE);
639   
640   message = dbus_message_new (DBUS_MESSAGE_SERVICE_EXISTS,
641                               DBUS_SERVICE_DBUS);
642   if (message == NULL)
643     {
644       _DBUS_SET_OOM (error);
645       return FALSE;
646     }
647   
648   if (!dbus_message_append_args (message,
649                                  DBUS_TYPE_STRING, service_name,
650                                  0))
651     {
652       dbus_message_unref (message);
653       _DBUS_SET_OOM (error);
654       return FALSE;
655     }
656   
657   reply = dbus_connection_send_with_reply_and_block (connection, message, -1, error);
658   dbus_message_unref (message);
659
660   if (reply == NULL)
661     {
662       _DBUS_ASSERT_ERROR_IS_SET (error);
663       return FALSE;
664     }
665
666   if (!dbus_message_get_args (reply, error,
667                               DBUS_TYPE_UINT32, &exists,
668                               0))
669     {
670       _DBUS_ASSERT_ERROR_IS_SET (error);
671       return FALSE;
672     }
673   
674   return (exists != FALSE);
675 }
676
677 /** @} */