X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=bus%2Fservices.c;h=90a2f5f18cd556c9e695b06a093ee075c47d8fe7;hb=c1a77d2c58c78abc606f1cb7918704596ebf2bfe;hp=1036947ae6de5992ec70f98a3248108fa52b393c;hpb=935a41a04c3f638134fa905503fc41ddbd18902f;p=platform%2Fupstream%2Fdbus.git diff --git a/bus/services.c b/bus/services.c index 1036947..90a2f5f 100644 --- a/bus/services.c +++ b/bus/services.c @@ -1,8 +1,9 @@ -/* -*- 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 + * Copyright (C) 2013 Samsung Electronics * * Licensed under the Academic Free License version 2.1 * @@ -18,13 +19,15 @@ * * 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 "driver.h" #include "services.h" #include "connection.h" @@ -34,6 +37,18 @@ #include "bus.h" #include "selinux.h" +#ifdef ENABLE_KDBUS_TRANSPORT +#include +#include +#include +#include +#include + +#include "kdbus-d.h" +#include "dbus/kdbus.h" +#include "dbus/kdbus-common.h" +#endif + struct BusService { int refcount; @@ -41,8 +56,20 @@ 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; +#ifdef ENABLE_KDBUS_TRANSPORT + unsigned int is_kdbus_starter : 1; +#endif }; struct BusRegistry @@ -53,6 +80,7 @@ struct BusRegistry DBusHashTable *service_hash; DBusMemPool *service_pool; + DBusMemPool *owner_pool; DBusHashTable *service_sid_table; }; @@ -76,9 +104,16 @@ 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; @@ -109,6 +144,8 @@ 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); @@ -128,10 +165,127 @@ 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; +} + +#ifdef ENABLE_KDBUS_TRANSPORT +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; +} +#endif + +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; + +#ifdef ENABLE_KDBUS_TRANSPORT + owner->is_kdbus_starter = + (flags & KDBUS_NAME_STARTER_NAME) != FALSE; +#endif +} + +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) { @@ -139,7 +293,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, @@ -156,17 +310,22 @@ 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; } + _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_if_created), + bus_connection_get_name (owner_connection_if_created), transaction, error)) { bus_service_unref (service); @@ -180,8 +339,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; @@ -195,7 +354,7 @@ bus_registry_ensure (BusRegistry *registry, BUS_SET_OOM (error); return NULL; } - + return service; } @@ -253,7 +412,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; @@ -269,21 +428,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; } @@ -291,7 +452,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)); @@ -301,6 +462,18 @@ bus_registry_acquire_service (BusRegistry *registry, goto out; } + if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS)) + { + 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; + } + policy = bus_connection_get_policy (connection); _dbus_assert (policy != NULL); @@ -308,115 +481,476 @@ bus_registry_acquire_service (BusRegistry *registry, * in bus_connection_selinux_allows_acquire_service() */ sid = bus_selinux_id_table_lookup (registry->service_sid_table, - service_name); + service_name); if (!bus_selinux_allows_acquire_service (connection, sid, - _dbus_string_get_const_data (service_name))) + _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; + } + + service = bus_registry_lookup (registry, service_name); + + if (service != NULL) + { + 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_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; +} + +#ifdef ENABLE_KDBUS_TRANSPORT +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; + const char* conn_unique_name; + DBusConnection* phantom; + unsigned long int uid; + + 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", name); + + _dbus_verbose ("Attempt to acquire invalid service name\n"); + + return FALSE; + } + + 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\"", name); + + _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"", name); + + return FALSE; + } + + conn_unique_name = dbus_message_get_sender(message); + + if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS)) + { + 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", + conn_unique_name, DBUS_SERVICE_DBUS); + return FALSE; + } + + sender_id = sender_name_to_id(conn_unique_name, error); + if(dbus_error_is_set(error)) + return FALSE; + + phantom = bus_connections_find_conn_by_name(bus_connection_get_connections(connection), conn_unique_name); + if(phantom == NULL) + { + phantom = create_phantom_connection(connection, conn_unique_name, error); + if(phantom == NULL) + return FALSE; + } + + if (!bus_client_policy_check_can_own (bus_connection_get_policy (phantom), service_name)) { 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)", + "Connection \"%s\" is not allowed to own the service \"%s\" due " + "to security policies in the configuration file", conn_unique_name, name); + goto failed; + } + + if (!kdbus_connection_get_unix_user(phantom, conn_unique_name, &uid, NULL)) + goto failed; + +#ifdef POLICY_TO_KDBUS + if (!register_kdbus_policy(name, dbus_connection_get_transport(phantom), uid)) + { + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "Kdbus error when setting policy for connection \"%s\" and service name \"%s\"", + conn_unique_name, name); + goto failed; + } +#endif + + *result = kdbus_request_name(connection, service_name, flags, sender_id); + if(*result == -EPERM) + { + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "Kdbus not allowed %s to own the service \"%s\"", + conn_unique_name, _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)) + { + service = bus_registry_lookup (registry, service_name); + if (service == NULL) + { + service = bus_registry_ensure (registry, service_name, phantom, flags, + transaction, error); + if (service == NULL) + goto failed2; + } + else + { + if (!bus_service_add_owner (service, phantom, flags, transaction, error)) + goto failed2; + } + + activation = bus_context_get_activation (registry->context); + retval = bus_activation_send_pending_auto_activation_messages (activation, + service, + transaction, + error); + } + else + retval = TRUE; + + return retval; + +failed2: + kdbus_release_name(phantom, service_name, sender_id); +failed: + bus_connection_disconnected(phantom); + + return FALSE; +} +#endif + +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 (!_dbus_validate_bus_name (service_name, 0, + _dbus_string_get_length (service_name))) + { + 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"); + goto out; } - - if (!bus_client_policy_check_can_own (policy, connection, - service_name)) + + if (_dbus_string_get_byte (service_name, 0) == ':') { - 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)", + /* 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; } - if (bus_connection_get_n_services_owned (connection) >= - bus_context_get_max_services_per_connection (registry->context)) + if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS)) { - 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)"); + /* 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); + + _dbus_verbose ("Attempt to release service name \"%s\"", + DBUS_SERVICE_DBUS); + goto out; } - + service = bus_registry_lookup (registry, service_name); - if (service != NULL) - old_owner = bus_service_get_primary_owner (service); - else - old_owner = NULL; - if (service == NULL) { - service = bus_registry_ensure (registry, - service_name, connection, transaction, error); - 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; } - current_owner = bus_service_get_primary_owner (service); + retval = TRUE; + + out: + return retval; +} + +#ifdef ENABLE_KDBUS_TRANSPORT +dbus_bool_t +bus_registry_release_service_kdbus (const char* sender_name, + DBusConnection *connection, + const DBusString *service_name, + dbus_uint32_t *result, + BusTransaction *transaction, + DBusError *error) +{ + dbus_bool_t retval = FALSE; + __u64 sender_id; - 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; } - activation = bus_context_get_activation (registry->context); - retval = bus_activation_send_pending_auto_activation_messages (activation, - service, - transaction, - error); - + sender_id = sender_name_to_id(sender_name, 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) + { + BusRegistry* registry; + BusService *service; + + registry = bus_connection_get_registry (connection); + service = bus_registry_lookup (registry, service_name); + if(service) + { + DBusConnection* phantom; + + phantom = _bus_service_find_owner_connection(service, sender_name); + if(phantom) + { + bus_service_remove_owner (service, phantom, transaction, NULL); + /* todo we could remove phantom if he doesn't own any name + * to do this we should write function in connection.c to check if + * _dbus_list_get_last (&d->services_owned) returns not NULL + * or we can leave phantom - he will be removed when he disconnects from the bus + */ + } + else + _dbus_verbose ("Didn't find phantom connection for released name!\n"); + } + } + + retval = TRUE; + out: return retval; } +#endif dbus_bool_t bus_registry_set_service_context_table (BusRegistry *registry, @@ -449,10 +983,10 @@ bus_registry_set_service_context_table (BusRegistry *registry, 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 @@ -491,7 +1025,7 @@ bus_service_relink (BusService *service, */ typedef struct { - DBusConnection *connection; /**< the connection */ + BusOwner *owner; /**< the owner */ BusService *service; /**< service to cancel ownership of */ } OwnershipCancelData; @@ -504,7 +1038,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); @@ -515,7 +1049,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); @@ -524,7 +1059,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; @@ -533,7 +1068,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)) @@ -543,18 +1078,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 @@ -562,42 +1102,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; @@ -609,7 +1190,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) { @@ -628,13 +1209,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 @@ -642,11 +1223,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 @@ -656,13 +1237,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); @@ -671,7 +1253,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; @@ -681,24 +1263,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; } @@ -707,7 +1290,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)) @@ -719,13 +1302,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 @@ -733,12 +1395,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) { @@ -747,7 +1424,7 @@ bus_service_remove_owner (BusService *service, else if (_dbus_list_length_is_one (&service->owners)) { if (!bus_driver_send_service_owner_changed (service->name, - bus_connection_get_name (owner), + bus_connection_get_name (connection), NULL, transaction, error)) return FALSE; @@ -755,35 +1432,37 @@ bus_service_remove_owner (BusService *service, else { DBusList *link; - DBusConnection *new_owner; + 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 = link->data; + new_owner = (BusOwner *)link->data; + new_owner_conn = new_owner->conn; if (!bus_driver_send_service_owner_changed (service->name, - bus_connection_get_name (owner), - bus_connection_get_name (new_owner), + 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, + 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); @@ -817,7 +1496,36 @@ bus_service_unref (BusService *service) } } -DBusConnection* +DBusConnection * +bus_service_get_primary_owners_connection (BusService *service) +{ + BusOwner *owner; +#ifdef ENABLE_KDBUS_TRANSPORT + char unique_name[(unsigned int)(snprintf((char*)NULL, 0, "%llu", ULLONG_MAX) + sizeof(":1."))]; +#endif + + owner = bus_service_get_primary_owner (service); + +#ifdef ENABLE_KDBUS_TRANSPORT + if(!owner) + return NULL; + if(bus_context_is_kdbus(service->registry->context)) + { + if(kdbus_get_name_owner(owner->conn, bus_service_get_name(service), unique_name) < 0) + return NULL; + return _bus_service_find_owner_connection(service, unique_name); //bus_connections_find_conn_by_name would be safer? but slower + } + else + return owner->conn; +#else + if (owner != NULL) + return owner->conn; + else + return NULL; +#endif +} + +BusOwner* bus_service_get_primary_owner (BusService *service) { return _dbus_list_get_first (&service->owners); @@ -829,34 +1537,83 @@ 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; } +#ifdef ENABLE_KDBUS_TRANSPORT dbus_bool_t -bus_service_get_prohibit_replacement (BusService *service) +bus_service_get_is_kdbus_starter (BusService *service) { - return service->prohibit_replacement; + BusOwner *owner; + DBusList *link; + + _dbus_assert (service->owners != NULL); + + link = _dbus_list_get_first_link (&service->owners); + owner = (BusOwner *) link->data; + + return owner->is_kdbus_starter; } +#endif dbus_bool_t bus_service_has_owner (BusService *service, - DBusConnection *owner) + DBusConnection *connection) +{ + DBusList *link; + + link = _bus_service_find_owner_link (service, connection); + + if (link == NULL) + return FALSE; + else + return TRUE; +} + +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); + +#ifdef ENABLE_KDBUS_TRANSPORT + if(!owner->is_kdbus_starter) +#endif + 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; }