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 2.0
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"
39 BusRegistry *registry;
43 unsigned int prohibit_replacement : 1;
52 DBusHashTable *service_hash;
53 DBusMemPool *service_pool;
57 bus_registry_new (BusContext *context)
59 BusRegistry *registry;
61 registry = dbus_new0 (BusRegistry, 1);
65 registry->refcount = 1;
66 registry->context = context;
68 registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
70 if (registry->service_hash == NULL)
73 registry->service_pool = _dbus_mem_pool_new (sizeof (BusService),
75 if (registry->service_pool == NULL)
81 bus_registry_unref (registry);
86 bus_registry_ref (BusRegistry *registry)
88 _dbus_assert (registry->refcount > 0);
89 registry->refcount += 1;
95 bus_registry_unref (BusRegistry *registry)
97 _dbus_assert (registry->refcount > 0);
98 registry->refcount -= 1;
100 if (registry->refcount == 0)
102 if (registry->service_hash)
103 _dbus_hash_table_unref (registry->service_hash);
104 if (registry->service_pool)
105 _dbus_mem_pool_free (registry->service_pool);
107 dbus_free (registry);
112 bus_registry_lookup (BusRegistry *registry,
113 const DBusString *service_name)
117 service = _dbus_hash_table_lookup_string (registry->service_hash,
118 _dbus_string_get_const_data (service_name));
124 bus_registry_ensure (BusRegistry *registry,
125 const DBusString *service_name,
126 DBusConnection *owner_if_created,
127 BusTransaction *transaction,
132 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
134 _dbus_assert (owner_if_created != NULL);
135 _dbus_assert (transaction != NULL);
137 service = _dbus_hash_table_lookup_string (registry->service_hash,
138 _dbus_string_get_const_data (service_name));
142 service = _dbus_mem_pool_alloc (registry->service_pool);
149 service->registry = registry;
150 service->refcount = 1;
152 if (!_dbus_string_copy_data (service_name, &service->name))
154 _dbus_mem_pool_dealloc (registry->service_pool, service);
159 if (!bus_driver_send_service_created (service->name, transaction, error))
161 bus_service_unref (service);
165 if (!bus_activation_service_created (bus_context_get_activation (registry->context),
166 service->name, transaction, error))
168 bus_service_unref (service);
172 if (!bus_service_add_owner (service, owner_if_created,
175 bus_service_unref (service);
179 if (!_dbus_hash_table_insert_string (registry->service_hash,
183 /* The add_owner gets reverted on transaction cancel */
192 bus_registry_foreach (BusRegistry *registry,
193 BusServiceForeachFunction function,
198 _dbus_hash_iter_init (registry->service_hash, &iter);
199 while (_dbus_hash_iter_next (&iter))
201 BusService *service = _dbus_hash_iter_get_value (&iter);
203 (* function) (service, data);
208 bus_registry_list_services (BusRegistry *registry,
216 len = _dbus_hash_table_get_n_entries (registry->service_hash);
217 retval = dbus_new (char *, len + 1);
222 _dbus_hash_iter_init (registry->service_hash, &iter);
224 while (_dbus_hash_iter_next (&iter))
226 BusService *service = _dbus_hash_iter_get_value (&iter);
228 retval[i] = _dbus_strdup (service->name);
229 if (retval[i] == NULL)
244 for (j = 0; j < i; j++)
245 dbus_free (retval[i]);
252 bus_registry_acquire_service (BusRegistry *registry,
253 DBusConnection *connection,
254 const DBusString *service_name,
256 dbus_uint32_t *result,
257 BusTransaction *transaction,
261 DBusConnection *old_owner;
262 DBusConnection *current_owner;
263 BusClientPolicy *policy;
265 BusActivation *activation;
269 if (_dbus_string_get_length (service_name) == 0)
271 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
272 "Zero-length service name is not allowed");
274 _dbus_verbose ("Attempt to acquire zero-length service name\n");
279 if (_dbus_string_get_byte (service_name, 0) == ':')
281 /* Not allowed; only base services can start with ':' */
282 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
283 "Cannot acquire a service starting with ':' such as \"%s\"",
284 _dbus_string_get_const_data (service_name));
286 _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
287 _dbus_string_get_const_data (service_name));
292 policy = bus_connection_get_policy (connection);
293 _dbus_assert (policy != NULL);
295 if (!bus_client_policy_check_can_own (policy, connection,
298 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
299 "Connection \"%s\" is not allowed to own the service \"%s\" due "
300 "to security policies in the configuration file",
301 bus_connection_is_active (connection) ?
302 bus_connection_get_name (connection) :
308 if (bus_connection_get_n_services_owned (connection) >=
309 bus_context_get_max_services_per_connection (registry->context))
311 dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
312 "Connection \"%s\" is not allowed to own more services "
313 "(increase limits in configuration file if required)",
314 bus_connection_is_active (connection) ?
315 bus_connection_get_name (connection) :
320 service = bus_registry_lookup (registry, service_name);
323 old_owner = bus_service_get_primary_owner (service);
329 service = bus_registry_ensure (registry,
330 service_name, connection, transaction, error);
335 current_owner = bus_service_get_primary_owner (service);
337 if (old_owner == NULL)
339 _dbus_assert (current_owner == connection);
341 bus_service_set_prohibit_replacement (service,
342 (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));
344 *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
346 else if (old_owner == connection)
347 *result = DBUS_SERVICE_REPLY_ALREADY_OWNER;
348 else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
349 *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
350 else if (bus_service_get_prohibit_replacement (service))
352 /* Queue the connection */
353 if (!bus_service_add_owner (service, connection,
357 *result = DBUS_SERVICE_REPLY_IN_QUEUE;
361 /* Replace the current owner */
363 /* We enqueue the new owner and remove the first one because
364 * that will cause ServiceAcquired and ServiceLost messages to
368 if (!bus_service_add_owner (service, connection,
372 if (!bus_service_remove_owner (service, old_owner,
376 _dbus_assert (connection == bus_service_get_primary_owner (service));
377 *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
380 activation = bus_context_get_activation (registry->context);
381 retval = bus_activation_send_pending_auto_activation_messages (activation,
391 bus_service_unlink_owner (BusService *service,
392 DBusConnection *owner)
394 _dbus_list_remove_last (&service->owners, owner);
395 bus_connection_remove_owned_service (owner, service);
399 bus_service_unlink (BusService *service)
401 _dbus_assert (service->owners == NULL);
403 /* the service may not be in the hash, if
404 * the failure causing transaction cancel
405 * was in the right place, but that's OK
407 _dbus_hash_table_remove_string (service->registry->service_hash,
410 bus_service_unref (service);
414 bus_service_relink (BusService *service,
415 DBusPreallocatedHash *preallocated)
417 _dbus_assert (service->owners == NULL);
418 _dbus_assert (preallocated != NULL);
420 _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
425 bus_service_ref (service);
429 * Data used to represent an ownership cancellation in
434 DBusConnection *connection; /**< the connection */
435 BusService *service; /**< service to cancel ownership of */
436 } OwnershipCancelData;
439 cancel_ownership (void *data)
441 OwnershipCancelData *d = data;
443 /* We don't need to send messages notifying of these
444 * changes, since we're reverting something that was
445 * cancelled (effectively never really happened)
447 bus_service_unlink_owner (d->service, d->connection);
449 if (d->service->owners == NULL)
450 bus_service_unlink (d->service);
454 free_ownership_cancel_data (void *data)
456 OwnershipCancelData *d = data;
458 dbus_connection_unref (d->connection);
459 bus_service_unref (d->service);
465 add_cancel_ownership_to_transaction (BusTransaction *transaction,
467 DBusConnection *connection)
469 OwnershipCancelData *d;
471 d = dbus_new (OwnershipCancelData, 1);
475 d->service = service;
476 d->connection = connection;
478 if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
479 free_ownership_cancel_data))
485 bus_service_ref (d->service);
486 dbus_connection_ref (d->connection);
491 /* this function is self-cancelling if you cancel the transaction */
493 bus_service_add_owner (BusService *service,
494 DBusConnection *owner,
495 BusTransaction *transaction,
498 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
500 /* Send service acquired message first, OOM will result
501 * in cancelling the transaction
503 if (service->owners == NULL)
505 if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
509 if (!_dbus_list_append (&service->owners,
516 if (!bus_connection_add_owned_service (owner, service))
518 _dbus_list_remove_last (&service->owners, owner);
523 if (!add_cancel_ownership_to_transaction (transaction,
527 bus_service_unlink_owner (service, owner);
537 DBusConnection *connection;
539 DBusConnection *before_connection; /* restore to position before this connection in owners list */
540 DBusList *connection_link;
541 DBusList *service_link;
542 DBusPreallocatedHash *hash_entry;
543 } OwnershipRestoreData;
546 restore_ownership (void *data)
548 OwnershipRestoreData *d = data;
551 _dbus_assert (d->service_link != NULL);
552 _dbus_assert (d->connection_link != NULL);
554 if (d->service->owners == NULL)
556 _dbus_assert (d->hash_entry != NULL);
557 bus_service_relink (d->service, d->hash_entry);
561 _dbus_assert (d->hash_entry == NULL);
564 /* We don't need to send messages notifying of these
565 * changes, since we're reverting something that was
566 * cancelled (effectively never really happened)
568 link = _dbus_list_get_first_link (&d->service->owners);
571 if (link->data == d->before_connection)
574 link = _dbus_list_get_next_link (&d->service->owners, link);
577 _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
579 /* Note that removing then restoring this changes the order in which
580 * ServiceDeleted messages are sent on destruction of the
581 * connection. This should be OK as the only guarantee there is
582 * that the base service is destroyed last, and we never even
583 * tentatively remove the base service.
585 bus_connection_add_owned_service_link (d->connection, d->service_link);
587 d->hash_entry = NULL;
588 d->service_link = NULL;
589 d->connection_link = NULL;
593 free_ownership_restore_data (void *data)
595 OwnershipRestoreData *d = data;
598 _dbus_list_free_link (d->service_link);
599 if (d->connection_link)
600 _dbus_list_free_link (d->connection_link);
602 _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
605 dbus_connection_unref (d->connection);
606 bus_service_unref (d->service);
612 add_restore_ownership_to_transaction (BusTransaction *transaction,
614 DBusConnection *connection)
616 OwnershipRestoreData *d;
619 d = dbus_new (OwnershipRestoreData, 1);
623 d->service = service;
624 d->connection = connection;
625 d->service_link = _dbus_list_alloc_link (service);
626 d->connection_link = _dbus_list_alloc_link (connection);
627 d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
629 bus_service_ref (d->service);
630 dbus_connection_ref (d->connection);
632 d->before_connection = NULL;
633 link = _dbus_list_get_first_link (&service->owners);
636 if (link->data == connection)
638 link = _dbus_list_get_next_link (&service->owners, link);
641 d->before_connection = link->data;
646 link = _dbus_list_get_next_link (&service->owners, link);
649 if (d->service_link == NULL ||
650 d->connection_link == NULL ||
651 d->hash_entry == NULL ||
652 !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
653 free_ownership_restore_data))
655 free_ownership_restore_data (d);
662 /* this function is self-cancelling if you cancel the transaction */
664 bus_service_remove_owner (BusService *service,
665 DBusConnection *owner,
666 BusTransaction *transaction,
669 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
671 /* We send out notifications before we do any work we
672 * might have to undo if the notification-sending failed
675 /* Send service lost message */
676 if (bus_service_get_primary_owner (service) == owner)
678 if (!bus_driver_send_service_lost (owner, service->name,
683 if (service->owners == NULL)
685 _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
687 else if (_dbus_list_length_is_one (&service->owners))
689 if (!bus_driver_send_service_deleted (service->name,
696 link = _dbus_list_get_first (&service->owners);
697 _dbus_assert (link != NULL);
698 link = _dbus_list_get_next_link (&service->owners, link);
700 _dbus_assert (link != NULL);
702 /* This will be our new owner */
703 if (!bus_driver_send_service_acquired (link->data,
710 if (!add_restore_ownership_to_transaction (transaction, service, owner))
716 bus_service_unlink_owner (service, owner);
718 if (service->owners == NULL)
719 bus_service_unlink (service);
725 bus_service_ref (BusService *service)
727 _dbus_assert (service->refcount > 0);
729 service->refcount += 1;
735 bus_service_unref (BusService *service)
737 _dbus_assert (service->refcount > 0);
739 service->refcount -= 1;
741 if (service->refcount == 0)
743 _dbus_assert (service->owners == NULL);
745 dbus_free (service->name);
746 _dbus_mem_pool_dealloc (service->registry->service_pool, service);
751 bus_service_get_primary_owner (BusService *service)
753 return _dbus_list_get_first (&service->owners);
757 bus_service_get_name (BusService *service)
759 return service->name;
763 bus_service_set_prohibit_replacement (BusService *service,
764 dbus_bool_t prohibit_replacement)
766 service->prohibit_replacement = prohibit_replacement != FALSE;
770 bus_service_get_prohibit_replacement (BusService *service)
772 return service->prohibit_replacement;
776 bus_service_has_owner (BusService *service,
777 DBusConnection *owner)
781 link = _dbus_list_get_first_link (&service->owners);
785 if (link->data == owner)
788 link = _dbus_list_get_next_link (&service->owners, link);