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