2003-03-31 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / services.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* services.c  Service management
3  *
4  * Copyright (C) 2003  Red Hat, Inc.
5  * Copyright (C) 2003  CodeFactory AB
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 #include <dbus/dbus-hash.h>
25 #include <dbus/dbus-list.h>
26 #include <dbus/dbus-mempool.h>
27
28 #include "driver.h"
29 #include "services.h"
30 #include "connection.h"
31 #include "utils.h"
32 #include "activation.h"
33
34 struct BusService
35 {
36   BusRegistry *registry;
37   char *name;
38   DBusList *owners;
39   
40   unsigned int prohibit_replacement : 1;
41 };
42
43 struct BusRegistry
44 {
45   int refcount;
46
47   BusContext *context;
48   
49   DBusHashTable *service_hash;
50   DBusMemPool   *service_pool;
51 };
52
53 BusRegistry*
54 bus_registry_new (BusContext *context)
55 {
56   BusRegistry *registry;
57
58   registry = dbus_new0 (BusRegistry, 1);
59   if (registry == NULL)
60     return NULL;
61
62   registry->refcount = 1;
63   registry->context = context;
64   
65   registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
66                                                  NULL, NULL);
67   if (registry->service_hash == NULL)
68     goto failed;
69   
70   registry->service_pool = _dbus_mem_pool_new (sizeof (BusService),
71                                                TRUE);
72   if (registry->service_pool == NULL)
73     goto failed;
74
75   return registry;
76
77  failed:
78   bus_registry_unref (registry);
79   return NULL;
80 }
81
82 void
83 bus_registry_ref (BusRegistry *registry)
84 {
85   _dbus_assert (registry->refcount > 0);
86   registry->refcount += 1;
87 }
88
89 void
90 bus_registry_unref  (BusRegistry *registry)
91 {
92   _dbus_assert (registry->refcount > 0);
93   registry->refcount -= 1;
94
95   if (registry->refcount == 0)
96     {
97       if (registry->service_hash)
98         _dbus_hash_table_unref (registry->service_hash);
99       if (registry->service_pool)
100         _dbus_mem_pool_free (registry->service_pool);
101
102       dbus_free (registry);
103     }
104 }
105
106 BusService*
107 bus_registry_lookup (BusRegistry      *registry,
108                      const DBusString *service_name)
109 {
110   BusService *service;
111
112   service = _dbus_hash_table_lookup_string (registry->service_hash,
113                                             _dbus_string_get_const_data (service_name));
114
115   return service;
116 }
117
118 BusService*
119 bus_registry_ensure (BusRegistry               *registry,
120                      const DBusString          *service_name,
121                      DBusConnection            *owner_if_created,
122                      BusTransaction            *transaction,
123                      DBusError                 *error)
124 {
125   BusService *service;
126
127   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
128   
129   _dbus_assert (owner_if_created != NULL);
130   _dbus_assert (transaction != NULL);
131
132   service = _dbus_hash_table_lookup_string (registry->service_hash,
133                                             _dbus_string_get_const_data (service_name));
134   if (service != NULL)
135     return service;
136   
137   service = _dbus_mem_pool_alloc (registry->service_pool);
138   if (service == NULL)
139     {
140       BUS_SET_OOM (error);
141       return NULL;
142     }
143
144   service->registry = registry;  
145
146   if (!_dbus_string_copy_data (service_name, &service->name))
147     {
148       _dbus_mem_pool_dealloc (registry->service_pool, service);
149       BUS_SET_OOM (error);
150       return NULL;
151     }
152
153   if (!bus_driver_send_service_created (service->name, transaction, error))
154     {
155       dbus_free (service->name);
156       _dbus_mem_pool_dealloc (registry->service_pool, service);
157       return NULL;
158     }
159
160   if (!bus_activation_service_created (bus_context_get_activation (registry->context),
161                                        service->name, error))
162     {
163       dbus_free (service->name);
164       _dbus_mem_pool_dealloc (registry->service_pool, service);
165       return NULL;
166     }
167   
168   if (!bus_service_add_owner (service, owner_if_created,
169                               transaction, error))
170     {
171       dbus_free (service->name);
172       _dbus_mem_pool_dealloc (registry->service_pool, service);
173       return NULL;
174     }
175   
176   if (!_dbus_hash_table_insert_string (registry->service_hash,
177                                        service->name,
178                                        service))
179     {
180       bus_connection_remove_owned_service (owner_if_created,
181                                            service);
182       _dbus_list_clear (&service->owners);
183       dbus_free (service->name);
184       _dbus_mem_pool_dealloc (registry->service_pool, service);
185       BUS_SET_OOM (error);
186       return NULL;
187     }
188   
189   return service;
190 }
191
192 void
193 bus_registry_foreach (BusRegistry               *registry,
194                       BusServiceForeachFunction  function,
195                       void                      *data)
196 {
197   DBusHashIter iter;
198   
199   _dbus_hash_iter_init (registry->service_hash, &iter);
200   while (_dbus_hash_iter_next (&iter))
201     {
202       BusService *service = _dbus_hash_iter_get_value (&iter);
203
204       (* function) (service, data);
205     }
206 }
207
208 dbus_bool_t
209 bus_registry_list_services (BusRegistry *registry,
210                             char      ***listp,
211                             int         *array_len)
212 {
213   int i, j, len;
214   char **retval;
215   DBusHashIter iter;
216    
217   len = _dbus_hash_table_get_n_entries (registry->service_hash);
218   retval = dbus_new (char *, len + 1);
219
220   if (retval == NULL)
221     return FALSE;
222
223   _dbus_hash_iter_init (registry->service_hash, &iter);
224   i = 0;
225   while (_dbus_hash_iter_next (&iter))
226     {
227       BusService *service = _dbus_hash_iter_get_value (&iter);
228
229       retval[i] = _dbus_strdup (service->name);
230       if (retval[i] == NULL)
231         goto error;
232
233       i++;
234     }
235
236   retval[i] = NULL;
237   
238   if (array_len)
239     *array_len = len;
240   
241   *listp = retval;
242   return TRUE;
243   
244  error:
245   for (j = 0; j < i; j++)
246     dbus_free (retval[i]);
247   dbus_free (retval);
248
249   return FALSE;
250 }
251
252 dbus_bool_t
253 bus_service_add_owner (BusService     *service,
254                        DBusConnection *owner,
255                        BusTransaction *transaction,
256                        DBusError      *error)
257 {
258   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
259   
260  /* Send service acquired message first, OOM will result
261   * in cancelling the transaction
262   */
263   if (service->owners == NULL)
264     {
265       if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
266         return FALSE;
267     }
268   
269   if (!_dbus_list_append (&service->owners,
270                           owner))
271     {
272       BUS_SET_OOM (error);
273       return FALSE;
274     }
275
276   if (!bus_connection_add_owned_service (owner, service))
277     {
278       _dbus_list_remove_last (&service->owners, owner);
279       BUS_SET_OOM (error);
280       return FALSE;
281     }
282   
283   return TRUE;
284 }
285
286 dbus_bool_t
287 bus_service_remove_owner (BusService     *service,
288                           DBusConnection *owner,
289                           BusTransaction *transaction,
290                           DBusError      *error)
291 {
292   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
293   
294   /* We send out notifications before we do any work we
295    * might have to undo if the notification-sending failed
296    */
297   
298   /* Send service lost message */
299   if (bus_service_get_primary_owner (service) == owner)
300     {
301       if (!bus_driver_send_service_lost (owner, service->name,
302                                          transaction, error))
303         return FALSE;
304     }
305
306   if (service->owners == NULL)
307     {
308       _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
309     }
310   else if (_dbus_list_length_is_one (&service->owners))
311     {
312       /* We are the only owner - send service deleted */
313       if (!bus_driver_send_service_deleted (service->name,
314                                             transaction, error))
315         return FALSE;
316     }
317   else
318     {
319       DBusList *link;
320       link = _dbus_list_get_first (&service->owners);
321       _dbus_assert (link != NULL);
322       link = _dbus_list_get_next_link (&service->owners, link);
323
324       if (link != NULL)
325         {
326           /* This will be our new owner */
327           if (!bus_driver_send_service_acquired (link->data,
328                                                  service->name,
329                                                  transaction,
330                                                  error))
331             return FALSE;
332         }
333     }
334   
335   _dbus_list_remove_last (&service->owners, owner);
336   bus_connection_remove_owned_service (owner, service);
337
338   if (service->owners == NULL)
339     {
340       /* Delete service (already sent message that it was deleted above) */
341       _dbus_hash_table_remove_string (service->registry->service_hash,
342                                       service->name);
343       
344       dbus_free (service->name);
345       _dbus_mem_pool_dealloc (service->registry->service_pool, service);
346     }
347
348   return TRUE;
349 }
350
351 DBusConnection*
352 bus_service_get_primary_owner (BusService *service)
353 {
354   return _dbus_list_get_first (&service->owners);
355 }
356
357 const char*
358 bus_service_get_name (BusService *service)
359 {
360   return service->name;
361 }
362
363 void
364 bus_service_set_prohibit_replacement (BusService  *service,
365                                       dbus_bool_t  prohibit_replacement)
366 {
367   service->prohibit_replacement = prohibit_replacement != FALSE;
368 }
369
370 dbus_bool_t
371 bus_service_get_prohibit_replacement (BusService *service)
372 {
373   return service->prohibit_replacement;
374 }
375
376 dbus_bool_t
377 bus_service_has_owner (BusService     *service,
378                        DBusConnection *owner)
379 {
380   DBusList *link;
381
382   link = _dbus_list_get_first_link (&service->owners);
383   
384   while (link != NULL)
385     {
386       if (link->data == owner)
387         return TRUE;
388       
389       link = _dbus_list_get_next_link (&service->owners, link);
390     }
391
392   return FALSE;
393 }