1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* services.c Service management
4 * Copyright (C) 2003 Red Hat, Inc.
5 * Copyright (C) 2003 CodeFactory AB
7 * Licensed under the Academic Free License version 1.2
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.
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.
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
24 #include <dbus/dbus-hash.h>
25 #include <dbus/dbus-list.h>
26 #include <dbus/dbus-mempool.h>
30 #include "connection.h"
32 #include "activation.h"
38 BusRegistry *registry;
42 unsigned int prohibit_replacement : 1;
51 DBusHashTable *service_hash;
52 DBusMemPool *service_pool;
56 bus_registry_new (BusContext *context)
58 BusRegistry *registry;
60 registry = dbus_new0 (BusRegistry, 1);
64 registry->refcount = 1;
65 registry->context = context;
67 registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
69 if (registry->service_hash == NULL)
72 registry->service_pool = _dbus_mem_pool_new (sizeof (BusService),
74 if (registry->service_pool == NULL)
80 bus_registry_unref (registry);
85 bus_registry_ref (BusRegistry *registry)
87 _dbus_assert (registry->refcount > 0);
88 registry->refcount += 1;
92 bus_registry_unref (BusRegistry *registry)
94 _dbus_assert (registry->refcount > 0);
95 registry->refcount -= 1;
97 if (registry->refcount == 0)
99 if (registry->service_hash)
100 _dbus_hash_table_unref (registry->service_hash);
101 if (registry->service_pool)
102 _dbus_mem_pool_free (registry->service_pool);
104 dbus_free (registry);
109 bus_registry_lookup (BusRegistry *registry,
110 const DBusString *service_name)
114 service = _dbus_hash_table_lookup_string (registry->service_hash,
115 _dbus_string_get_const_data (service_name));
121 bus_registry_ensure (BusRegistry *registry,
122 const DBusString *service_name,
123 DBusConnection *owner_if_created,
124 BusTransaction *transaction,
129 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
131 _dbus_assert (owner_if_created != NULL);
132 _dbus_assert (transaction != NULL);
134 service = _dbus_hash_table_lookup_string (registry->service_hash,
135 _dbus_string_get_const_data (service_name));
139 service = _dbus_mem_pool_alloc (registry->service_pool);
146 service->registry = registry;
147 service->refcount = 1;
149 if (!_dbus_string_copy_data (service_name, &service->name))
151 _dbus_mem_pool_dealloc (registry->service_pool, service);
156 if (!bus_driver_send_service_created (service->name, transaction, error))
158 bus_service_unref (service);
162 if (!bus_activation_service_created (bus_context_get_activation (registry->context),
163 service->name, transaction, error))
165 bus_service_unref (service);
169 if (!bus_service_add_owner (service, owner_if_created,
172 bus_service_unref (service);
176 if (!_dbus_hash_table_insert_string (registry->service_hash,
180 /* The add_owner gets reverted on transaction cancel */
189 bus_registry_foreach (BusRegistry *registry,
190 BusServiceForeachFunction function,
195 _dbus_hash_iter_init (registry->service_hash, &iter);
196 while (_dbus_hash_iter_next (&iter))
198 BusService *service = _dbus_hash_iter_get_value (&iter);
200 (* function) (service, data);
205 bus_registry_list_services (BusRegistry *registry,
213 len = _dbus_hash_table_get_n_entries (registry->service_hash);
214 retval = dbus_new (char *, len + 1);
219 _dbus_hash_iter_init (registry->service_hash, &iter);
221 while (_dbus_hash_iter_next (&iter))
223 BusService *service = _dbus_hash_iter_get_value (&iter);
225 retval[i] = _dbus_strdup (service->name);
226 if (retval[i] == NULL)
241 for (j = 0; j < i; j++)
242 dbus_free (retval[i]);
249 bus_registry_acquire_service (BusRegistry *registry,
250 DBusConnection *connection,
251 const DBusString *service_name,
253 dbus_uint32_t *result,
254 BusTransaction *transaction,
258 DBusConnection *old_owner;
259 DBusConnection *current_owner;
264 if (_dbus_string_get_length (service_name) == 0)
266 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
267 "Zero-length service name is not allowed");
269 _dbus_verbose ("Attempt to acquire zero-length service name\n");
274 if (_dbus_string_get_byte (service_name, 0) == ':')
276 /* Not allowed; only base services can start with ':' */
277 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
278 "Cannot acquire a service starting with ':' such as \"%s\"",
279 _dbus_string_get_const_data (service_name));
281 _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
282 _dbus_string_get_const_data (service_name));
287 service = bus_registry_lookup (registry, service_name);
290 old_owner = bus_service_get_primary_owner (service);
296 service = bus_registry_ensure (registry,
297 service_name, connection, transaction, error);
302 current_owner = bus_service_get_primary_owner (service);
304 if (old_owner == NULL)
306 _dbus_assert (current_owner == connection);
308 bus_service_set_prohibit_replacement (service,
309 (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));
311 *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
313 else if (old_owner == connection)
314 *result = DBUS_SERVICE_REPLY_ALREADY_OWNER;
315 else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
316 *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
317 else if (bus_service_get_prohibit_replacement (service))
319 /* Queue the connection */
320 if (!bus_service_add_owner (service, connection,
324 *result = DBUS_SERVICE_REPLY_IN_QUEUE;
328 /* Replace the current owner */
330 /* We enqueue the new owner and remove the first one because
331 * that will cause ServiceAcquired and ServiceLost messages to
335 if (!bus_service_add_owner (service, connection,
339 if (!bus_service_remove_owner (service, old_owner,
343 _dbus_assert (connection == bus_service_get_primary_owner (service));
344 *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
354 bus_service_unlink_owner (BusService *service,
355 DBusConnection *owner)
357 _dbus_list_remove_last (&service->owners, owner);
358 bus_connection_remove_owned_service (owner, service);
362 bus_service_unlink (BusService *service)
364 _dbus_assert (service->owners == NULL);
366 /* the service may not be in the hash, if
367 * the failure causing transaction cancel
368 * was in the right place, but that's OK
370 _dbus_hash_table_remove_string (service->registry->service_hash,
373 bus_service_unref (service);
377 bus_service_relink (BusService *service,
378 DBusPreallocatedHash *preallocated)
380 _dbus_assert (service->owners == NULL);
381 _dbus_assert (preallocated != NULL);
383 _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
388 bus_service_ref (service);
393 DBusConnection *connection;
395 } OwnershipCancelData;
398 cancel_ownership (void *data)
400 OwnershipCancelData *d = data;
402 /* We don't need to send messages notifying of these
403 * changes, since we're reverting something that was
404 * cancelled (effectively never really happened)
406 bus_service_unlink_owner (d->service, d->connection);
408 if (d->service->owners == NULL)
409 bus_service_unlink (d->service);
413 free_ownership_cancel_data (void *data)
415 OwnershipCancelData *d = data;
417 dbus_connection_unref (d->connection);
418 bus_service_unref (d->service);
424 add_cancel_ownership_to_transaction (BusTransaction *transaction,
426 DBusConnection *connection)
428 OwnershipCancelData *d;
430 d = dbus_new (OwnershipCancelData, 1);
434 d->service = service;
435 d->connection = connection;
437 if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
438 free_ownership_cancel_data))
444 bus_service_ref (d->service);
445 dbus_connection_ref (d->connection);
450 /* this function is self-cancelling if you cancel the transaction */
452 bus_service_add_owner (BusService *service,
453 DBusConnection *owner,
454 BusTransaction *transaction,
457 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
459 /* Send service acquired message first, OOM will result
460 * in cancelling the transaction
462 if (service->owners == NULL)
464 if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
468 if (!_dbus_list_append (&service->owners,
475 if (!bus_connection_add_owned_service (owner, service))
477 _dbus_list_remove_last (&service->owners, owner);
482 if (!add_cancel_ownership_to_transaction (transaction,
486 bus_service_unlink_owner (service, owner);
496 DBusConnection *connection;
498 DBusConnection *before_connection; /* restore to position before this connection in owners list */
499 DBusList *connection_link;
500 DBusList *service_link;
501 DBusPreallocatedHash *hash_entry;
502 } OwnershipRestoreData;
505 restore_ownership (void *data)
507 OwnershipRestoreData *d = data;
510 _dbus_assert (d->service_link != NULL);
511 _dbus_assert (d->connection_link != NULL);
513 if (d->service->owners == NULL)
515 _dbus_assert (d->hash_entry != NULL);
516 bus_service_relink (d->service, d->hash_entry);
520 _dbus_assert (d->hash_entry == NULL);
523 /* We don't need to send messages notifying of these
524 * changes, since we're reverting something that was
525 * cancelled (effectively never really happened)
527 link = _dbus_list_get_first_link (&d->service->owners);
530 if (link->data == d->before_connection)
533 link = _dbus_list_get_next_link (&d->service->owners, link);
536 _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
538 /* Note that removing then restoring this changes the order in which
539 * ServiceDeleted messages are sent on destruction of the
540 * connection. This should be OK as the only guarantee there is
541 * that the base service is destroyed last, and we never even
542 * tentatively remove the base service.
544 bus_connection_add_owned_service_link (d->connection, d->service_link);
546 d->hash_entry = NULL;
547 d->service_link = NULL;
548 d->connection_link = NULL;
552 free_ownership_restore_data (void *data)
554 OwnershipRestoreData *d = data;
557 _dbus_list_free_link (d->service_link);
558 if (d->connection_link)
559 _dbus_list_free_link (d->connection_link);
561 _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
564 dbus_connection_unref (d->connection);
565 bus_service_unref (d->service);
571 add_restore_ownership_to_transaction (BusTransaction *transaction,
573 DBusConnection *connection)
575 OwnershipRestoreData *d;
578 d = dbus_new (OwnershipRestoreData, 1);
582 d->service = service;
583 d->connection = connection;
584 d->service_link = _dbus_list_alloc_link (service);
585 d->connection_link = _dbus_list_alloc_link (connection);
586 d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
588 bus_service_ref (d->service);
589 dbus_connection_ref (d->connection);
591 d->before_connection = NULL;
592 link = _dbus_list_get_first_link (&service->owners);
595 if (link->data == connection)
597 link = _dbus_list_get_next_link (&service->owners, link);
600 d->before_connection = link->data;
605 link = _dbus_list_get_next_link (&service->owners, link);
608 if (d->service_link == NULL ||
609 d->connection_link == NULL ||
610 d->hash_entry == NULL ||
611 !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
612 free_ownership_restore_data))
614 free_ownership_restore_data (d);
621 /* this function is self-cancelling if you cancel the transaction */
623 bus_service_remove_owner (BusService *service,
624 DBusConnection *owner,
625 BusTransaction *transaction,
628 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
630 /* We send out notifications before we do any work we
631 * might have to undo if the notification-sending failed
634 /* Send service lost message */
635 if (bus_service_get_primary_owner (service) == owner)
637 if (!bus_driver_send_service_lost (owner, service->name,
642 if (service->owners == NULL)
644 _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
646 else if (_dbus_list_length_is_one (&service->owners))
648 if (!bus_driver_send_service_deleted (service->name,
655 link = _dbus_list_get_first (&service->owners);
656 _dbus_assert (link != NULL);
657 link = _dbus_list_get_next_link (&service->owners, link);
659 _dbus_assert (link != NULL);
661 /* This will be our new owner */
662 if (!bus_driver_send_service_acquired (link->data,
669 if (!add_restore_ownership_to_transaction (transaction, service, owner))
675 bus_service_unlink_owner (service, owner);
677 if (service->owners == NULL)
678 bus_service_unlink (service);
684 bus_service_ref (BusService *service)
686 _dbus_assert (service->refcount > 0);
688 service->refcount += 1;
692 bus_service_unref (BusService *service)
694 _dbus_assert (service->refcount > 0);
696 service->refcount -= 1;
698 if (service->refcount == 0)
700 _dbus_assert (service->owners == NULL);
702 dbus_free (service->name);
703 _dbus_mem_pool_dealloc (service->registry->service_pool, service);
708 bus_service_get_primary_owner (BusService *service)
710 return _dbus_list_get_first (&service->owners);
714 bus_service_get_name (BusService *service)
716 return service->name;
720 bus_service_set_prohibit_replacement (BusService *service,
721 dbus_bool_t prohibit_replacement)
723 service->prohibit_replacement = prohibit_replacement != FALSE;
727 bus_service_get_prohibit_replacement (BusService *service)
729 return service->prohibit_replacement;
733 bus_service_has_owner (BusService *service,
734 DBusConnection *owner)
738 link = _dbus_list_get_first_link (&service->owners);
742 if (link->data == owner)
745 link = _dbus_list_get_next_link (&service->owners, link);