services.c revision 90ed1d84588a84697051e643175452c50d682ece
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
34struct BusService
35{
36  BusRegistry *registry;
37  char *name;
38  DBusList *owners;
39
40  unsigned int prohibit_replacement : 1;
41};
42
43struct BusRegistry
44{
45  int refcount;
46
47  BusContext *context;
48
49  DBusHashTable *service_hash;
50  DBusMemPool   *service_pool;
51};
52
53BusRegistry*
54bus_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
82void
83bus_registry_ref (BusRegistry *registry)
84{
85  _dbus_assert (registry->refcount > 0);
86  registry->refcount += 1;
87}
88
89void
90bus_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
106BusService*
107bus_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
121BusService*
122bus_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
199void
200bus_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
215dbus_bool_t
216bus_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
259dbus_bool_t
260bus_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
293dbus_bool_t
294bus_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
358DBusConnection*
359bus_service_get_primary_owner (BusService *service)
360{
361  return _dbus_list_get_first (&service->owners);
362}
363
364const char*
365bus_service_get_name (BusService *service)
366{
367  return service->name;
368}
369
370void
371bus_service_set_prohibit_replacement (BusService  *service,
372				      dbus_bool_t  prohibit_replacement)
373{
374  service->prohibit_replacement = prohibit_replacement != FALSE;
375}
376
377dbus_bool_t
378bus_service_get_prohibit_replacement (BusService *service)
379{
380  return service->prohibit_replacement;
381}
382
383dbus_bool_t
384bus_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}
401