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.1
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>
27 #include <dbus/dbus-marshal-validate.h>
31 #include "connection.h"
33 #include "activation.h"
42 BusRegistry *registry;
46 unsigned int prohibit_replacement : 1;
55 DBusHashTable *service_hash;
56 DBusMemPool *service_pool;
58 DBusHashTable *service_sid_table;
62 bus_registry_new (BusContext *context)
64 BusRegistry *registry;
66 registry = dbus_new0 (BusRegistry, 1);
70 registry->refcount = 1;
71 registry->context = context;
73 registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
75 if (registry->service_hash == NULL)
78 registry->service_pool = _dbus_mem_pool_new (sizeof (BusService),
80 if (registry->service_pool == NULL)
83 registry->service_sid_table = NULL;
88 bus_registry_unref (registry);
93 bus_registry_ref (BusRegistry *registry)
95 _dbus_assert (registry->refcount > 0);
96 registry->refcount += 1;
102 bus_registry_unref (BusRegistry *registry)
104 _dbus_assert (registry->refcount > 0);
105 registry->refcount -= 1;
107 if (registry->refcount == 0)
109 if (registry->service_hash)
110 _dbus_hash_table_unref (registry->service_hash);
111 if (registry->service_pool)
112 _dbus_mem_pool_free (registry->service_pool);
113 if (registry->service_sid_table)
114 _dbus_hash_table_unref (registry->service_sid_table);
116 dbus_free (registry);
121 bus_registry_lookup (BusRegistry *registry,
122 const DBusString *service_name)
126 service = _dbus_hash_table_lookup_string (registry->service_hash,
127 _dbus_string_get_const_data (service_name));
133 bus_registry_ensure (BusRegistry *registry,
134 const DBusString *service_name,
135 DBusConnection *owner_if_created,
136 BusTransaction *transaction,
141 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
143 _dbus_assert (owner_if_created != NULL);
144 _dbus_assert (transaction != NULL);
146 service = _dbus_hash_table_lookup_string (registry->service_hash,
147 _dbus_string_get_const_data (service_name));
151 service = _dbus_mem_pool_alloc (registry->service_pool);
158 service->registry = registry;
159 service->refcount = 1;
161 _dbus_verbose ("copying string %p '%s' to service->name\n",
162 service_name, _dbus_string_get_const_data (service_name));
163 if (!_dbus_string_copy_data (service_name, &service->name))
165 _dbus_mem_pool_dealloc (registry->service_pool, service);
169 _dbus_verbose ("copied string %p '%s' to '%s'\n",
170 service_name, _dbus_string_get_const_data (service_name),
173 if (!bus_driver_send_service_owner_changed (service->name,
175 bus_connection_get_name (owner_if_created),
178 bus_service_unref (service);
182 if (!bus_activation_service_created (bus_context_get_activation (registry->context),
183 service->name, transaction, error))
185 bus_service_unref (service);
189 if (!bus_service_add_owner (service, owner_if_created,
192 bus_service_unref (service);
196 if (!_dbus_hash_table_insert_string (registry->service_hash,
200 /* The add_owner gets reverted on transaction cancel */
209 bus_registry_foreach (BusRegistry *registry,
210 BusServiceForeachFunction function,
215 _dbus_hash_iter_init (registry->service_hash, &iter);
216 while (_dbus_hash_iter_next (&iter))
218 BusService *service = _dbus_hash_iter_get_value (&iter);
220 (* function) (service, data);
225 bus_registry_list_services (BusRegistry *registry,
233 len = _dbus_hash_table_get_n_entries (registry->service_hash);
234 retval = dbus_new (char *, len + 1);
239 _dbus_hash_iter_init (registry->service_hash, &iter);
241 while (_dbus_hash_iter_next (&iter))
243 BusService *service = _dbus_hash_iter_get_value (&iter);
245 retval[i] = _dbus_strdup (service->name);
246 if (retval[i] == NULL)
261 for (j = 0; j < i; j++)
262 dbus_free (retval[i]);
269 bus_registry_acquire_service (BusRegistry *registry,
270 DBusConnection *connection,
271 const DBusString *service_name,
273 dbus_uint32_t *result,
274 BusTransaction *transaction,
278 DBusConnection *old_owner;
279 DBusConnection *current_owner;
280 BusClientPolicy *policy;
282 BusActivation *activation;
287 if (!_dbus_validate_bus_name (service_name, 0,
288 _dbus_string_get_length (service_name)))
290 dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
291 "Requested bus name \"%s\" is not valid",
292 _dbus_string_get_const_data (service_name));
294 _dbus_verbose ("Attempt to acquire invalid service name\n");
299 if (_dbus_string_get_byte (service_name, 0) == ':')
301 /* Not allowed; only base services can start with ':' */
302 dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
303 "Cannot acquire a service starting with ':' such as \"%s\"",
304 _dbus_string_get_const_data (service_name));
306 _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
307 _dbus_string_get_const_data (service_name));
312 policy = bus_connection_get_policy (connection);
313 _dbus_assert (policy != NULL);
315 /* Note that if sid is #NULL then the bus's own context gets used
316 * in bus_connection_selinux_allows_acquire_service()
318 sid = bus_selinux_id_table_lookup (registry->service_sid_table,
321 if (!bus_selinux_allows_acquire_service (connection, sid,
322 _dbus_string_get_const_data (service_name), error))
325 if (dbus_error_is_set (error) &&
326 dbus_error_has_name (error, DBUS_ERROR_NO_MEMORY))
331 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
332 "Connection \"%s\" is not allowed to own the service \"%s\" due "
334 bus_connection_is_active (connection) ?
335 bus_connection_get_name (connection) :
337 _dbus_string_get_const_data (service_name));
341 if (!bus_client_policy_check_can_own (policy, connection,
344 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
345 "Connection \"%s\" is not allowed to own the service \"%s\" due "
346 "to security policies in the configuration file",
347 bus_connection_is_active (connection) ?
348 bus_connection_get_name (connection) :
350 _dbus_string_get_const_data (service_name));
354 if (bus_connection_get_n_services_owned (connection) >=
355 bus_context_get_max_services_per_connection (registry->context))
357 dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
358 "Connection \"%s\" is not allowed to own more services "
359 "(increase limits in configuration file if required)",
360 bus_connection_is_active (connection) ?
361 bus_connection_get_name (connection) :
366 service = bus_registry_lookup (registry, service_name);
369 old_owner = bus_service_get_primary_owner (service);
375 service = bus_registry_ensure (registry,
376 service_name, connection, transaction, error);
381 current_owner = bus_service_get_primary_owner (service);
383 if (old_owner == NULL)
385 _dbus_assert (current_owner == connection);
387 bus_service_set_prohibit_replacement (service,
388 (flags & DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT));
390 *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
392 else if (old_owner == connection)
393 *result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER;
394 else if (!((flags & DBUS_NAME_FLAG_REPLACE_EXISTING)))
395 *result = DBUS_REQUEST_NAME_REPLY_EXISTS;
396 else if (bus_service_get_prohibit_replacement (service))
398 /* Queue the connection */
399 if (!bus_service_add_owner (service, connection,
403 *result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE;
407 /* Replace the current owner */
409 /* We enqueue the new owner and remove the first one because
410 * that will cause NameAcquired and NameLost messages to
414 if (!bus_service_add_owner (service, connection,
418 if (!bus_service_remove_owner (service, old_owner,
422 _dbus_assert (connection == bus_service_get_primary_owner (service));
423 *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
426 activation = bus_context_get_activation (registry->context);
427 retval = bus_activation_send_pending_auto_activation_messages (activation,
437 bus_registry_release_service (BusRegistry *registry,
438 DBusConnection *connection,
439 const DBusString *service_name,
440 dbus_uint32_t *result,
441 BusTransaction *transaction,
449 if (!_dbus_validate_bus_name (service_name, 0,
450 _dbus_string_get_length (service_name)))
452 dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
453 "Given bus name \"%s\" is not valid",
454 _dbus_string_get_const_data (service_name));
456 _dbus_verbose ("Attempt to release invalid service name\n");
461 if (_dbus_string_get_byte (service_name, 0) == ':')
463 /* Not allowed; the base service name cannot be created or released */
464 dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
465 "Cannot release a service starting with ':' such as \"%s\"",
466 _dbus_string_get_const_data (service_name));
468 _dbus_verbose ("Attempt to release invalid base service name \"%s\"",
469 _dbus_string_get_const_data (service_name));
474 service = bus_registry_lookup (registry, service_name);
478 *result = DBUS_RELEASE_NAME_REPLY_NON_EXISTENT;
480 else if (!bus_service_has_owner (service, connection))
482 *result = DBUS_RELEASE_NAME_REPLY_NOT_OWNER;
486 if (!bus_service_remove_owner (service, connection,
490 _dbus_assert (!bus_service_has_owner (service, connection));
491 *result = DBUS_RELEASE_NAME_REPLY_RELEASED;
501 bus_registry_set_service_context_table (BusRegistry *registry,
502 DBusHashTable *table)
504 DBusHashTable *new_table;
507 new_table = bus_selinux_id_table_new ();
511 _dbus_hash_iter_init (table, &iter);
512 while (_dbus_hash_iter_next (&iter))
514 const char *service = _dbus_hash_iter_get_string_key (&iter);
515 const char *context = _dbus_hash_iter_get_value (&iter);
517 if (!bus_selinux_id_table_insert (new_table,
523 if (registry->service_sid_table)
524 _dbus_hash_table_unref (registry->service_sid_table);
525 registry->service_sid_table = new_table;
530 bus_service_unlink_owner (BusService *service,
531 DBusConnection *owner)
533 _dbus_list_remove_last (&service->owners, owner);
534 bus_connection_remove_owned_service (owner, service);
538 bus_service_unlink (BusService *service)
540 _dbus_assert (service->owners == NULL);
542 /* the service may not be in the hash, if
543 * the failure causing transaction cancel
544 * was in the right place, but that's OK
546 _dbus_hash_table_remove_string (service->registry->service_hash,
549 bus_service_unref (service);
553 bus_service_relink (BusService *service,
554 DBusPreallocatedHash *preallocated)
556 _dbus_assert (service->owners == NULL);
557 _dbus_assert (preallocated != NULL);
559 _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
564 bus_service_ref (service);
568 * Data used to represent an ownership cancellation in
573 DBusConnection *connection; /**< the connection */
574 BusService *service; /**< service to cancel ownership of */
575 } OwnershipCancelData;
578 cancel_ownership (void *data)
580 OwnershipCancelData *d = data;
582 /* We don't need to send messages notifying of these
583 * changes, since we're reverting something that was
584 * cancelled (effectively never really happened)
586 bus_service_unlink_owner (d->service, d->connection);
588 if (d->service->owners == NULL)
589 bus_service_unlink (d->service);
593 free_ownership_cancel_data (void *data)
595 OwnershipCancelData *d = data;
597 dbus_connection_unref (d->connection);
598 bus_service_unref (d->service);
604 add_cancel_ownership_to_transaction (BusTransaction *transaction,
606 DBusConnection *connection)
608 OwnershipCancelData *d;
610 d = dbus_new (OwnershipCancelData, 1);
614 d->service = service;
615 d->connection = connection;
617 if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
618 free_ownership_cancel_data))
624 bus_service_ref (d->service);
625 dbus_connection_ref (d->connection);
630 /* this function is self-cancelling if you cancel the transaction */
632 bus_service_add_owner (BusService *service,
633 DBusConnection *owner,
634 BusTransaction *transaction,
637 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
639 /* Send service acquired message first, OOM will result
640 * in cancelling the transaction
642 if (service->owners == NULL)
644 if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
648 if (!_dbus_list_append (&service->owners,
655 if (!bus_connection_add_owned_service (owner, service))
657 _dbus_list_remove_last (&service->owners, owner);
662 if (!add_cancel_ownership_to_transaction (transaction,
666 bus_service_unlink_owner (service, owner);
676 DBusConnection *connection;
678 DBusConnection *before_connection; /* restore to position before this connection in owners list */
679 DBusList *connection_link;
680 DBusList *service_link;
681 DBusPreallocatedHash *hash_entry;
682 } OwnershipRestoreData;
685 restore_ownership (void *data)
687 OwnershipRestoreData *d = data;
690 _dbus_assert (d->service_link != NULL);
691 _dbus_assert (d->connection_link != NULL);
693 if (d->service->owners == NULL)
695 _dbus_assert (d->hash_entry != NULL);
696 bus_service_relink (d->service, d->hash_entry);
700 _dbus_assert (d->hash_entry == NULL);
703 /* We don't need to send messages notifying of these
704 * changes, since we're reverting something that was
705 * cancelled (effectively never really happened)
707 link = _dbus_list_get_first_link (&d->service->owners);
710 if (link->data == d->before_connection)
713 link = _dbus_list_get_next_link (&d->service->owners, link);
716 _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
718 /* Note that removing then restoring this changes the order in which
719 * ServiceDeleted messages are sent on destruction of the
720 * connection. This should be OK as the only guarantee there is
721 * that the base service is destroyed last, and we never even
722 * tentatively remove the base service.
724 bus_connection_add_owned_service_link (d->connection, d->service_link);
726 d->hash_entry = NULL;
727 d->service_link = NULL;
728 d->connection_link = NULL;
732 free_ownership_restore_data (void *data)
734 OwnershipRestoreData *d = data;
737 _dbus_list_free_link (d->service_link);
738 if (d->connection_link)
739 _dbus_list_free_link (d->connection_link);
741 _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
744 dbus_connection_unref (d->connection);
745 bus_service_unref (d->service);
751 add_restore_ownership_to_transaction (BusTransaction *transaction,
753 DBusConnection *connection)
755 OwnershipRestoreData *d;
758 d = dbus_new (OwnershipRestoreData, 1);
762 d->service = service;
763 d->connection = connection;
764 d->service_link = _dbus_list_alloc_link (service);
765 d->connection_link = _dbus_list_alloc_link (connection);
766 d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
768 bus_service_ref (d->service);
769 dbus_connection_ref (d->connection);
771 d->before_connection = NULL;
772 link = _dbus_list_get_first_link (&service->owners);
775 if (link->data == connection)
777 link = _dbus_list_get_next_link (&service->owners, link);
780 d->before_connection = link->data;
785 link = _dbus_list_get_next_link (&service->owners, link);
788 if (d->service_link == NULL ||
789 d->connection_link == NULL ||
790 d->hash_entry == NULL ||
791 !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
792 free_ownership_restore_data))
794 free_ownership_restore_data (d);
801 /* this function is self-cancelling if you cancel the transaction */
803 bus_service_remove_owner (BusService *service,
804 DBusConnection *owner,
805 BusTransaction *transaction,
808 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
810 /* We send out notifications before we do any work we
811 * might have to undo if the notification-sending failed
814 /* Send service lost message */
815 if (bus_service_get_primary_owner (service) == owner)
817 if (!bus_driver_send_service_lost (owner, service->name,
822 if (service->owners == NULL)
824 _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
826 else if (_dbus_list_length_is_one (&service->owners))
828 if (!bus_driver_send_service_owner_changed (service->name,
829 bus_connection_get_name (owner),
837 DBusConnection *new_owner;
838 link = _dbus_list_get_first_link (&service->owners);
839 _dbus_assert (link != NULL);
840 link = _dbus_list_get_next_link (&service->owners, link);
841 _dbus_assert (link != NULL);
843 new_owner = link->data;
845 if (!bus_driver_send_service_owner_changed (service->name,
846 bus_connection_get_name (owner),
847 bus_connection_get_name (new_owner),
851 /* This will be our new owner */
852 if (!bus_driver_send_service_acquired (new_owner,
859 if (!add_restore_ownership_to_transaction (transaction, service, owner))
865 bus_service_unlink_owner (service, owner);
867 if (service->owners == NULL)
868 bus_service_unlink (service);
874 bus_service_ref (BusService *service)
876 _dbus_assert (service->refcount > 0);
878 service->refcount += 1;
884 bus_service_unref (BusService *service)
886 _dbus_assert (service->refcount > 0);
888 service->refcount -= 1;
890 if (service->refcount == 0)
892 _dbus_assert (service->owners == NULL);
894 dbus_free (service->name);
895 _dbus_mem_pool_dealloc (service->registry->service_pool, service);
900 bus_service_get_primary_owner (BusService *service)
902 return _dbus_list_get_first (&service->owners);
906 bus_service_get_name (BusService *service)
908 return service->name;
912 bus_service_set_prohibit_replacement (BusService *service,
913 dbus_bool_t prohibit_replacement)
915 service->prohibit_replacement = prohibit_replacement != FALSE;
919 bus_service_get_prohibit_replacement (BusService *service)
921 return service->prohibit_replacement;
925 bus_service_has_owner (BusService *service,
926 DBusConnection *owner)
930 link = _dbus_list_get_first_link (&service->owners);
934 if (link->data == owner)
937 link = _dbus_list_get_next_link (&service->owners, link);