Disable message dispatching when send rule result is not known 24/31024/3
authorJacek Bukarewicz <j.bukarewicz@samsung.com>
Fri, 28 Nov 2014 11:07:39 +0000 (12:07 +0100)
committerJacek Bukarewicz <j.bukarewicz@samsung.com>
Fri, 12 Dec 2014 16:37:42 +0000 (17:37 +0100)
When unicast message to addressed recipient is sent and policy result
is not available message dispatch from the sender is disabled.
This also means that any further messages from the given connection are
put into the incoming queue. If response is received message dispatching
is resumed. This time answer is expected to be in cache so the message is
processed synchronously.
Receive rule result unavailability is not yet handled - such messages are
rejected. Also, if message is sent to non-addressed recipient message
is silently dropped.

Change-Id: Ia45905baf667ca42f386c1def108eca190d615bb

bus/activation.c
bus/check.c
bus/check.h
bus/dispatch.c
bus/dispatch.h
bus/driver.c
dbus/dbus-connection-internal.h
dbus/dbus-connection.c
dbus/dbus-list.c
dbus/dbus-list.h
dbus/dbus-shared.h

index fa6c156..6fab7e2 100644 (file)
@@ -31,6 +31,7 @@
 #include "services.h"
 #include "test.h"
 #include "utils.h"
+#include <dbus/dbus-connection-internal.h>
 #include <dbus/dbus-internals.h>
 #include <dbus/dbus-hash.h>
 #include <dbus/dbus-list.h>
@@ -91,6 +92,8 @@ struct BusPendingActivationEntry
   DBusConnection *connection;
 
   dbus_bool_t auto_activation;
+
+  dbus_bool_t is_put_back;
 };
 
 typedef struct
@@ -1183,18 +1186,59 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
       BusPendingActivationEntry *entry = link->data;
       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
 
