Merge "Optional autogen.sh flag --enable-kdbus-transport added allowing to compile...
[platform/upstream/dbus.git] / bus / services.c
index 68a7022..8a18627 100644 (file)
@@ -3,6 +3,7 @@
  *
  * 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
  * 
 #include <dbus/dbus-list.h>
 #include <dbus/dbus-mempool.h>
 #include <dbus/dbus-marshal-validate.h>
+#ifdef ENABLE_KDBUS_TRANSPORT
+#include <linux/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#endif
 
 #include "driver.h"
 #include "services.h"
 #include "policy.h"
 #include "bus.h"
 #include "selinux.h"
+#ifdef ENABLE_KDBUS_TRANSPORT
+#include "kdbus-d.h"
+#include "dbus/kdbus.h"
+#endif
 
 struct BusService
 {
@@ -55,6 +65,9 @@ struct BusOwner
 
   unsigned int allow_replacement : 1;
   unsigned int do_not_queue : 1;
+#ifdef ENABLE_KDBUS_TRANSPORT
+  unsigned int is_kdbus_starter : 1;
+#endif
 };
 
 struct BusRegistry
@@ -172,6 +185,30 @@ _bus_service_find_owner_link (BusService *service,
   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)
@@ -181,6 +218,11 @@ bus_owner_set_flags (BusOwner *owner,
 
    owner->do_not_queue =
         (flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) != FALSE;
+
+#ifdef ENABLE_KDBUS_TRANSPORT
+   owner->is_kdbus_starter =
+        (flags & KDBUS_NAME_STARTER) != FALSE;
+#endif
 }
 
 static BusOwner *
@@ -310,7 +352,7 @@ bus_registry_ensure (BusRegistry               *registry,
       BUS_SET_OOM (error);
       return NULL;
     }
-  
+
   return service;
 }
 
@@ -368,7 +410,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;
@@ -437,164 +479,321 @@ 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), error))
-    {
+       {
 
-      if (dbus_error_is_set (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, connection,
-                                        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;
-    }
+         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)
-    {
-      primary_owner = bus_service_get_primary_owner (service);
-      if (primary_owner != NULL)
-        old_owner_conn = primary_owner->conn;
-      else
-        old_owner_conn = 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;
-      
+       old_owner_conn = NULL;
+
   if (service == NULL)
-    {
-      service = bus_registry_ensure (registry,
-                                     service_name, connection, flags,
-                                     transaction, error);
-      if (service == NULL)
-        goto out;
-    }
+       {
+         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;
+       goto out;
 
   if (old_owner_conn == NULL)
-    {
-      _dbus_assert (primary_owner->conn == connection);
+       {
+         _dbus_assert (primary_owner->conn == connection);
 
-      *result = DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;      
-    }
+         *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;
-    }
+       {
+         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))) ||
+                  !(bus_service_get_allow_replacement (service))) ||
           ((flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
-           !(flags & DBUS_NAME_FLAG_REPLACE_EXISTING))) 
+                  !(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;
+  dbus_bool_t rm_owner_daemon = FALSE;
+
+  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)))
     {
-      DBusList *link;
-      BusOwner *temp_owner;
-    /* Since we can't be queued if we are already in the queue
-       remove us */
+      dbus_set_error (error, DBUS_ERROR_INVALID_ARGS,
+                      "Requested bus name \"%s\" is not valid",
+                      _dbus_string_get_const_data (service_name));
 
-      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;
+      _dbus_verbose ("Attempt to acquire invalid service name\n");
+
+      goto out;
     }
-  else if (!(flags & DBUS_NAME_FLAG_DO_NOT_QUEUE) &&
-           (!(flags & DBUS_NAME_FLAG_REPLACE_EXISTING) ||
-           !(bus_service_get_allow_replacement (service))))
+
+  if (_dbus_string_get_byte (service_name, 0) == ':')
     {
-      /* Queue the connection */
-      if (!bus_service_add_owner (service, connection, 
-                                  flags,
-                                  transaction, error))
-        goto out;
-      
-      *result = DBUS_REQUEST_NAME_REPLY_IN_QUEUE;
+      /* 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;
     }
-  else
+
+  if (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS))
     {
-      /* Replace the current owner */
+      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;
+    }
 
-      /* 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;
+       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;
+
+               rm_owner_daemon = TRUE;
+               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;
+               }
+       }
 
-      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;
-    }
+       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;
+           const char* name;
+//         DBusList *link;
+
+           name = dbus_message_get_sender(message);
+           phantom = bus_connections_find_conn_by_name(bus_connection_get_connections(connection), name);
+        if(phantom == NULL)
+            phantom = create_phantom_connection(connection, name, error);
+           if(phantom == NULL)
+               goto failed2;
+           if (!bus_service_add_owner (service, phantom, flags, transaction, error))
+               goto failed2;
+           if((*result == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) && rm_owner_daemon)
+           {
+            /* 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);
+           }
+           /*if(bus_service_get_is_kdbus_starter(service))
+           {
+               if (!bus_service_swap_owner (service, bus_service_get_primary_owners_connection(service),
+                                              transaction, error))
+                   goto failed2;
+           }*/
+           /*if((link = _bus_service_find_owner_link (service, connection)))  //if daemon is a starter
+           {
+               _dbus_list_unlink (&service->owners, link);
+               _dbus_list_append_link (&service->owners, link);  //it must be moved at the end of the queue
+           }*/
+       }
 
   activation = bus_context_get_activation (registry->context);
   retval = bus_activation_send_pending_auto_activation_messages (activation,
                                                                 service,
                                                                 transaction,
                                                                 error);
-  
  out:
-  return retval;
+     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;
 }
+#endif
 
 dbus_bool_t
 bus_registry_release_service (BusRegistry      *registry,
@@ -673,6 +872,95 @@ bus_registry_release_service (BusRegistry      *registry,
   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 (!_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 (_dbus_string_get_byte (service_name, 0) == ':')
+    {
+      /* 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 (_dbus_string_equal_c_str (service_name, DBUS_SERVICE_DBUS))
+    {
+      /* 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;
+    }
+
+    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,
                                        DBusHashTable *table)
@@ -801,7 +1089,7 @@ add_cancel_ownership_to_transaction (BusTransaction *transaction,
   bus_service_ref (d->service);
   bus_owner_ref (owner);
   dbus_connection_ref (d->owner->conn);
+
   return TRUE;
 }
 
@@ -1256,6 +1544,22 @@ bus_service_get_allow_replacement (BusService *service)
   return owner->allow_replacement;
 }
 
+#ifdef ENABLE_KDBUS_TRANSPORT
+dbus_bool_t
+bus_service_get_is_kdbus_starter (BusService *service)
+{
+  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 *connection)
@@ -1290,8 +1594,11 @@ bus_service_list_queued_owners (BusService *service,
       owner = (BusOwner *) link->data;
       uname = bus_connection_get_name (owner->conn);
 
-      if (!_dbus_list_append (return_list, (char *)uname))
-        goto oom;
+#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);
     }