From c2f4ba585c777b731df6b6b8a165b6cc4dc5d639 Mon Sep 17 00:00:00 2001 From: Jacek Bukarewicz Date: Fri, 28 Nov 2014 12:07:39 +0100 Subject: [PATCH 2/8] Disable message dispatching when send rule result is not known MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When unicast message is sent to addressed recipient 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 without being processed. If response is received message dispatching is resumed. This time answer is attached to the message which is now processed synchronously. Receive rule result unavailability is not yet handled - such messages are rejected. Also, if message is sent to non-addressed recipient and policy result is unknown, message is silently dropped. Cherry-picked from b1b87ad9f20b2052c28431b48e81073078a745ce by Jose Bollo. Updated for dbus 1.10.20 by Scott Murray and José Bollo Signed-off-by: José Bollo Signed-off-by: Scott Murray --- bus/activation.c | 76 +++++++++++-- bus/check.c | 109 +++++++++++++++++-- bus/check.h | 10 ++ bus/cynara.c | 1 - bus/dispatch.c | 184 ++++++++++++++++++++++++++++---- bus/dispatch.h | 2 +- bus/driver.c | 12 ++- dbus/dbus-connection-internal.h | 15 +++ dbus/dbus-connection.c | 125 +++++++++++++++++++++- dbus/dbus-list.c | 29 +++++ dbus/dbus-list.h | 3 + dbus/dbus-shared.h | 3 +- 12 files changed, 528 insertions(+), 41 deletions(-) diff --git a/bus/activation.c b/bus/activation.c index f9c6c62..8301b59 100644 --- a/bus/activation.c +++ b/bus/activation.c @@ -32,6 +32,7 @@ #include "services.h" #include "test.h" #include "utils.h" +#include #include #include #include @@ -94,6 +95,8 @@ struct BusPendingActivationEntry DBusConnection *connection; dbus_bool_t auto_activation; + + dbus_bool_t is_put_back; }; typedef struct @@ -1241,20 +1244,23 @@ 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; DBusError error; + BusResult res; dbus_error_init (&error); 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)) + res = bus_dispatch_matches (transaction, + entry->connection, + addressed_recipient, + entry->activation_message, &error); + if (res == BUS_RESULT_FALSE) { /* If permission is denied, we just want to return the error * to the original method invoker; in particular, we don't @@ -1266,11 +1272,44 @@ bus_activation_send_pending_auto_activation_messages (BusActivation *activation bus_connection_send_oom_error (entry->connection, entry->activation_message); } - dbus_error_free (&error); link = next; continue; } + else if (res == 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); + } + } } link = next; @@ -1287,6 +1326,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; } @@ -2079,6 +2131,7 @@ bus_activation_activate_service (BusActivation *activation, if (service != NULL) { + BusResult res; bus_context_log (activation->context, DBUS_SYSTEM_LOG_INFO, "Activating via systemd: service name='%s' unit='%s' requested by '%s' (%s)", service_name, @@ -2086,8 +2139,17 @@ bus_activation_activate_service (BusActivation *activation, bus_connection_get_name (connection), bus_connection_get_loginfo (connection)); /* Wonderful, systemd is connected, let's just send the msg */ - retval = bus_dispatch_matches (activation_transaction, NULL, + res = bus_dispatch_matches (activation_transaction, NULL, systemd, message, error); + + if (res == BUS_RESULT_TRUE) + retval = TRUE; + else + { + retval = FALSE; + if (res == BUS_RESULT_LATER) + _dbus_verbose("Unexpectedly need time to check message from bus driver to systemd - dropping the message.\n"); + } } else { diff --git a/bus/check.c b/bus/check.c index 5b72d31..4b8a699 100644 --- a/bus/check.c +++ b/bus/check.c @@ -55,6 +55,8 @@ typedef struct BusDeferredMessage BusCheckResponseFunc response_callback; } BusDeferredMessage; +static dbus_int32_t deferred_message_data_slot = -1; + BusCheck * bus_check_new (BusContext *context, DBusError *error) { @@ -67,11 +69,19 @@ bus_check_new (BusContext *context, DBusError *error) return NULL; } + if (!dbus_message_allocate_data_slot(&deferred_message_data_slot)) + { + dbus_free(check); + BUS_SET_OOM(error); + return NULL; + } + check->refcount = 1; check->context = context; check->cynara = bus_cynara_new(check, error); if (dbus_error_is_set(error)) { + dbus_message_free_data_slot(&deferred_message_data_slot); dbus_free(check); return NULL; } @@ -98,6 +108,7 @@ bus_check_unref (BusCheck *check) if (check->refcount == 0) { bus_cynara_unref(check->cynara); + dbus_message_free_data_slot(&deferred_message_data_slot); dbus_free(check); } } @@ -114,6 +125,45 @@ 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); + + deferred_message->response = result; + _dbus_connection_enable_dispatch(deferred_message->sender); +} + +static void +deferred_message_free_function(void *data) +{ + BusDeferredMessage *deferred_message = (BusDeferredMessage *)data; + bus_deferred_message_unref(deferred_message); +} + +void +bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message) +{ + _dbus_assert(deferred_message != NULL); + _dbus_assert(deferred_message->sender != NULL); + + if (dbus_message_get_data(deferred_message->message, deferred_message_data_slot) == NULL) + { + if (dbus_message_set_data(deferred_message->message, deferred_message_data_slot, deferred_message, + deferred_message_free_function)) + bus_deferred_message_ref(deferred_message); + } + + _dbus_connection_disable_dispatch(deferred_message->sender); + deferred_message->response_callback = bus_check_enable_dispatch_callback; +} + +#ifdef DBUS_ENABLE_EMBEDDED_TESTS +BusResult (*bus_check_test_override) (DBusConnection *connection, + const char *privilege); +#endif + BusResult bus_check_privilege (BusCheck *check, DBusMessage *message, @@ -124,6 +174,7 @@ bus_check_privilege (BusCheck *check, BusDeferredMessageStatus check_type, BusDeferredMessage **deferred_message) { + BusDeferredMessage *previous_deferred_message; BusResult result = BUS_RESULT_FALSE; #ifdef DBUS_ENABLE_CYNARA BusCynara *cynara; @@ -137,16 +188,54 @@ bus_check_privilege (BusCheck *check, return BUS_RESULT_FALSE; } - /* ask policy checkers */ -#ifdef DBUS_ENABLE_CYNARA - cynara = bus_check_get_cynara(check); - result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient, - proposed_recipient, privilege, check_type, deferred_message); +#ifdef DBUS_ENABLE_EMBEDDED_TESTS + if (bus_check_test_override) + return bus_check_test_override (connection, privilege); #endif - if (result == BUS_RESULT_LATER && deferred_message != NULL) + previous_deferred_message = dbus_message_get_data(message, deferred_message_data_slot); + /* check if message blocked at sender's queue is being processed */ + if (previous_deferred_message != NULL) + { + if ((check_type & BUS_DEFERRED_MESSAGE_CHECK_SEND) && + !(previous_deferred_message->status & BUS_DEFERRED_MESSAGE_CHECK_SEND)) + { + /** + * Message has been deferred due to receive or own rule which means that sending this message + * is allowed - it must have been checked previously. + * This might happen when client calls RequestName method which depending on security + * policy might result in both "can_send" and "can_own" Cynara checks. + */ + result = BUS_RESULT_TRUE; + } + else + { + result = previous_deferred_message->response; + if (result == BUS_RESULT_LATER) + { + /* result is still not known - reuse deferred message object */ + if (deferred_message != NULL) + *deferred_message = previous_deferred_message; + } + else + { + /* result is available - we can remove deferred message from the processed message */ + dbus_message_set_data(message, deferred_message_data_slot, NULL, NULL); + } + } + } + else { - (*deferred_message)->status |= check_type; + /* ask policy checkers */ +#ifdef DBUS_ENABLE_CYNARA + cynara = bus_check_get_cynara(check); + result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient, + proposed_recipient, privilege, check_type, deferred_message); +#endif + if (result == BUS_RESULT_LATER && deferred_message != NULL) + { + (*deferred_message)->status |= check_type; + } } return result; } @@ -206,6 +295,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 c3fcaf9..d177549 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 BusResult (*bus_check_test_override) (DBusConnection *connection, + const char *privilege); +#endif + #endif /* BUS_CHECK_H */ diff --git a/bus/cynara.c b/bus/cynara.c index 57a4c45..77aed62 100644 --- a/bus/cynara.c +++ b/bus/cynara.c @@ -36,7 +36,6 @@ #include #endif - #ifdef DBUS_ENABLE_CYNARA typedef struct BusCynara { diff --git a/bus/dispatch.c b/bus/dispatch.c index d3867f7..50a22a3 100644 --- a/bus/dispatch.c +++ b/bus/dispatch.c @@ -35,6 +35,7 @@ #include "signals.h" #include "test.h" #include +#include #include #include @@ -122,7 +123,7 @@ send_one_message (DBusConnection *connection, return TRUE; } -dbus_bool_t +BusResult bus_dispatch_matches (BusTransaction *transaction, DBusConnection *sender, DBusConnection *addressed_recipient, @@ -158,13 +159,29 @@ bus_dispatch_matches (BusTransaction *transaction, message, NULL, error, &deferred_message); if (res == BUS_RESULT_FALSE) - return FALSE; + return BUS_RESULT_FALSE; else if (res == BUS_RESULT_LATER) { - dbus_set_error (error, - DBUS_ERROR_ACCESS_DENIED, - "Rejecting message because time is needed to check security policy"); - return 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) && @@ -175,14 +192,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; } } @@ -197,7 +214,7 @@ bus_dispatch_matches (BusTransaction *transaction, &recipients)) { BUS_SET_OOM (error); - return FALSE; + return BUS_RESULT_FALSE; } link = _dbus_list_get_first_link (&recipients); @@ -219,10 +236,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 @@ -409,10 +426,12 @@ bus_dispatch (DBusConnection *connection, } else if (res == 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; } @@ -514,8 +533,14 @@ 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; + if (BUS_RESULT_LATER == bus_dispatch_matches (transaction, connection, addressed_recipient, + message, &error)) + { + /* 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; + } out: if (dbus_error_is_set (&error)) @@ -5060,9 +5085,132 @@ bus_dispatch_test_conf_fail (const DBusString *test_data_dir, } #endif +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 BusResult +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 (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 (" 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 fb5ba7a..afba6a2 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 cd0a714..f414f64 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -218,6 +218,7 @@ bus_driver_send_service_owner_changed (const char *service_name, { DBusMessage *message; dbus_bool_t retval; + BusResult res; const char *null_service; _DBUS_ASSERT_ERROR_IS_CLEAR (error); @@ -253,7 +254,16 @@ bus_driver_send_service_owner_changed (const char *service_name, if (!bus_transaction_capture (transaction, NULL, NULL, message)) goto oom; - retval = bus_dispatch_matches (transaction, NULL, NULL, message, error); + res = bus_dispatch_matches (transaction, NULL, NULL, message, error); + if (res == BUS_RESULT_TRUE) + retval = TRUE; + else + { + retval = FALSE; + if (res == BUS_RESULT_LATER) + /* should never happen */ + _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly"); + } dbus_message_unref (message); return retval; diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h index 4835732..94b1c95 100644 --- a/dbus/dbus-connection-internal.h +++ b/dbus/dbus-connection-internal.h @@ -118,6 +118,21 @@ DBUS_PRIVATE_EXPORT dbus_bool_t _dbus_connection_get_linux_security_label (DBusConnection *connection, char **label_p); +DBUS_PRIVATE_EXPORT +void _dbus_connection_enable_dispatch (DBusConnection *connection); +DBUS_PRIVATE_EXPORT +void _dbus_connection_disable_dispatch (DBusConnection *connection); + +DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_connection_putback_message (DBusConnection *connection, + DBusMessage *after_message, + DBusMessage *message, + DBusError *error); + +DBUS_PRIVATE_EXPORT +dbus_bool_t _dbus_connection_remove_message (DBusConnection *connection, + DBusMessage *message); + /* if DBUS_ENABLE_STATS */ DBUS_PRIVATE_EXPORT void _dbus_connection_get_stats (DBusConnection *connection, diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index c525b6d..958968c 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -311,7 +311,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 */ @@ -439,6 +440,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 @@ -4069,6 +4103,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 @@ -4252,8 +4362,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_unlocked(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; @@ -4716,6 +4827,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"); @@ -4838,9 +4951,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 8e713c0..32ea871 100644 --- a/dbus/dbus-list.c +++ b/dbus/dbus-list.c @@ -458,6 +458,35 @@ _dbus_list_remove_last (DBusList **list, return FALSE; } +/** + * 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. diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h index 9350a0d..fee9f1b 100644 --- a/dbus/dbus-list.h +++ b/dbus/dbus-list.h @@ -68,6 +68,9 @@ DBUS_PRIVATE_EXPORT void _dbus_list_remove_link (DBusList **list, DBusList *link); DBUS_PRIVATE_EXPORT +DBusList* _dbus_list_find_first (DBusList **list, + void *data); +DBUS_PRIVATE_EXPORT DBusList* _dbus_list_find_last (DBusList **list, void *data); DBUS_PRIVATE_EXPORT diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h index 7ab9103..e5bfbed 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 */ -- 2.21.1