-      if (entry->auto_activation && (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
+      if (entry->auto_activation && !entry->is_put_back &&
+          (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
         {
           DBusConnection *addressed_recipient;
 
           addressed_recipient = bus_service_get_primary_owners_connection (service);
 
           /* Resume dispatching where we left off in bus_dispatch() */
-          if (!bus_dispatch_matches (transaction,
-                                     entry->connection,
-                                     addressed_recipient,
-                                     entry->activation_message, error))
-            goto error;
+          switch (bus_dispatch_matches (transaction,
+                                        entry->connection,
+                                        addressed_recipient,
+                                        entry->activation_message, error))
+            {
+            case BUS_RESULT_TRUE:
+              break;
+            case BUS_RESULT_FALSE:
+              goto error;
+            case BUS_RESULT_LATER:
+              {
+                DBusList *putback_message_link = link;
+                DBusMessage *last_inserted_message = NULL;
+
+                /* NULL entry->connection implies sending pending ActivationRequest message to systemd */
+                if (entry->connection == NULL)
+                  {
+                    _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly when sender is NULL");
+                    link = next;
+                    continue;
+                  }
+
+                /**
+                 * Getting here means that policy check result is not yet available and dispatching
+                 * messages from entry->connection has been disabled.
+                 * Let's put back all messages for the given connection in the incoming queue and mark
+                 * this entry as put back so they are not handled twice.
+                 */
+                while (putback_message_link != NULL)
+                  {
+                    BusPendingActivationEntry *putback_message = putback_message_link->data;
+                    if (putback_message->connection == entry->connection)
+                      {
+                        if (!_dbus_connection_putback_message (putback_message->connection, last_inserted_message,
+                              putback_message->activation_message, error))
+                          goto error;
+                        last_inserted_message = putback_message->activation_message;
+                        putback_message->is_put_back = TRUE;
+                      }
+
+                    putback_message_link = _dbus_list_get_next_link(&pending_activation->entries, putback_message_link);
+                  }
+                break;
+              }
+            }
         }
 
       link = next;
@@ -1212,6 +1256,19 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
   return TRUE;
 
  error:
+  /* remove all messages that have been put to connections' incoming queues */
+  link = _dbus_list_get_first_link (&pending_activation->entries);
+  while (link != NULL)
+    {
+      BusPendingActivationEntry *entry = link->data;
+      if (entry->is_put_back)
+        {
+          _dbus_connection_remove_message(entry->connection, entry->activation_message);
+          entry->is_put_back = FALSE;
+        }
+      link = _dbus_list_get_next_link(&pending_activation->entries, link);
+    }
+
   return FALSE;
 }
 
@@ -1976,8 +2033,20 @@ bus_activation_activate_service (BusActivation  *activation,
                                service_name,
                                entry->systemd_service);
               /* Wonderful, systemd is connected, let's just send the msg */
-              retval = bus_dispatch_matches (activation_transaction, NULL, bus_service_get_primary_owners_connection (service),
-                                             message, error);
+              switch (bus_dispatch_matches (activation_transaction, NULL, bus_service_get_primary_owners_connection (service),
+                                            message, error))
+                {
+                case BUS_RESULT_TRUE:
+                  retval = TRUE;
+                  break;
+                case BUS_RESULT_FALSE:
+                  retval = FALSE;
+                  break;
+                case BUS_RESULT_LATER:
+                  _dbus_verbose("Unexpectedly need time to check message from bus driver to systemd - dropping the message.\n");
+                  retval = FALSE;
+                  break;
+                }
             }
           else
             {
index d2f418a..a5dbaff 100644 (file)
@@ -114,6 +114,29 @@ bus_check_get_cynara (BusCheck *check)
   return check->cynara;
 }
 
+static void
+bus_check_enable_dispatch_callback (BusDeferredMessage *deferred_message,
+                                    BusResult result)
+{
+  _dbus_verbose("bus_check_enable_dispatch_callback called deferred_message=%p\n", deferred_message);
+  _dbus_connection_enable_dispatch(deferred_message->sender);
+}
+
+void
+bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message)
+{
+  _dbus_assert(deferred_message != NULL);
+  _dbus_assert(deferred_message->sender != NULL);
+
+  _dbus_connection_disable_dispatch(deferred_message->sender);
+  deferred_message->response_callback = bus_check_enable_dispatch_callback;
+}
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+dbus_bool_t (*bus_check_test_override) (DBusConnection *connection,
+                                        const char *privilege);
+#endif
+
 BusResult
 bus_check_privilege (BusCheck *check,
                      DBusMessage *message,
@@ -135,6 +158,11 @@ bus_check_privilege (BusCheck *check,
       return BUS_RESULT_FALSE;
     }
 
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+  if (bus_check_test_override)
+    return bus_check_test_override (connection, privilege);
+#endif
+
   /* ask policy checkers */
 #ifdef DBUS_ENABLE_CYNARA
   cynara = bus_check_get_cynara(check);
@@ -204,6 +232,12 @@ bus_deferred_message_unref (BusDeferredMessage *deferred_message)
      }
 }
 
+BusDeferredMessageStatus
+bus_deferred_message_get_status (BusDeferredMessage *deferred_message)
+{
+  return deferred_message->status;
+}
+
 void
 bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
                                         BusResult result)
index c3fcaf9..f381789 100644 (file)
@@ -55,6 +55,7 @@ BusResult   bus_check_privilege   (BusCheck *check,
                                    BusDeferredMessageStatus check_type,
                                    BusDeferredMessage **deferred_message);
 
