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