kdbus: add full support for synchronous method calls 08/69108/1
authorLukasz Skalski <l.skalski@samsung.com>
Wed, 11 May 2016 13:04:21 +0000 (15:04 +0200)
committerLukasz Skalski <l.skalski@samsung.com>
Wed, 11 May 2016 13:56:12 +0000 (15:56 +0200)
Change-Id: Ib7b2115ab6a3ea76f25915eaf153772d2fa1c02f

dbus/dbus-connection.c
dbus/dbus-message-internal.h
dbus/dbus-message.c
dbus/dbus-transport-kdbus.c
dbus/dbus-transport.c
dbus/dbus-transport.h

index 5531fbb..4d0adc5 100644 (file)
@@ -3580,6 +3580,22 @@ dbus_connection_send_with_reply_and_block (DBusConnection     *connection,
   _dbus_return_val_if_fail (timeout_milliseconds >= 0 || timeout_milliseconds == -1, NULL);
   _dbus_return_val_if_error_is_set (error, NULL);
 
+  if (_dbus_transport_can_send_sync_call (connection->transport))
+    {
+      dbus_int32_t serial;
+
+      /* set serial */
+      serial = dbus_message_get_serial (message);
+      if (serial == 0)
+        {
+          serial = _dbus_connection_get_next_client_serial (connection);
+          dbus_message_set_serial (message, serial);
+        }
+
+      reply = _dbus_transport_send_sync_call (connection->transport, message);
+      goto out;
+    }
+
 #ifdef HAVE_UNIX_FD_PASSING
 
   CONNECTION_LOCK (connection);
@@ -3615,9 +3631,11 @@ dbus_connection_send_with_reply_and_block (DBusConnection     *connection,
   /* call_complete_and_unlock() called from pending_call_block() should
    * always fill this in.
    */
+
+out:
   _dbus_assert (reply != NULL);
   
-   if (dbus_set_error_from_message (error, reply))
+  if (dbus_set_error_from_message (error, reply))
     {
       dbus_message_unref (reply);
       return NULL;
index e1c27fa..88586b5 100644 (file)
@@ -122,6 +122,11 @@ void            _dbus_check_fdleaks_leave (DBusInitialFDs *fds);
 
 DBusMessage *      _dbus_message_remarshal(DBusMessage *message, dbus_bool_t gvariant);
 
+DBusMessage *      _dbus_decode_kmsg                            (DBusString  *data,
+                                                                 uint64_t     sender_id,
+                                                                 int         *fds,
+                                                                 unsigned     n_fds);
+
 DBusMessage *      _dbus_generate_local_error_message           (dbus_uint32_t serial,
                                                                  char *error_name,
                                                                  char *error_msg);
index 90f766f..0792e11 100644 (file)
@@ -4582,6 +4582,172 @@ _dbus_message_loader_queue_messages (DBusMessageLoader *loader)
   return TRUE;
 }
 
+DBusMessage *
+_dbus_decode_kmsg (DBusString  *data,
+                   uint64_t     sender_id,
+                   int         *fds,
+                   unsigned     n_fds)
+{
+  if (_dbus_string_get_length (data) >= DBUS_MINIMUM_HEADER_SIZE)
+    {
+
+      DBusValidity validity;
+      int byte_order, fields_array_len, header_len, body_len;
+      dbus_bool_t is_gvariant;
+
+      if (_dbus_header_have_message_untrusted (DBUS_MAXIMUM_MESSAGE_LENGTH,
+                                               &validity,
+                                               &byte_order,
+                                               &fields_array_len,
+                                               &header_len,
+                                               &body_len,
+                                               data, 0,
+                                               _dbus_string_get_length (data),
+                                               &is_gvariant))
+        {
+          DBusMessage *message;
+          dbus_uint32_t n_unix_fds = 0;
+          const DBusString *type_str = NULL;
+          int type_pos = 0;
+
+          _dbus_assert (validity == DBUS_VALID);
+
+          message = dbus_message_new_empty_header (is_gvariant);
+          if (message == NULL)
+            return NULL;
+
+          /*
+           * Validate and copy over header
+           */
+          _dbus_assert (_dbus_string_get_length (&message->header.data) == 0);
+          _dbus_assert ((header_len + body_len) <= _dbus_string_get_length (data));
+
+          if (!_dbus_header_load (&message->header,
+                                  DBUS_VALIDATION_MODE_DATA_IS_UNTRUSTED,
+                                  &validity,
+                                  byte_order,
+                                  fields_array_len,
+                                  header_len,
+                                  body_len,
+                                  data, 0,
+                                  _dbus_string_get_length (data)))
+            {
+              _dbus_verbose ("Failed to load header for new message code %d\n", validity);
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          _dbus_assert (validity == DBUS_VALID);
+
+          /*
+           * Validate body
+           */
+          if (_dbus_message_is_gvariant (message))
+            {
+              validity = _dbus_validate_gvariant_body_with_reason (type_str,
+                                                                   type_pos,
+                                                                   byte_order,
+                                                                   NULL,
+                                                                   data,
+                                                                   header_len,
+                                                                   body_len);
+            }
+          else
+            {
+              _dbus_verbose ("Not valid GVariant dbus message\n");
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          if (validity != DBUS_VALID)
+            {
+              _dbus_verbose ("Failed to validate message body code %d\n", validity);
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          /*
+           * Copy over Unix FDS
+           */
+          _dbus_header_get_field_basic(&message->header,
+                                       DBUS_HEADER_FIELD_UNIX_FDS,
+                                       DBUS_TYPE_UINT32,
+                                       &n_unix_fds);
+
+#ifdef HAVE_UNIX_FD_PASSING
+
+          if (n_unix_fds > n_fds)
+            {
+              _dbus_verbose("Message contains references to more unix fds than were sent %u != %u\n",
+                            n_unix_fds, loader->n_unix_fds);
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          /* If this was a recycled message there might still be
+             some memory allocated for the fds */
+          dbus_free(message->unix_fds);
+
+          if (n_unix_fds > 0)
+            {
+              message->unix_fds = _dbus_memdup(fds, n_unix_fds * sizeof(message->unix_fds[0]));
+              if (message->unix_fds == NULL)
+                {
+                  _dbus_verbose ("Failed to allocate file descriptor array\n");
+                  dbus_message_unref (message);
+                  return NULL;
+                }
+
+              message->n_unix_fds_allocated = message->n_unix_fds = n_unix_fds;
+            }
+          else
+            message->unix_fds = NULL;
+#else
+          if (n_unix_fds > 0)
+            {
+              _dbus_verbose ("Hmm, message claims to come with file descriptors "
+                             "but that's not supported on our platform, disconnecting.\n");
+              dbus_message_unref (message);
+              return NULL;
+            }
+#endif
+
+          /*
+           * Copy over message body
+           */
+          _dbus_assert (_dbus_string_get_length (&message->body) == 0);
+          _dbus_assert (_dbus_string_get_length (data) >= (header_len + body_len));
+
+          if (!_dbus_string_copy_len (data, header_len, body_len, &message->body, 0))
+            {
+              _dbus_verbose ("Failed to move body into new message\n");
+              dbus_message_unref (message);
+              return NULL;
+            }
+
+          _dbus_assert (_dbus_string_get_length (&message->header.data) == header_len);
+          _dbus_assert (_dbus_string_get_length (&message->body) == body_len);
+
+           set_unique_sender (message, sender_id);
+           message->locked = TRUE;
+
+           /* Yupi, we have DBusMessage* */
+           return message;
+        }
+      else
+        {
+          _dbus_verbose ("Data broken with invalid code %d\n", validity);
+          return NULL;
+        } /* if _dbus_header_have_message_untrusted() */
+
+    }
+  else
+    {
+      _dbus_verbose ("message size < DBUS_MINIMUM_HEADER_SIZE\n");
+      return NULL;
+    } /* if DBUS_MINIMUM_HEADER_SIZE */
+}
+
 /**
  * Peeks at first loaded message, returns #NULL if no messages have
  * been queued.
index ba96b90..c36ac64 100644 (file)
@@ -590,7 +590,6 @@ send_message (DBusTransportKdbus *transport,
           case EMLINK:
             dbus_set_error (error,
                             DBUS_ERROR_LIMITS_EXCEEDED,
-                            NULL,
                             "The maximum number of pending replies per connection has been reached");
             break;
 
@@ -598,11 +597,20 @@ send_message (DBusTransportKdbus *transport,
           case EXFULL:
             dbus_set_error (error,
                             DBUS_ERROR_LIMITS_EXCEEDED,
-                            "No space in receiver's buffer",
-                            destination);
+                            "No space in receiver's buffer");
+            break;
+
+          case ETIMEDOUT:
+            dbus_set_error (error,
+                            DBUS_ERROR_TIMEOUT,
+                            "Connection does not receive a reply");
             break;
 
           default:
+            dbus_set_error (error,
+                            DBUS_ERROR_FAILED,
+                            "Something went wrong: %s",
+                            _dbus_strerror (ret));
             break;
         }
 
@@ -681,11 +689,20 @@ can_send (DBusTransportKdbus *transport,
   return result;
 }
 
+/* function prototypes */
+static int kdbus_message_size (const struct kdbus_msg *msg,
+                               int                    *n_fds);
+static int kdbus_decode_dbus_message (const struct kdbus_msg *msg,
+                                      char                   *data,
+                                      DBusTransportKdbus     *kdbus_transport,
+                                      int                    *fds,
+                                      int                    *n_fds);
+
 static int
 kdbus_write_msg_internal (DBusTransportKdbus  *transport,
                           DBusMessage         *message,
                           const char          *destination,
-                          dbus_bool_t          check_sync_reply,
+                          DBusMessage        **sync_reply,
                           dbus_bool_t          check_privileges)
 {
   struct kdbus_msg *msg = NULL;
@@ -705,10 +722,13 @@ kdbus_write_msg_internal (DBusTransportKdbus  *transport,
   dbus_uint64_t items_size;
   dbus_uint64_t flags = 0;
   dbus_uint64_t timeout_ns_or_cookie_reply = 0;
-
+  dbus_bool_t check_sync_reply = FALSE;
 
   dbus_error_init (&error);
 
+  if (sync_reply != NULL)
+    check_sync_reply = TRUE;
+
   // determine destination and destination id
   if (destination)
     {
@@ -871,11 +891,17 @@ kdbus_write_msg_internal (DBusTransportKdbus  *transport,
         }
       else
         {
-          /* puts locally generated reply into received messages queue */
-          if (!add_message_to_received (reply, transport->base.connection))
-            ret_size = -1;
+          if (check_sync_reply)
+            {
+              *sync_reply = reply;
+            }
+          else
+            {
+              /* puts locally generated reply into received messages queue */
+              if (!add_message_to_received (reply, transport->base.connection))
+                ret_size = -1;
+            }
         }
-
       goto out;
     }
 
@@ -903,16 +929,71 @@ kdbus_write_msg_internal (DBusTransportKdbus  *transport,
         }
       else
         {
-          /* puts locally generated reply into received messages queue */
-          if (!add_message_to_received (reply, transport->base.connection))
-            ret_size = -1;
+          if (check_sync_reply)
+            {
+              *sync_reply = reply;
+            }
+          else
+            {
+              /* puts locally generated reply into received messages queue */
+              if (!add_message_to_received (reply, transport->base.connection))
+                ret_size = -1;
+            }
         }
+      goto out;
     }
 
-  if (-1 != ret_size && check_sync_reply)
-    kdbus_close_message (transport, msg_reply);
+  if (check_sync_reply)
+    {
+      DBusString str;
+      int size, ret, *fds;
+      unsigned n_fds;
+      char *data;
+
+      /* check message size */
+      size = kdbus_message_size (msg_reply, &n_fds);
+      if (size <= 0)
+        {
+          ret_size = -1;
+          kdbus_close_message (transport, msg_reply);
+          goto out;
+        }
+
+      /* alloc buffer for decoded message */
+      data = dbus_malloc (sizeof (char) * size);
+      if (data == NULL)
+        {
+          ret_size = -1;
+          kdbus_close_message (transport, msg_reply);
+          goto out;
+        }
+
+      /* alloc palce for fds */
+      fds = dbus_malloc (sizeof (int) * (n_fds + 1));
+
+      /* decode dbus message */
+      ret = kdbus_decode_dbus_message (msg_reply, data,
+                                       transport, fds, &n_fds);
+      if (ret <= 0)
+        {
+          ret_size = -1;
+          kdbus_close_message (transport, msg_reply);
+          dbus_free (data);
+          goto out;
+        }
+      _dbus_string_init_const_len (&str, data, ret);
+
+      /* create DBusMessage from kmsg */
+      *sync_reply = _dbus_decode_kmsg (&str, msg_reply->src_id, fds, n_fds);
+      if (*sync_reply == NULL)
+        ret_size = -1;
 
-  out:
+      kdbus_close_message (transport, msg_reply);
+      dbus_free (fds);
+      dbus_free (data);
+    }
+
+out:
   if (msg)
     _kdbus_free_msg (msg);
   if (memfd >= 0)
@@ -939,7 +1020,7 @@ kdbus_write_msg (DBusTransportKdbus  *transport,
                  DBusMessage         *message,
                  const char          *destination)
 {
-  return kdbus_write_msg_internal (transport, message, destination, FALSE, TRUE);
+  return kdbus_write_msg_internal (transport, message, destination, NULL, TRUE);
 }
 
 static dbus_uint64_t
@@ -2133,7 +2214,7 @@ capture_org_freedesktop_DBus_StartServiceByName (DBusTransportKdbus *transport,
 
       dbus_message_lock (sub_message);
 
-      if (kdbus_write_msg_internal (transport, sub_message, name, FALSE, FALSE) == -1)
+      if (kdbus_write_msg_internal (transport, sub_message, name, NULL, FALSE) == -1)
           return NULL;
       else
         {
@@ -2369,7 +2450,8 @@ get_next_client_serial (DBusTransportKdbus *transport)
  * @return the length of the kdbus message's payload.
  */
 static int
-kdbus_message_size (const struct kdbus_msg *msg)
+kdbus_message_size (const struct kdbus_msg *msg,
+                    int                    *n_fds)
 {
   const struct kdbus_item *item;
   int ret_size = 0;
@@ -2389,6 +2471,9 @@ kdbus_message_size (const struct kdbus_msg *msg)
           case KDBUS_ITEM_PAYLOAD_MEMFD:
             ret_size += item->memfd.size;
             break;
+          case KDBUS_ITEM_FDS:
+            *n_fds = (item->size - KDBUS_ITEM_HEADER_SIZE) / sizeof (int);
+            break;
           default:
             break;
         }
@@ -3136,7 +3221,7 @@ kdbus_read_message (DBusTransportKdbus *kdbus_transport,
       return -1;
     }
 
-  buf_size = kdbus_message_size (msg);
+  buf_size = kdbus_message_size (msg, n_fds);
   if (buf_size == -1)
     {
       _dbus_verbose ("kdbus error - too short message: %d (%m)\n", errno);
@@ -3177,6 +3262,56 @@ kdbus_read_message (DBusTransportKdbus *kdbus_transport,
   return ret_size;
 }
 
+static DBusMessage *
+kdbus_send_sync_call (DBusTransportKdbus  *transport,
+                      DBusMessage         *message)
+{
+  DBusMessage *reply = NULL;
+  DBusError error;
+  const char *destination;
+
+  dbus_message_lock (message);
+
+  destination = dbus_message_get_destination (message);
+  if (destination)
+    {
+      int ret;
+
+      ret = capture_org_freedesktop_DBus ((DBusTransportKdbus*)transport, destination, message, &reply);
+      if (ret < 0)
+        goto error;
+      else if (ret == 0)
+        goto out;
+    }
+
+  kdbus_write_msg_internal (transport, message, destination, &reply, TRUE);
+  if (reply == NULL)
+    goto error;
+  else
+    goto out;
+
+error:
+
+  dbus_error_init (&error);
+
+  dbus_set_error (&error, DBUS_ERROR_FAILED,
+                  "Something went wrong");
+
+  reply = reply_with_error ((char*)error.name,
+                            NULL,
+                            error.message,
+                            message);
+
+  dbus_error_free (&error);
+
+out:
+
+  _dbus_assert (reply != NULL);
+  dbus_message_lock (reply);
+
+  return reply;
+}
+
 /**
  * Copy-paste from socket transport. Only renames done.
  */
@@ -3970,6 +4105,8 @@ new_kdbus_transport (kdbus_t          *kdbus,
                                               _dbus_transport_kdbus_get_unix_user);
   _dbus_transport_set_get_unix_process_id_function (&kdbus_transport->base,
                                                     _dbus_transport_kdbus_get_unix_process_id);
+  _dbus_transport_set_send_sync_call_function (&kdbus_transport->base,
+                                               (DBusTransportSendSyncCallFunction) kdbus_send_sync_call);
   _dbus_transport_set_assure_protocol_function (&kdbus_transport->base,
                                                 _dbus_message_assure_gvariant,
                                                 DBUS_PROTOCOL_VERSION_GVARIANT);
index e3f9720..db9b037 100644 (file)
@@ -286,7 +286,7 @@ _dbus_transport_assure_protocol_version (DBusTransport *transport,
 
 DBusMessage *
 _dbus_transport_send_sync_call (DBusTransport *transport,
-                                DBusMessage  **message)
+                                DBusMessage   *message)
 {
   return transport->send_sync_call_function (transport, message);
 }
index e60857a..55182c5 100644 (file)
@@ -109,7 +109,7 @@ void               _dbus_transport_set_pending_fds_function (DBusTransport *tran
 dbus_bool_t        _dbus_transport_assure_protocol_version (DBusTransport             *transport,
                                                             DBusMessage              **message);
 DBusMessage*       _dbus_transport_send_sync_call          (DBusTransport             *transport,
-                                                            DBusMessage              **message);
+                                                            DBusMessage               *message);
 
 
 /* if DBUS_ENABLE_STATS */