+
 BusDeferredMessage *bus_deferred_message_new                (DBusMessage *message,
                                                              DBusConnection *sender,
                                                              DBusConnection *addressed_recipient,
@@ -65,4 +66,13 @@ BusDeferredMessage *bus_deferred_message_ref                (BusDeferredMessage
 void                bus_deferred_message_unref              (BusDeferredMessage *deferred_message);
 void                bus_deferred_message_response_received  (BusDeferredMessage *deferred_message,
                                                              BusResult result);
+void                bus_deferred_message_disable_sender     (BusDeferredMessage *deferred_message);
+
+BusDeferredMessageStatus  bus_deferred_message_get_status   (BusDeferredMessage *deferred_message);
+
+#ifdef DBUS_ENABLE_EMBEDDED_TESTS
+extern dbus_bool_t (*bus_check_test_override) (DBusConnection *connection,
+                                               const char *privilege);
+#endif
+
 #endif /* BUS_CHECK_H */
index 556715d..bb81cc0 100644 (file)
@@ -81,7 +81,7 @@ send_one_message (DBusConnection *connection,
   return TRUE;
 }
 
-dbus_bool_t
+BusResult
 bus_dispatch_matches (BusTransaction *transaction,
                       DBusConnection *sender,
                       DBusConnection *addressed_recipient,
@@ -121,10 +121,28 @@ bus_dispatch_matches (BusTransaction *transaction,
           case BUS_RESULT_FALSE:
             return BUS_RESULT_FALSE;
           case BUS_RESULT_LATER:
-            dbus_set_error (error,
-                            DBUS_ERROR_ACCESS_DENIED,
-                            "Rejecting message because time is needed to check security policy");
-            return BUS_RESULT_FALSE;
+            {
+              BusDeferredMessageStatus status;
+              status = bus_deferred_message_get_status(deferred_message);
+
+              if (status & BUS_DEFERRED_MESSAGE_CHECK_SEND)
+                {
+                  /* send rule result not available - disable dispatching messages from the sender */
+                  bus_deferred_message_disable_sender(deferred_message);
+                  return BUS_RESULT_LATER;
+                }
+              else if (status & BUS_DEFERRED_MESSAGE_CHECK_RECEIVE)
+                {
+                   dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+                              "Rejecting message because time is needed to check security policy");
+                   return BUS_RESULT_FALSE;
+                }
+              else
+                {
+                  _dbus_verbose("deferred message has no status field set to send or receive unexpectedly\n");
+                  return BUS_RESULT_FALSE;
+                }
+            }
         }
 
       if (dbus_message_contains_unix_fds (message) &&
@@ -135,14 +153,14 @@ bus_dispatch_matches (BusTransaction *transaction,
                           DBUS_ERROR_NOT_SUPPORTED,
                           "Tried to send message with Unix file descriptors"
                           "to a client that doesn't support that.");
-          return FALSE;
-      }
+          return BUS_RESULT_FALSE;
+        }
 
       /* Dispatch the message */
       if (!bus_transaction_send (transaction, addressed_recipient, message))
         {
           BUS_SET_OOM (error);
-          return FALSE;
+          return BUS_RESULT_FALSE;
         }
     }
 
@@ -157,7 +175,7 @@ bus_dispatch_matches (BusTransaction *transaction,
                                       &recipients))
     {
       BUS_SET_OOM (error);
-      return FALSE;
+      return BUS_RESULT_FALSE;
     }
 
   link = _dbus_list_get_first_link (&recipients);
@@ -179,10 +197,10 @@ bus_dispatch_matches (BusTransaction *transaction,
   if (dbus_error_is_set (&tmp_error))
     {
       dbus_move_error (&tmp_error, error);
-      return FALSE;
+      return BUS_RESULT_FALSE;
     }
   else
-    return TRUE;
+    return BUS_RESULT_TRUE;
 }
 
 static DBusHandlerResult
@@ -298,10 +316,12 @@ bus_dispatch (DBusConnection *connection,
           _dbus_verbose ("Security policy rejected message\n");
           goto out;
         case BUS_RESULT_LATER:
-          dbus_set_error (&error,
-                          DBUS_ERROR_ACCESS_DENIED,
-                          "Rejecting message because time is needed to check security policy");
-          _dbus_verbose ("Security policy needs time to check policy. Dropping message\n");
+        /* Disable dispatching messages from the sender,
+         * roll back and dispatch the message once the policy result is available */
+          bus_deferred_message_disable_sender(deferred_message);
+          bus_transaction_cancel_and_free (transaction);
+          transaction = NULL;
+          result = DBUS_HANDLER_RESULT_LATER;
           goto out;
         }
 
