X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=bus%2Fservices.c;h=a1563badbdc118d7d85558b43c3351ad20e55854;hb=bd6d8d261df61ed8b396434e5a80285d43184d6c;hp=ae7b7838f3eb794b7e6cbd4645e9c4dda9d14b86;hpb=47d8e53bfeccc6f90475408bfbef9c0132a4122f;p=platform%2Fupstream%2Fdbus.git diff --git a/bus/services.c b/bus/services.c index ae7b783..a1563ba 100644 --- a/bus/services.c +++ b/bus/services.c @@ -1,10 +1,10 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* services.c Service management * * Copyright (C) 2003 Red Hat, Inc. * Copyright (C) 2003 CodeFactory AB * - * Licensed under the Academic Free License version 2.0 + * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,12 +18,19 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ + +#include #include #include #include +#include +#include +#include +#include + #include "driver.h" #include "services.h" @@ -31,6 +38,9 @@ #include "utils.h" #include "activation.h" #include "policy.h" +#include "bus.h" +#include "selinux.h" +#include "kdbus-d.h" struct BusService { @@ -39,8 +49,17 @@ struct BusService BusRegistry *registry; char *name; DBusList *owners; - - unsigned int prohibit_replacement : 1; +}; + +struct BusOwner +{ + int refcount; + + BusService *service; + DBusConnection *conn; + + unsigned int allow_replacement : 1; + unsigned int do_not_queue : 1; }; struct BusRegistry @@ -51,6 +70,9 @@ struct BusRegistry DBusHashTable *service_hash; DBusMemPool *service_pool; + DBusMemPool *owner_pool; + + DBusHashTable *service_sid_table; }; BusRegistry* @@ -72,9 +94,18 @@ bus_registry_new (BusContext *context) registry->service_pool = _dbus_mem_pool_new (sizeof (BusService), TRUE); + if (registry->service_pool == NULL) goto failed; + registry->owner_pool = _dbus_mem_pool_new (sizeof (BusOwner), + TRUE); + + if (registry->owner_pool == NULL) + goto failed; + + registry->service_sid_table = NULL; + return registry; failed: @@ -103,7 +134,11 @@ bus_registry_unref (BusRegistry *registry) _dbus_hash_table_unref (registry->service_hash); if (registry->service_pool) _dbus_mem_pool_free (registry->service_pool); - + if (registry->owner_pool) + _dbus_mem_pool_free (registry->owner_pool); + if (registry->service_sid_table) + _dbus_hash_table_unref (registry->service_sid_table); + dbus_free (registry); } } @@ -120,10 +155,120 @@ bus_registry_lookup (BusRegistry *registry, return service; } +static DBusList * +_bus_service_find_owner_link (BusService *service, + DBusConnection *connection) +{ + DBusList *link; + + link = _dbus_list_get_first_link (&service->owners); + + while (link != NULL) + { + BusOwner *bus_owner; + + bus_owner = (BusOwner *) link->data; + if (bus_owner->conn == connection) + break; + + link = _dbus_list_get_next_link (&service->owners, link); + } + + return link; +} + +static DBusConnection * +_bus_service_find_owner_connection (BusService *service, + const char* unique_name) +{ + DBusList *link; + + link = _dbus_list_get_first_link (&service->owners); + + while (link != NULL) + { + BusOwner *bus_owner; + + bus_owner = (BusOwner *) link->data; + if(!strcmp(bus_connection_get_name(bus_owner->conn), unique_name)) + return bus_owner->conn; + + link = _dbus_list_get_next_link (&service->owners, link); + } + + return NULL; +} + +static void +bus_owner_set_flags (BusOwner *owner, + dbus_uint32_t flags) +{ + owner->allow_replacement = + (flags & DBUS_NAME_FLAG_ALLOW_REPLACEMENT) != FALSE; + + owner->do_not_queue = + (flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) != FALSE; +} + +static BusOwner * +bus_owner_new (BusService *service, + DBusConnection *conn, + dbus_uint32_t flags) +{ + BusOwner *result; + + result = _dbus_mem_pool_alloc (service->registry->owner_pool); + if (result != NULL) + { + result->refcount = 1; + /* don't ref the connection because we don't want + to block the connection from going away. + transactions take care of reffing the connection + but we need to use refcounting on the owner + so that the owner does not get freed before + we can deref the connection in the transaction + */ + result->conn = conn; + result->service = service; + + if (!bus_connection_add_owned_service (conn, service)) + { + _dbus_mem_pool_dealloc (service->registry->owner_pool, result); + return NULL; + } + + bus_owner_set_flags (result, flags); + } + return result; +} + +static BusOwner * +bus_owner_ref (BusOwner *owner) +{ + _dbus_assert (owner->refcount > 0); + owner->refcount += 1; + + return owner; +} + +static void +bus_owner_unref (BusOwner *owner) +{ + _dbus_assert (owner->refcount > 0); + owner->refcount -= 1; + + if (owner->refcount == 0) + { + bus_connection_remove_owned_service (owner->conn, owner->service); + _dbus_mem_pool_dealloc (owner->service->registry->owner_pool, owner); + } +} + BusService* bus_registry_ensure (BusRegistry *registry, const DBusString *service_name, - DBusConnection *owner_if_created, + DBusConnection *owner_connection_if_created, + dbus_uint32_t flags, BusTransaction *transaction, DBusError *error) { @@ -131,7 +276,7 @@ bus_registry_ensure (BusRegistry *registry, _DBUS_ASSERT_ERROR_IS_CLEAR (error); - _dbus_assert (owner_if_created != NULL); + _dbus_assert (owner_connection_if_created != NULL); _dbus_assert (transaction != NULL); service = _dbus_hash_table_lookup_string (registry->service_hash, @@ -148,15 +293,23 @@ bus_registry_ensure (BusRegistry *registry, service->registry = registry; service->refcount = 1; - + + _dbus_verbose ("copying string %p '%s' to service->name\n", + service_name, _dbus_string_get_const_data (service_name)); if (!_dbus_string_copy_data (service_name, &service->name)) { _dbus_mem_pool_dealloc (registry->service_pool, service); BUS_SET_OOM (error); return NULL; } - - if (!bus_driver_send_service_created (service->name, transaction, error)) + _dbus_verbose ("copied string %p '%s' to '%s'\n", + service_name, _dbus_string_get_const_data (service_name), + service->name); + + if (!bus_driver_send_service_owner_changed (service->name, + NULL, + bus_connection_get_name (owner_connection_if_created), + transaction, error)) { bus_service_unref (service); return NULL; @@ -169,8 +322,8 @@ bus_registry_ensure (BusRegistry *registry, return NULL; } - if (!bus_service_add_owner (service, owner_if_created, - transaction, error)) + if (!bus_service_add_owner (service, owner_connection_if_created, flags, + transaction, error)) { bus_service_unref (service); return NULL; @@ -184,7 +337,7 @@ bus_registry_ensure (BusRegistry *registry, BUS_SET_OOM (error); return NULL; } - + return service; } @@ -242,7 +395,7 @@ bus_registry_list_services (BusRegistry *registry, error: for (j = 0; j < i; j++) - dbus_free (retval[i]); + dbus_free (retval[j]); dbus_free (retval); return FALSE; @@ -258,19 +411,23 @@ bus_registry_acquire_service (BusRegistry *registry, DBusError *error) { dbus_bool_t retval; - DBusConnection *old_owner; - DBusConnection *current_owner; + DBusConnection *old_owner_conn; BusClientPolicy *policy; BusService *service; - + BusActivation *activation; + BusSELinuxID *sid; + BusOwner *primary_owner; + retval = FALSE; - if (_dbus_string_get_length (service_name) == 0) + if (!_dbus_validate_bus_name (service_name, 0, + _dbus_string_get_length (service_name))) { - dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, - "Zero-length service name is not allowed"); + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Requested bus name \"%s\" is not valid", + _dbus_string_get_const_data (service_name)); - _dbus_verbose ("Attempt to acquire zero-length service name\n"); + _dbus_verbose ("Attempt to acquire invalid service name\n"); goto out; } @@ -278,7 +435,7 @@ bus_registry_acquire_service (BusRegistry *registry, if (_dbus_string_get_byte (service_name, 0) == ':') { /* Not allowed; only base services can start with ':' */ - dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, "Cannot acquire a service starting with ':' such as \"%s\"", _dbus_string_get_const_data (service_name)); @@ -288,106 +445,472 @@ bus_registry_acquire_service (BusRegistry *registry, goto out; } - policy = bus_connection_get_policy (connection); - _dbus_assert (policy != NULL); - - if (!bus_client_policy_check_can_own (policy, connection, - service_name)) + if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS)) { - dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, - "Connection \"%s\" is not allowed to own the service \"%s\" due " - "to security policies in the configuration file", + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Connection \"%s\" is not allowed to own the service \"%s\"because " + "it is reserved for D-Bus' use only", bus_connection_is_active (connection) ? bus_connection_get_name (connection) : "(inactive)", - service_name); + DBUS_SERVICE_DBUS); goto out; } + policy = bus_connection_get_policy (connection); + _dbus_assert (policy != NULL); + + /* Note that if sid is #NULL then the bus's own context gets used + * in bus_connection_selinux_allows_acquire_service() + */ + sid = bus_selinux_id_table_lookup (registry->service_sid_table, + service_name); + + if (!bus_selinux_allows_acquire_service (connection, sid, + _dbus_string_get_const_data (service_name), error)) + { + + if (dbus_error_is_set (error) && + dbus_error_has_name (error, DBUS_ERROR_NO_MEMORY)) + { + goto out; + } + + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "Connection \"%s\" is not allowed to own the service \"%s\" due " + "to SELinux policy", + bus_connection_is_active (connection) ? + bus_connection_get_name (connection) : + "(inactive)", + _dbus_string_get_const_data (service_name)); + goto out; + } + + if (!bus_client_policy_check_can_own (policy, service_name)) + { + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "Connection \"%s\" is not allowed to own the service \"%s\" due " + "to security policies in the configuration file", + bus_connection_is_active (connection) ? + bus_connection_get_name (connection) : + "(inactive)", + _dbus_string_get_const_data (service_name)); + goto out; + } + if (bus_connection_get_n_services_owned (connection) >= - bus_context_get_max_services_per_connection (registry->context)) - { - dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, - "Connection \"%s\" is not allowed to own more services " - "(increase limits in configuration file if required)", - bus_connection_is_active (connection) ? - bus_connection_get_name (connection) : - "(inactive)"); - goto out; - } - + bus_context_get_max_services_per_connection (registry->context)) + { + dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, + "Connection \"%s\" is not allowed to own more services " + "(increase limits in configuration file if required)", + bus_connection_is_active (connection) ? + bus_connection_get_name (connection) : + "(inactive)"); + goto out; + } + service = bus_registry_lookup (registry, service_name); if (service != NULL) - old_owner = bus_service_get_primary_owner (service); + { + primary_owner = bus_service_get_primary_owner (service); + if (primary_owner != NULL) + old_owner_conn = primary_owner->conn; + else + old_owner_conn = NULL; + } else - old_owner = NULL; - + old_owner_conn = NULL; + if (service == NULL) + { + service = bus_registry_ensure (registry, + service_name, connection, flags, + transaction, error); + if (service == NULL) + goto out; + } + + primary_owner = bus_service_get_primary_owner (service); + if (primary_owner == NULL) + goto out; + + if (old_owner_conn == NULL) + { + _dbus_assert (primary_owner->conn == connection); + + *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; + } + else if (old_owner_conn == connection) + { + bus_owner_set_flags (primary_owner, flags); + *result = DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER; + } + else if (((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) && + !(bus_service_get_allow_replacement (service))) || + ((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) && + !(flags & DBUS_NAME_FLAG_REPLACE_EXISTING))) + { + DBusList *link; + BusOwner *temp_owner; + /* Since we can't be queued if we are already in the queue + remove us */ + + link = _bus_service_find_owner_link (service, connection); + if (link != NULL) + { + _dbus_list_unlink (&service->owners, link); + temp_owner = (BusOwner *)link->data; + bus_owner_unref (temp_owner); + _dbus_list_free_link (link); + } + + *result = DBUS_REQUEST_NAME_REPLY_EXISTS; + } + else if (!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) && + (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) || + !(bus_service_get_allow_replacement (service)))) + { + /* Queue the connection */ + if (!bus_service_add_owner (service, connection, + flags, + transaction, error)) + goto out; + + *result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE; + } + else + { + /* Replace the current owner */ + + /* We enqueue the new owner and remove the first one because + * that will cause NameAcquired and NameLost messages to + * be sent. + */ + + if (!bus_service_add_owner (service, connection, + flags, + transaction, error)) + goto out; + + if (primary_owner->do_not_queue) + { + if (!bus_service_remove_owner (service, old_owner_conn, + transaction, error)) + goto out; + } + else + { + if (!bus_service_swap_owner (service, old_owner_conn, + transaction, error)) + goto out; + } + + + _dbus_assert (connection == bus_service_get_primary_owner (service)->conn); + *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER; + } + + activation = bus_context_get_activation (registry->context); + retval = bus_activation_send_pending_auto_activation_messages (activation, + service, + transaction, + error); + + out: + return retval; +} + +dbus_bool_t +bus_registry_acquire_kdbus_service (BusRegistry *registry, + DBusConnection *connection, + DBusMessage *message, + dbus_uint32_t *result, + BusTransaction *transaction, + DBusError *error) +{ + dbus_bool_t retval; + BusService *service; + BusActivation *activation; + + DBusString service_name_real; + const DBusString *service_name = &service_name_real; + char* name; + dbus_uint32_t flags; + __u64 sender_id; + + if (!dbus_message_get_args (message, error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_UINT32, &flags, + DBUS_TYPE_INVALID)) + return FALSE; + + retval = FALSE; + + _dbus_string_init_const (&service_name_real, name); + + if (!_dbus_validate_bus_name (service_name, 0, + _dbus_string_get_length (service_name))) + { + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Requested bus name \"%s\" is not valid", + _dbus_string_get_const_data (service_name)); + + _dbus_verbose ("Attempt to acquire invalid service name\n"); + + goto out; + } + + if (_dbus_string_get_byte (service_name, 0) == ':') + { + /* Not allowed; only base services can start with ':' */ + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Cannot acquire a service starting with ':' such as \"%s\"", + _dbus_string_get_const_data (service_name)); + + _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"", + _dbus_string_get_const_data (service_name)); + + goto out; + } + + if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS)) { - service = bus_registry_ensure (registry, - service_name, connection, transaction, error); - if (service == NULL) - goto out; + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Connection \"%s\" is not allowed to own the service \"%s\"because " + "it is reserved for D-Bus' use only", + bus_connection_is_active (connection) ? + bus_connection_get_name (connection) : + "(inactive)", + DBUS_SERVICE_DBUS); + goto out; } - current_owner = bus_service_get_primary_owner (service); + service = bus_registry_lookup (registry, service_name); + if (service == NULL) + { + service = bus_registry_ensure (registry, service_name, connection, flags, + transaction, error); //adds daemon to service owners list - must be removed after right owner is set + if (service == NULL) + goto out; + + if(!kdbus_register_policy(service_name, connection)) + { + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "Connection is not allowed to own the service \"%s\" due to security policies in the configuration file", + _dbus_string_get_const_data (service_name)); + goto failed; + } + } + + sender_id = sender_name_to_id(dbus_message_get_sender(message), error); + if(dbus_error_is_set(error)) + goto failed; + + *result = kdbus_request_name(connection, service_name, flags, sender_id); + if(*result == -EPERM) + { + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "Connection is not allowed to own the service \"%s\" due to security policies in the configuration file", + _dbus_string_get_const_data (service_name)); + goto failed; + } + else if(*result < 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED , "Name \"%s\" could not be acquired", name); + goto failed; + } + + if((*result == DBUS_REQUEST_NAME_REPLY_IN_QUEUE) || (*result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)) + { + DBusConnection* phantom; + + phantom = create_phantom_connection(connection, dbus_message_get_sender(message), error); + if(phantom == NULL) + goto failed2; + if (!bus_service_add_owner (service, phantom, flags, transaction, error)) + { + dbus_connection_unref_phantom(phantom); + goto failed2; + } + if(*result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + /* Here we are removing DBus daemon as an owner of the service, + * which is set by bus_registry_ensure. + * If bus_service_remove_owner fail, we ignore it, because it has + * almost none impact on the usage + */ + if(_bus_service_find_owner_link (service, connection)) + bus_service_remove_owner (service, connection, transaction, NULL); + } + } + + activation = bus_context_get_activation (registry->context); + retval = bus_activation_send_pending_auto_activation_messages (activation, + service, + transaction, + error); + out: + return retval; + +failed2: + kdbus_release_name(connection, service_name, sender_id); +failed: + if(_bus_service_find_owner_link (service, connection)) + bus_service_remove_owner (service, connection, transaction, NULL); + + return FALSE; +} + +dbus_bool_t +bus_registry_release_service (BusRegistry *registry, + DBusConnection *connection, + const DBusString *service_name, + dbus_uint32_t *result, + BusTransaction *transaction, + DBusError *error) +{ + dbus_bool_t retval; + BusService *service; + + retval = FALSE; - if (old_owner == NULL) + if (!_dbus_validate_bus_name (service_name, 0, + _dbus_string_get_length (service_name))) { - _dbus_assert (current_owner == connection); + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Given bus name \"%s\" is not valid", + _dbus_string_get_const_data (service_name)); + + _dbus_verbose ("Attempt to release invalid service name\n"); - bus_service_set_prohibit_replacement (service, - (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT)); - - *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER; + goto out; } - else if (old_owner == connection) - *result = DBUS_SERVICE_REPLY_ALREADY_OWNER; - else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING))) - *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS; - else if (bus_service_get_prohibit_replacement (service)) + + if (_dbus_string_get_byte (service_name, 0) == ':') { - /* Queue the connection */ - if (!bus_service_add_owner (service, connection, - transaction, error)) - goto out; - - *result = DBUS_SERVICE_REPLY_IN_QUEUE; + /* Not allowed; the base service name cannot be created or released */ + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Cannot release a service starting with ':' such as \"%s\"", + _dbus_string_get_const_data (service_name)); + + _dbus_verbose ("Attempt to release invalid base service name \"%s\"", + _dbus_string_get_const_data (service_name)); + + goto out; } - else + + if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS)) { - /* Replace the current owner */ + /* Not allowed; the base service name cannot be created or released */ + dbus_set_error (error, DBUS_ERROR_INVALID_ARGS, + "Cannot release the %s service because it is owned by the bus", + DBUS_SERVICE_DBUS); - /* We enqueue the new owner and remove the first one because - * that will cause ServiceAcquired and ServiceLost messages to - * be sent. - */ - - if (!bus_service_add_owner (service, connection, - transaction, error)) - goto out; + _dbus_verbose ("Attempt to release service name \"%s\"", + DBUS_SERVICE_DBUS); - if (!bus_service_remove_owner (service, old_owner, - transaction, error)) - goto out; - - _dbus_assert (connection == bus_service_get_primary_owner (service)); - *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER; + goto out; } + if(bus_context_is_kdbus(bus_transaction_get_context (transaction))) + { + __u64 sender_id; + + sender_id = sender_name_to_id((const char*)registry, error); + if(dbus_error_is_set(error)) + return FALSE; + + *result = kdbus_release_name(connection, service_name, sender_id); + + if(*result == DBUS_RELEASE_NAME_REPLY_RELEASED) + { + const char* name; + + name = (const char*)registry; //get name passed in registry pointer + registry = bus_connection_get_registry (connection); //than take original registry address + + service = bus_registry_lookup (registry, service_name); + if(service) + { + DBusConnection* phantom; + + phantom = _bus_service_find_owner_connection(service, name); + if(phantom) + { + bus_service_remove_owner (service, phantom, transaction, NULL); + dbus_connection_unref_phantom(phantom); + } + else + _dbus_verbose ("Didn't find phantom connection for released name!\n"); + } + } + } + else + { + service = bus_registry_lookup (registry, service_name); + + if (service == NULL) + { + *result = DBUS_RELEASE_NAME_REPLY_NON_EXISTENT; + } + else if (!bus_service_has_owner (service, connection)) + { + *result = DBUS_RELEASE_NAME_REPLY_NOT_OWNER; + } + else + { + if (!bus_service_remove_owner (service, connection, + transaction, error)) + goto out; + + _dbus_assert (!bus_service_has_owner (service, connection)); + *result = DBUS_RELEASE_NAME_REPLY_RELEASED; + } + } + retval = TRUE; - + out: return retval; } +dbus_bool_t +bus_registry_set_service_context_table (BusRegistry *registry, + DBusHashTable *table) +{ + DBusHashTable *new_table; + DBusHashIter iter; + + new_table = bus_selinux_id_table_new (); + if (!new_table) + return FALSE; + + _dbus_hash_iter_init (table, &iter); + while (_dbus_hash_iter_next (&iter)) + { + const char *service = _dbus_hash_iter_get_string_key (&iter); + const char *context = _dbus_hash_iter_get_value (&iter); + + if (!bus_selinux_id_table_insert (new_table, + service, + context)) + return FALSE; + } + + if (registry->service_sid_table) + _dbus_hash_table_unref (registry->service_sid_table); + registry->service_sid_table = new_table; + return TRUE; +} + static void bus_service_unlink_owner (BusService *service, - DBusConnection *owner) + BusOwner *owner) { _dbus_list_remove_last (&service->owners, owner); - bus_connection_remove_owned_service (owner, service); + bus_owner_unref (owner); } static void @@ -426,7 +949,7 @@ bus_service_relink (BusService *service, */ typedef struct { - DBusConnection *connection; /**< the connection */ + BusOwner *owner; /**< the owner */ BusService *service; /**< service to cancel ownership of */ } OwnershipCancelData; @@ -439,7 +962,7 @@ cancel_ownership (void *data) * changes, since we're reverting something that was * cancelled (effectively never really happened) */ - bus_service_unlink_owner (d->service, d->connection); + bus_service_unlink_owner (d->service, d->owner); if (d->service->owners == NULL) bus_service_unlink (d->service); @@ -450,7 +973,8 @@ free_ownership_cancel_data (void *data) { OwnershipCancelData *d = data; - dbus_connection_unref (d->connection); + dbus_connection_unref (d->owner->conn); + bus_owner_unref (d->owner); bus_service_unref (d->service); dbus_free (d); @@ -459,7 +983,7 @@ free_ownership_cancel_data (void *data) static dbus_bool_t add_cancel_ownership_to_transaction (BusTransaction *transaction, BusService *service, - DBusConnection *connection) + BusOwner *owner) { OwnershipCancelData *d; @@ -468,7 +992,7 @@ add_cancel_ownership_to_transaction (BusTransaction *transaction, return FALSE; d->service = service; - d->connection = connection; + d->owner = owner; if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d, free_ownership_cancel_data)) @@ -478,18 +1002,23 @@ add_cancel_ownership_to_transaction (BusTransaction *transaction, } bus_service_ref (d->service); - dbus_connection_ref (d->connection); - + bus_owner_ref (owner); + dbus_connection_ref (d->owner->conn); + return TRUE; } /* this function is self-cancelling if you cancel the transaction */ dbus_bool_t bus_service_add_owner (BusService *service, - DBusConnection *owner, + DBusConnection *connection, + dbus_uint32_t flags, BusTransaction *transaction, DBusError *error) { + BusOwner *bus_owner; + DBusList *bus_owner_link; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); /* Send service acquired message first, OOM will result @@ -497,42 +1026,83 @@ bus_service_add_owner (BusService *service, */ if (service->owners == NULL) { - if (!bus_driver_send_service_acquired (owner, service->name, transaction, error)) + if (!bus_driver_send_service_acquired (connection, service->name, transaction, error)) return FALSE; } - if (!_dbus_list_append (&service->owners, - owner)) + bus_owner_link = _bus_service_find_owner_link (service, connection); + + if (bus_owner_link == NULL) { - BUS_SET_OOM (error); - return FALSE; - } + bus_owner = bus_owner_new (service, connection, flags); + if (bus_owner == NULL) + { + BUS_SET_OOM (error); + return FALSE; + } - if (!bus_connection_add_owned_service (owner, service)) + bus_owner_set_flags (bus_owner, flags); + if (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) || service->owners == NULL) + { + if (!_dbus_list_append (&service->owners, + bus_owner)) + { + bus_owner_unref (bus_owner); + BUS_SET_OOM (error); + return FALSE; + } + } + else + { + if (!_dbus_list_insert_after (&service->owners, + _dbus_list_get_first_link (&service->owners), + bus_owner)) + { + bus_owner_unref (bus_owner); + BUS_SET_OOM (error); + return FALSE; + } + } + } + else { - _dbus_list_remove_last (&service->owners, owner); - BUS_SET_OOM (error); - return FALSE; + /* Update the link since we are already in the queue + * No need for operations that can produce OOM + */ + + bus_owner = (BusOwner *) bus_owner_link->data; + if (flags & DBUS_NAME_FLAG_REPLACE_EXISTING) + { + DBusList *link; + _dbus_list_unlink (&service->owners, bus_owner_link); + link = _dbus_list_get_first_link (&service->owners); + _dbus_assert (link != NULL); + + _dbus_list_insert_after_link (&service->owners, link, bus_owner_link); + } + + bus_owner_set_flags (bus_owner, flags); + return TRUE; } if (!add_cancel_ownership_to_transaction (transaction, service, - owner)) + bus_owner)) { - bus_service_unlink_owner (service, owner); + bus_service_unlink_owner (service, bus_owner); BUS_SET_OOM (error); return FALSE; } - + return TRUE; } typedef struct { - DBusConnection *connection; + BusOwner *owner; BusService *service; - DBusConnection *before_connection; /* restore to position before this connection in owners list */ - DBusList *connection_link; + BusOwner *before_owner; /* restore to position before this connection in owners list */ + DBusList *owner_link; DBusList *service_link; DBusPreallocatedHash *hash_entry; } OwnershipRestoreData; @@ -544,7 +1114,7 @@ restore_ownership (void *data) DBusList *link; _dbus_assert (d->service_link != NULL); - _dbus_assert (d->connection_link != NULL); + _dbus_assert (d->owner_link != NULL); if (d->service->owners == NULL) { @@ -563,13 +1133,13 @@ restore_ownership (void *data) link = _dbus_list_get_first_link (&d->service->owners); while (link != NULL) { - if (link->data == d->before_connection) + if (link->data == d->before_owner) break; link = _dbus_list_get_next_link (&d->service->owners, link); } - _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link); + _dbus_list_insert_before_link (&d->service->owners, link, d->owner_link); /* Note that removing then restoring this changes the order in which * ServiceDeleted messages are sent on destruction of the @@ -577,11 +1147,11 @@ restore_ownership (void *data) * that the base service is destroyed last, and we never even * tentatively remove the base service. */ - bus_connection_add_owned_service_link (d->connection, d->service_link); + bus_connection_add_owned_service_link (d->owner->conn, d->service_link); d->hash_entry = NULL; d->service_link = NULL; - d->connection_link = NULL; + d->owner_link = NULL; } static void @@ -591,13 +1161,14 @@ free_ownership_restore_data (void *data) if (d->service_link) _dbus_list_free_link (d->service_link); - if (d->connection_link) - _dbus_list_free_link (d->connection_link); + if (d->owner_link) + _dbus_list_free_link (d->owner_link); if (d->hash_entry) _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash, d->hash_entry); - dbus_connection_unref (d->connection); + dbus_connection_unref (d->owner->conn); + bus_owner_unref (d->owner); bus_service_unref (d->service); dbus_free (d); @@ -606,7 +1177,7 @@ free_ownership_restore_data (void *data) static dbus_bool_t add_restore_ownership_to_transaction (BusTransaction *transaction, BusService *service, - DBusConnection *connection) + BusOwner *owner) { OwnershipRestoreData *d; DBusList *link; @@ -616,24 +1187,25 @@ add_restore_ownership_to_transaction (BusTransaction *transaction, return FALSE; d->service = service; - d->connection = connection; + d->owner = owner; d->service_link = _dbus_list_alloc_link (service); - d->connection_link = _dbus_list_alloc_link (connection); + d->owner_link = _dbus_list_alloc_link (owner); d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash); bus_service_ref (d->service); - dbus_connection_ref (d->connection); + bus_owner_ref (d->owner); + dbus_connection_ref (d->owner->conn); - d->before_connection = NULL; + d->before_owner = NULL; link = _dbus_list_get_first_link (&service->owners); while (link != NULL) { - if (link->data == connection) + if (link->data == owner) { link = _dbus_list_get_next_link (&service->owners, link); if (link) - d->before_connection = link->data; + d->before_owner = link->data; break; } @@ -642,7 +1214,7 @@ add_restore_ownership_to_transaction (BusTransaction *transaction, } if (d->service_link == NULL || - d->connection_link == NULL || + d->owner_link == NULL || d->hash_entry == NULL || !bus_transaction_add_cancel_hook (transaction, restore_ownership, d, free_ownership_restore_data)) @@ -654,13 +1226,92 @@ add_restore_ownership_to_transaction (BusTransaction *transaction, return TRUE; } +dbus_bool_t +bus_service_swap_owner (BusService *service, + DBusConnection *connection, + BusTransaction *transaction, + DBusError *error) +{ + DBusList *swap_link; + BusOwner *primary_owner; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + /* We send out notifications before we do any work we + * might have to undo if the notification-sending failed + */ + + /* Send service lost message */ + primary_owner = bus_service_get_primary_owner (service); + if (primary_owner == NULL || primary_owner->conn != connection) + _dbus_assert_not_reached ("Tried to swap a non primary owner"); + + + if (!bus_driver_send_service_lost (connection, service->name, + transaction, error)) + return FALSE; + + if (service->owners == NULL) + { + _dbus_assert_not_reached ("Tried to swap owner of a service that has no owners"); + } + else if (_dbus_list_length_is_one (&service->owners)) + { + _dbus_assert_not_reached ("Tried to swap owner of a service that has no other owners in the queue"); + } + else + { + DBusList *link; + BusOwner *new_owner; + DBusConnection *new_owner_conn; + link = _dbus_list_get_first_link (&service->owners); + _dbus_assert (link != NULL); + link = _dbus_list_get_next_link (&service->owners, link); + _dbus_assert (link != NULL); + + new_owner = (BusOwner *)link->data; + new_owner_conn = new_owner->conn; + + if (!bus_driver_send_service_owner_changed (service->name, + bus_connection_get_name (connection), + bus_connection_get_name (new_owner_conn), + transaction, error)) + return FALSE; + + /* This will be our new owner */ + if (!bus_driver_send_service_acquired (new_owner_conn, + service->name, + transaction, + error)) + return FALSE; + } + + if (!add_restore_ownership_to_transaction (transaction, service, primary_owner)) + { + BUS_SET_OOM (error); + return FALSE; + } + + /* unlink the primary and make it the second link */ + swap_link = _dbus_list_get_first_link (&service->owners); + _dbus_list_unlink (&service->owners, swap_link); + + _dbus_list_insert_after_link (&service->owners, + _dbus_list_get_first_link (&service->owners), + swap_link); + + return TRUE; +} + /* this function is self-cancelling if you cancel the transaction */ dbus_bool_t bus_service_remove_owner (BusService *service, - DBusConnection *owner, + DBusConnection *connection, BusTransaction *transaction, DBusError *error) { + BusOwner *primary_owner; + _DBUS_ASSERT_ERROR_IS_CLEAR (error); /* We send out notifications before we do any work we @@ -668,12 +1319,27 @@ bus_service_remove_owner (BusService *service, */ /* Send service lost message */ - if (bus_service_get_primary_owner (service) == owner) + primary_owner = bus_service_get_primary_owner (service); + if (primary_owner != NULL && primary_owner->conn == connection) { - if (!bus_driver_send_service_lost (owner, service->name, + if (!bus_driver_send_service_lost (connection, service->name, transaction, error)) return FALSE; } + else + { + /* if we are not the primary owner then just remove us from the queue */ + DBusList *link; + BusOwner *temp_owner; + + link = _bus_service_find_owner_link (service, connection); + _dbus_list_unlink (&service->owners, link); + temp_owner = (BusOwner *)link->data; + bus_owner_unref (temp_owner); + _dbus_list_free_link (link); + + return TRUE; + } if (service->owners == NULL) { @@ -681,34 +1347,46 @@ bus_service_remove_owner (BusService *service, } else if (_dbus_list_length_is_one (&service->owners)) { - if (!bus_driver_send_service_deleted (service->name, - transaction, error)) + if (!bus_driver_send_service_owner_changed (service->name, + bus_connection_get_name (connection), + NULL, + transaction, error)) return FALSE; } else { DBusList *link; - link = _dbus_list_get_first (&service->owners); + BusOwner *new_owner; + DBusConnection *new_owner_conn; + link = _dbus_list_get_first_link (&service->owners); _dbus_assert (link != NULL); link = _dbus_list_get_next_link (&service->owners, link); - _dbus_assert (link != NULL); + new_owner = (BusOwner *)link->data; + new_owner_conn = new_owner->conn; + + if (!bus_driver_send_service_owner_changed (service->name, + bus_connection_get_name (connection), + bus_connection_get_name (new_owner_conn), + transaction, error)) + return FALSE; + /* This will be our new owner */ - if (!bus_driver_send_service_acquired (link->data, + if (!bus_driver_send_service_acquired (new_owner_conn, service->name, transaction, error)) return FALSE; } - if (!add_restore_ownership_to_transaction (transaction, service, owner)) + if (!add_restore_ownership_to_transaction (transaction, service, primary_owner)) { BUS_SET_OOM (error); return FALSE; } - - bus_service_unlink_owner (service, owner); + + bus_service_unlink_owner (service, primary_owner); if (service->owners == NULL) bus_service_unlink (service); @@ -742,7 +1420,20 @@ bus_service_unref (BusService *service) } } -DBusConnection* +DBusConnection * +bus_service_get_primary_owners_connection (BusService *service) +{ + BusOwner *owner; + + owner = bus_service_get_primary_owner (service); + + if (owner != NULL) + return owner->conn; + else + return NULL; +} + +BusOwner* bus_service_get_primary_owner (BusService *service) { return _dbus_list_get_first (&service->owners); @@ -754,34 +1445,64 @@ bus_service_get_name (BusService *service) return service->name; } -void -bus_service_set_prohibit_replacement (BusService *service, - dbus_bool_t prohibit_replacement) +dbus_bool_t +bus_service_get_allow_replacement (BusService *service) { - service->prohibit_replacement = prohibit_replacement != FALSE; + BusOwner *owner; + DBusList *link; + + _dbus_assert (service->owners != NULL); + + link = _dbus_list_get_first_link (&service->owners); + owner = (BusOwner *) link->data; + + return owner->allow_replacement; } dbus_bool_t -bus_service_get_prohibit_replacement (BusService *service) +bus_service_has_owner (BusService *service, + DBusConnection *connection) { - return service->prohibit_replacement; + DBusList *link; + + link = _bus_service_find_owner_link (service, connection); + + if (link == NULL) + return FALSE; + else + return TRUE; } -dbus_bool_t -bus_service_has_owner (BusService *service, - DBusConnection *owner) +dbus_bool_t +bus_service_list_queued_owners (BusService *service, + DBusList **return_list, + DBusError *error) { DBusList *link; + _dbus_assert (*return_list == NULL); + link = _dbus_list_get_first_link (&service->owners); + _dbus_assert (link != NULL); while (link != NULL) { - if (link->data == owner) - return TRUE; - + BusOwner *owner; + const char *uname; + + owner = (BusOwner *) link->data; + uname = bus_connection_get_name (owner->conn); + + if (!_dbus_list_append (return_list, (char *)uname)) + goto oom; + link = _dbus_list_get_next_link (&service->owners, link); } - + + return TRUE; + + oom: + _dbus_list_clear (return_list); + BUS_SET_OOM (error); return FALSE; }