2003-03-25 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   const char *c_name;
111   BusService *service;
112   
113   _dbus_string_get_const_data (service_name, &c_name);
114
115   service = _dbus_hash_table_lookup_string (registry->service_hash,
116                                             c_name);
117
118   return service;
119 }
120
121 BusService*
122 bus_registry_ensure (BusRegistry               *registry,
123                      const DBusString          *service_name,
124                      DBusConnection            *owner_if_created,
125                      BusTransaction            *transaction,
126                      DBusError                 *error)
127 {
128   const char *c_name;
129   BusService *service;
130
131   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
132   
133   _dbus_assert (owner_if_created != NULL);
134   _dbus_assert (transaction != NULL);
135   
136   _dbus_string_get_const_data (service_name, &c_name);
137
138   service = _dbus_hash_table_lookup_string (registry->service_hash,
139                                             c_name);
140   if (service != NULL)
141     return service;
142   
143   service = _dbus_mem_pool_alloc (registry->service_pool);
144   if (service == NULL)
145     {
146       BUS_SET_OOM (error);
147       return NULL;
148     }
149
150   service->registry = registry;  
151
152   service->name = _dbus_strdup (c_name);
153   if (service->name == NULL)
154     {
155       _dbus_mem_pool_dealloc (registry->service_pool, service);
156       BUS_SET_OOM (error);
157       return NULL;
158     }
159
160   if (!bus_driver_send_service_created (service->name, transaction, error))
161     {
162       dbus_free (service->name);
163       _dbus_mem_pool_dealloc (registry->service_pool, service);
164       return NULL;
165     }
166
167   if (!bus_activation_service_created (bus_context_get_activation (registry->context),
168                                        service->name, error))
169     {
170       dbus_free (service->name);
171       _dbus_mem_pool_dealloc (registry->service_pool, service);
172       return NULL;
173     }
174   
175   if (!bus_service_add_owner (service, owner_if_created,
176                               transaction, error))
177     {
178       dbus_free (service->name);
179       _dbus_mem_pool_dealloc (registry->service_pool, service);
180       return NULL;
181     }
182   
183   if (!_dbus_hash_table_insert_string (registry->service_hash,
184                                        service->name,
185                                        service))
186     {
187       bus_connection_remove_owned_service (owner_if_created,
188                                            service);
189       _dbus_list_clear (&service->owners);
190       dbus_free (service->name);
191       _dbus_mem_pool_dealloc (registry->service_pool, service);
192       BUS_SET_OOM (error);
193       return NULL;
194     }
195   
196   return service;
197 }
198
199 void
200 bus_registry_foreach (BusRegistry               *registry,
201                       BusServiceForeachFunction  function,
202                       void                      *data)
203 {
204   DBusHashIter iter;
205   
206   _dbus_hash_iter_init (registry->service_hash, &iter);
207   while (_dbus_hash_iter_next (&iter))
208     {
209       BusService *service = _dbus_hash_iter_get_value (&iter);
210
211       (* function) (service, data);
212     }
213 }
214
215 dbus_bool_t
216 bus_registry_list_services (BusRegistry *registry,
217                             char      ***listp,
218                             int         *array_len)
219 {
220   int i, j, len;
221   char **retval;
222   DBusHashIter iter;
223    
224   len = _dbus_hash_table_get_n_entries (registry->service_hash);
225   retval = dbus_new (char *, len + 1);
226
227   if (retval == NULL)
228     return FALSE;
229
230   _dbus_hash_iter_init (registry->service_hash, &iter);
231   i = 0;
232   while (_dbus_hash_iter_next (&iter))
233     {
234       BusService *service = _dbus_hash_iter_get_value (&iter);
235
236       retval[i] = _dbus_strdup (service->name);
237       if (retval[i] == NULL)
238         goto error;
239
240       i++;
241     }
242
243   retval[i] = NULL;
244   
245   if (array_len)
246     *array_len = len;
247   
248   *listp = retval;
249   return TRUE;
250   
251  error:
252   for (j = 0; j < i; j++)
253     dbus_free (retval[i]);
254   dbus_free (retval);
255
256   return FALSE;
257 }
258
259 dbus_bool_t
260 bus_service_add_owner (BusService     *service,
261                        DBusConnection *owner,
262                        BusTransaction *transaction,
263                        DBusError      *error)
264 {
265   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
266   
267  /* Send service acquired message first, OOM will result
268   * in cancelling the transaction
269   */
270   if (service->owners == NULL)
271     {
272       if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
273         return FALSE;
274     }
275   
276   if (!_dbus_list_append (&service->owners,
277                           owner))
278     {
279       BUS_SET_OOM (error);
280       return FALSE;
281     }
282
283   if (!bus_connection_add_owned_service (owner, service))
284     {
285       _dbus_list_remove_last (&service->owners, owner);
286       BUS_SET_OOM (error);
287       return FALSE;
288     }
289   
290   return TRUE;
291 }
292
293 dbus_bool_t
294 bus_service_remove_owner (BusService     *service,
295                           DBusConnection *owner,
296                           BusTransaction *transaction,
297                           DBusError      *error)
298 {
299   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
300   
301   /* We send out notifications before we do any work we
302    * might have to undo if the notification-sending failed
303    */
304   
305   /* Send service lost message */
306   if (bus_service_get_primary_owner (service) == owner)
307     {
308       if (!bus_driver_send_service_lost (owner, service->name,
309                                          transaction, error))
310         return FALSE;
311     }
312
313   if (service->owners == NULL)
314     {
315       _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
316     }
317   else if (_dbus_list_length_is_one (&service->owners))
318     {
319       /* We are the only owner - send service deleted */
320       if (!bus_driver_send_service_deleted (service->name,
321                                             transaction, error))
322         return FALSE;
323     }
324   else
325     {
326       DBusList *link;
327       link = _dbus_list_get_first (&service->owners);
328       _dbus_assert (link != NULL);
329       link = _dbus_list_get_next_link (&service->owners, link);
330
331       if (link != NULL)
332         {
333           /* This will be our new owner */
334           if (!bus_driver_send_service_acquired (link->data,
335                                                  service->name,
336                                                  transaction,
337                                                  error))
338             return FALSE;
339         }
340     }
341   
342   _dbus_list_remove_last (&service->owners, owner);
343   bus_connection_remove_owned_service (owner, service);
344
345   if (service->owners == NULL)
346     {
347       /* Delete service (already sent message that it was deleted above) */
348       _dbus_hash_table_remove_string (service->registry->service_hash,
349                                       service->name);
350       
351       dbus_free (service->name);
352       _dbus_mem_pool_dealloc (service->registry->service_pool, service);
353     }
354
355   return TRUE;
356 }
357
358 DBusConnection*
359 bus_service_get_primary_owner (BusService *service)
360 {
361   return _dbus_list_get_first (&service->owners);
362 }
363
364 const char*
365 bus_service_get_name (BusService *service)
366 {
367   return service->name;
368 }
369
370 void
371 bus_service_set_prohibit_replacement (BusService  *service,
372                                       dbus_bool_t  prohibit_replacement)
373 {
374   service->prohibit_replacement = prohibit_replacement != FALSE;
375 }
376
377 dbus_bool_t
378 bus_service_get_prohibit_replacement (BusService *service)
379 {
380   return service->prohibit_replacement;
381 }
382
383 dbus_bool_t
384 bus_service_has_owner (BusService     *service,
385                        DBusConnection *owner)
386 {
387   DBusList *link;
388
389   link = _dbus_list_get_first_link (&service->owners);
390   
391   while (link != NULL)
392     {
393       if (link->data == owner)
394         return TRUE;
395       
396       link = _dbus_list_get_next_link (&service->owners, link);
397     }
398
399   return FALSE;
400 }