@@ -366,8 +386,18 @@ bus_dispatch (DBusConnection *connection,
    * addressed_recipient == NULL), and match it against other connections'
    * match rules.
    */
-  if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
-    goto out;
+  switch (bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
+    {
+    case BUS_RESULT_TRUE:
+    case BUS_RESULT_FALSE:
+      break;
+    case BUS_RESULT_LATER:
+      /* Roll back and dispatch the message once the policy result is available */
+      bus_transaction_cancel_and_free (transaction);
+      transaction = NULL;
+      result = DBUS_HANDLER_RESULT_LATER;
+      break;
+    }
 
  out:
   if (dbus_error_is_set (&error))
@@ -4714,9 +4744,132 @@ bus_dispatch_test_conf_fail (const DBusString *test_data_dir,
   return TRUE;
 }
 
+typedef struct {
+  DBusTimeout *timeout;
+  DBusConnection *connection;
+  dbus_bool_t timedout;
+  int check_counter;
+} BusTestCheckData;
+
+static BusTestCheckData *cdata;
+
+static dbus_bool_t
+bus_dispatch_test_check_timeout (void *data)
+{
+  _dbus_verbose ("timeout triggered - pretend that privilege check result is available\n");
+
+  /* should only happen once during the test */
+  _dbus_assert (!cdata->timedout);
+  cdata->timedout = TRUE;
+  _dbus_connection_enable_dispatch (cdata->connection);
+
+  /* don't call this again */
+  _dbus_loop_remove_timeout (bus_connection_get_loop (cdata->connection),
+                             cdata->timeout);
+  dbus_connection_unref (cdata->connection);
+  cdata->connection = NULL;
+  return TRUE;
+}
+
+static dbus_bool_t
+bus_dispatch_test_check_override (DBusConnection *connection,
+                                  const char *privilege)
+{
+  _dbus_verbose ("overriding privilege check %s #%d\n", privilege, cdata->check_counter);
+  cdata->check_counter++;
+  if (!cdata->timedout)
+    {
+      dbus_bool_t added;
+
+      /* Should be the first privilege check for the "Echo" method. */
+      _dbus_assert (cdata->check_counter == 1);
+      cdata->timeout = _dbus_timeout_new (1, bus_dispatch_test_check_timeout,
+                                          NULL, NULL);
+      _dbus_assert (cdata->timeout);
+      added = _dbus_loop_add_timeout (bus_connection_get_loop (connection),
+                                      cdata->timeout);
+      _dbus_assert (added);
+      cdata->connection = connection;
+      dbus_connection_ref (connection);
+      _dbus_connection_disable_dispatch (connection);
+      return BUS_RESULT_LATER;
+    }
+  else
+    {
+      /* Should only be checked one more time, and this time succeeds. */
+      _dbus_assert (cdata->check_counter == 2);
+      return BUS_RESULT_TRUE;
+    }
+}
+
+static dbus_bool_t
+bus_dispatch_test_check (const DBusString *test_data_dir)
+{
+  const char *filename = "valid-config-files/debug-check-some.conf";
+  BusContext *context;
+  DBusConnection *foo;
+  DBusError error;
+  dbus_bool_t result = TRUE;
+  BusTestCheckData data;
+
+  /* save the config name for the activation helper */
+  if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename))
+    _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG");
+
+  dbus_error_init (&error);
+
+  context = bus_context_new_test (test_data_dir, filename);
+  if (context == NULL)
+    return FALSE;
+
+  foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
+  if (foo == NULL)
+    _dbus_assert_not_reached ("could not alloc connection");
+
+  if (!bus_setup_debug_client (foo))
+    _dbus_assert_not_reached ("could not set up connection");
+
+  spin_connection_until_authenticated (context, foo);
+
+  if (!check_hello_message (context, foo))
+    _dbus_assert_not_reached ("hello message failed");
+
+  if (!check_double_hello_message (context, foo))
+    _dbus_assert_not_reached ("double hello message failed");
+
+  if (!check_add_match_all (context, foo))
+    _dbus_assert_not_reached ("AddMatch message failed");
+
+  /*
+   * Cause bus_check_send_privilege() to return BUS_RESULT_LATER in the
+   * first call, then BUS_RESULT_TRUE.
+   */
+  cdata = &data;
+  memset (cdata, 0, sizeof(*cdata));
+  bus_check_test_override = bus_dispatch_test_check_override;
+
+  result = check_existent_service_auto_start (context, foo);
+
+  _dbus_assert (cdata->check_counter == 2);
+  _dbus_assert (cdata->timedout);
+  _dbus_assert (cdata->timeout);
+  _dbus_assert (!cdata->connection);
+  _dbus_timeout_unref (cdata->timeout);
+
+  kill_client_connection_unchecked (foo);
+
+  bus_context_unref (context);
+
+  return result;
+}
+
 dbus_bool_t
 bus_dispatch_test (const DBusString *test_data_dir)
 {
+  _dbus_verbose ("<check> tests\n");
+  if (!bus_dispatch_test_check (test_data_dir))
+    return FALSE;
+
   /* run normal activation tests */
   _dbus_verbose ("Normal activation tests\n");
   if (!bus_dispatch_test_conf (test_data_dir,
index fb5ba7a..afba6a2 100644 (file)
@@ -29,7 +29,7 @@
 
 dbus_bool_t bus_dispatch_add_connection    (DBusConnection *connection);
 void        bus_dispatch_remove_connection (DBusConnection *connection);
-dbus_bool_t bus_dispatch_matches           (BusTransaction *transaction,
+BusResult   bus_dispatch_matches           (BusTransaction *transaction,
                                             DBusConnection *sender,
                                             DBusConnection *recipient,
                                             DBusMessage    *message,
index 58f184c..a9e670e 100644 (file)
@@ -129,7 +129,20 @@ bus_driver_send_service_owner_changed (const char     *service_name,
 
   _dbus_assert (dbus_message_has_signature (message, "sss"));
 
-  retval = bus_dispatch_matches (transaction, NULL, NULL, message, error);
+  switch (bus_dispatch_matches (transaction, NULL, NULL, message, error))
+    {
+    case BUS_RESULT_TRUE:
+      retval = TRUE;
+      break;
+    case BUS_RESULT_FALSE:
+      retval = FALSE;
+      break;
+    case BUS_RESULT_LATER:
+      /* should never happen */
+      _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly");
+      retval = FALSE;
+      break;
+    }
   dbus_message_unref (message);
 
   return retval;
index 2842f2f..682b5bf 100644 (file)
@@ -101,6 +101,16 @@ void              _dbus_connection_test_get_locks                 (DBusConnectio
                                                                    DBusCondVar **dispatch_cond_loc,
                                                                    DBusCondVar **io_path_cond_loc);
 
+void              _dbus_connection_enable_dispatch                (DBusConnection *connection);
+void              _dbus_connection_disable_dispatch               (DBusConnection *connection);
+dbus_bool_t       _dbus_connection_putback_message                (DBusConnection *connection,
+                                                                   DBusMessage    *after_message,
+                                                                   DBusMessage    *message,
+                                                                   DBusError      *error);
+
+dbus_bool_t       _dbus_connection_remove_message                 (DBusConnection *connection,
+                                                                   DBusMessage    *message);
+
 /* if DBUS_ENABLE_STATS */
 void _dbus_connection_get_stats (DBusConnection *connection,
                                  dbus_uint32_t  *in_messages,
index af8be96..618b956 100644 (file)
@@ -319,7 +319,8 @@ struct DBusConnection
    */
   dbus_bool_t dispatch_acquired; /**< Someone has dispatch path (can drain incoming queue) */
   dbus_bool_t io_path_acquired;  /**< Someone has transport io path (can use the transport to read/write messages) */
-  
+
+  unsigned int dispatch_disabled : 1;  /**< if true, then dispatching incoming messages is stopped until enabled again */
   unsigned int shareable : 1; /**< #TRUE if libdbus owns a reference to the connection and can return it from dbus_connection_open() more than once */
   
   unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
@@ -447,6 +448,39 @@ _dbus_connection_wakeup_mainloop (DBusConnection *connection)
     (*connection->wakeup_main_function) (connection->wakeup_main_data);
 }
 
+static void
+_dbus_connection_set_dispatch(DBusConnection *connection,
+                              dbus_bool_t disabled)
+{
+  CONNECTION_LOCK (connection);
+  if (connection->dispatch_disabled != disabled)
+    {
+      DBusDispatchStatus status;
+
+      connection->dispatch_disabled = disabled;
+      status = _dbus_connection_get_dispatch_status_unlocked (connection);
+      _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+    }
+  else
+    {
+      CONNECTION_UNLOCK (connection);
+    }
+}
+
+
+void
+_dbus_connection_enable_dispatch (DBusConnection *connection)
+{
+  _dbus_connection_set_dispatch (connection, FALSE);
+}
+
+void
+ _dbus_connection_disable_dispatch (DBusConnection *connection)
+{
+  _dbus_connection_set_dispatch (connection, TRUE);
+}
+
+
 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
 /**
  * Gets the locks so we can examine them
@@ -4067,6 +4101,82 @@ _dbus_connection_putback_message_link_unlocked (DBusConnection *connection,
       "_dbus_connection_putback_message_link_unlocked");
 }
 
+dbus_bool_t
+_dbus_connection_putback_message (DBusConnection *connection,
+                                  DBusMessage    *after_message,
+                                  DBusMessage    *message,
+                                  DBusError      *error)
+{
+  DBusDispatchStatus status;
+  DBusList *message_link = _dbus_list_alloc_link (message);
+  DBusList *after_link;
+  if (message_link == NULL)
+    {
+      _DBUS_SET_OOM (error);
+      return FALSE;
+    }
+  dbus_message_ref (message);
+
+  CONNECTION_LOCK (connection);
+  _dbus_connection_acquire_dispatch (connection);
+  HAVE_LOCK_CHECK (connection);
+
+  after_link = _dbus_list_find_first(&connection->incoming_messages, after_message);
+  _dbus_list_insert_after_link (&connection->incoming_messages, after_link, message_link);
+  connection->n_incoming += 1;
+
+  _dbus_verbose ("Message %p (%s %s %s '%s') put back into queue %p, %d incoming\n",
+                 message_link->data,
+                 dbus_message_type_to_string (dbus_message_get_type (message_link->data)),
+                 dbus_message_get_interface (message_link->data) ?
+                 dbus_message_get_interface (message_link->data) :
+                 "no interface",
+                 dbus_message_get_member (message_link->data) ?
+                 dbus_message_get_member (message_link->data) :
+                 "no member",
+                 dbus_message_get_signature (message_link->data),
+                 connection, connection->n_incoming);
+
+  _dbus_message_trace_ref (message_link->data, -1, -1,
+      "_dbus_connection_putback_message");
+
+  _dbus_connection_release_dispatch (connection);
+
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+
+  return TRUE;
+}
+
+dbus_bool_t
+_dbus_connection_remove_message (DBusConnection *connection,
+                                 DBusMessage *message)
+{
+  DBusDispatchStatus status;
+  dbus_bool_t removed;
+
+  CONNECTION_LOCK (connection);
+  _dbus_connection_acquire_dispatch (connection);
+  HAVE_LOCK_CHECK (connection);
+
+  removed = _dbus_list_remove(&connection->incoming_messages, message);
+
+  if (removed)
+    {
+      connection->n_incoming -= 1;
+      dbus_message_unref(message);
+      _dbus_verbose ("Message %p removed from incoming queue\n", message);
+    }
+  else
+      _dbus_verbose ("Message %p not found in the incoming queue\n", message);
+
+  _dbus_connection_release_dispatch (connection);
+
+  status = _dbus_connection_get_dispatch_status_unlocked (connection);
+  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
+  return removed;
+}
+
 /**
  * Returns the first-received message from the incoming message queue,
  * removing it from the queue. The caller owns a reference to the
@@ -4250,8 +4360,9 @@ static DBusDispatchStatus
 _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection)
 {
   HAVE_LOCK_CHECK (connection);
-  
-  if (connection->n_incoming > 0)
+  if (connection->dispatch_disabled && dbus_connection_get_is_connected(connection))
+    return DBUS_DISPATCH_COMPLETE;
+  else if (connection->n_incoming > 0)
     return DBUS_DISPATCH_DATA_REMAINS;
   else if (!_dbus_transport_queue_messages (connection->transport))
     return DBUS_DISPATCH_NEED_MEMORY;
@@ -4688,6 +4799,8 @@ dbus_connection_dispatch (DBusConnection *connection)
   
   CONNECTION_LOCK (connection);
 
+  if (result == DBUS_HANDLER_RESULT_LATER)
+      goto out;
   if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
     {
       _dbus_verbose ("No memory\n");
@@ -4810,9 +4923,11 @@ dbus_connection_dispatch (DBusConnection *connection)
                  connection);
   
  out:
-  if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+  if (result == DBUS_HANDLER_RESULT_LATER ||
+      result == DBUS_HANDLER_RESULT_NEED_MEMORY)
     {
-      _dbus_verbose ("out of memory\n");
+      if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+        _dbus_verbose ("out of memory\n");
       
       /* Put message back, and we'll start over.
        * Yes this means handlers must be idempotent if they
index c4c1856..f84918b 100644 (file)
@@ -459,6 +459,35 @@ _dbus_list_remove_last (DBusList **list,
 }
 
 /**
+ * Finds a value in the list. Returns the first link
+ * with value equal to the given data pointer.
+ * This is a linear-time operation.
+ * Returns #NULL if no value found that matches.
+ *
+ * @param list address of the list head.
+ * @param data the value to find.
+ * @returns the link if found
+ */
+DBusList*
+_dbus_list_find_first (DBusList **list,
+                       void      *data)
+{
+  DBusList *link;
+
+  link = _dbus_list_get_first_link (list);
+
+  while (link != NULL)
+    {
+      if (link->data == data)
+        return link;
+
+      link = _dbus_list_get_next_link (list, link);
+    }
+
+  return NULL;
+}
+
+/**
  * Finds a value in the list. Returns the last link
  * with value equal to the given data pointer.
  * This is a linear-time operation.
index 910d738..abe8331 100644 (file)
@@ -59,6 +59,8 @@ dbus_bool_t _dbus_list_remove_last        (DBusList **list,
                                            void      *data);
 void        _dbus_list_remove_link        (DBusList **list,
                                            DBusList  *link);
+DBusList*   _dbus_list_find_first         (DBusList **list,
+                                           void      *data);
 DBusList*   _dbus_list_find_last          (DBusList **list,
                                            void      *data);
 void        _dbus_list_clear              (DBusList **list);
index 6a57670..5371d88 100644 (file)
@@ -67,7 +67,8 @@ typedef enum
 {
   DBUS_HANDLER_RESULT_HANDLED,         /**< Message has had its effect - no need to run more handlers. */ 
   DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see if other handlers want it. */
-  DBUS_HANDLER_RESULT_NEED_MEMORY      /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */
+  DBUS_HANDLER_RESULT_NEED_MEMORY,     /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */
+  DBUS_HANDLER_RESULT_LATER            /**< Message dispatch deferred due to pending policy check */
 } DBusHandlerResult;
 
 /* Bus names */