summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacek Bukarewicz <j.bukarewicz@samsung.com>2014-11-28 12:07:39 +0100
committerJacek Bukarewicz <j.bukarewicz@samsung.com>2014-12-12 17:37:42 +0100
commit8966dcc678630ba6420b75261f996ac068088d04 (patch)
tree3c701b1ccf54a613014f5411a439086cade62ba9
parent9e1bbf442bb9e565ff84c77e5a64c61eefa049d0 (diff)
downloaddbus-8966dcc678630ba6420b75261f996ac068088d04.tar.gz
dbus-8966dcc678630ba6420b75261f996ac068088d04.tar.bz2
dbus-8966dcc678630ba6420b75261f996ac068088d04.zip
Disable message dispatching when send rule result is not known
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
-rw-r--r--bus/activation.c85
-rw-r--r--bus/check.c34
-rw-r--r--bus/check.h10
-rw-r--r--bus/dispatch.c187
-rw-r--r--bus/dispatch.h2
-rw-r--r--bus/driver.c15
-rw-r--r--dbus/dbus-connection-internal.h10
-rw-r--r--dbus/dbus-connection.c125
-rw-r--r--dbus/dbus-list.c29
-rw-r--r--dbus/dbus-list.h2
-rw-r--r--dbus/dbus-shared.h3
11 files changed, 469 insertions, 33 deletions
diff --git a/bus/activation.c b/bus/activation.c
index fa6c1568..6fab7e29 100644
--- a/bus/activation.c
+++ b/bus/activation.c
@@ -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
{
diff --git a/bus/check.c b/bus/check.c
index d2f418a7..a5dbaffb 100644
--- a/bus/check.c
+++ b/bus/check.c
@@ -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)
diff --git a/bus/check.h b/bus/check.h
index c3fcaf90..f3817890 100644
--- a/bus/check.h
+++ b/bus/check.h
@@ -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 */
diff --git a/bus/dispatch.c b/bus/dispatch.c
index 556715d5..bb81cc09 100644
--- a/bus/dispatch.c
+++ b/bus/dispatch.c
@@ -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,
diff --git a/bus/dispatch.h b/bus/dispatch.h
index fb5ba7a5..afba6a24 100644
--- a/bus/dispatch.h
+++ b/bus/dispatch.h
@@ -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,
diff --git a/bus/driver.c b/bus/driver.c
index 58f184c5..a9e670e5 100644
--- a/bus/driver.c
+++ b/bus/driver.c
@@ -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;
diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
index 2842f2f4..682b5bff 100644
--- a/dbus/dbus-connection-internal.h
+++ b/dbus/dbus-connection-internal.h
@@ -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,
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
index af8be962..618b956d 100644
--- a/dbus/dbus-connection.c
+++ b/dbus/dbus-connection.c
@@ -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
diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c
index c4c1856f..f84918b1 100644
--- a/dbus/dbus-list.c
+++ b/dbus/dbus-list.c
@@ -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.
diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h
index 910d7383..abe83317 100644
--- a/dbus/dbus-list.h
+++ b/dbus/dbus-list.h
@@ -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);
diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h
index 6a576704..5371d886 100644
--- a/dbus/dbus-shared.h
+++ b/dbus/dbus-shared.h
@@ -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 */