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