1 From b1b87ad9f20b2052c28431b48e81073078a745ce Mon Sep 17 00:00:00 2001
2 From: Jacek Bukarewicz <j.bukarewicz@samsung.com>
3 Date: Fri, 28 Nov 2014 12:07:39 +0100
4 Subject: [PATCH 5/8] Disable message dispatching when send rule result is not
7 When unicast message is sent to addressed recipient and policy result
8 is not available message dispatch from the sender is disabled.
9 This also means that any further messages from the given connection are
10 put into the incoming queue without being processed. If response is received
11 message dispatching is resumed. This time answer is attached to the message
12 which is now processed synchronously.
13 Receive rule result unavailability is not yet handled - such messages are
14 rejected. Also, if message is sent to non-addressed recipient and policy result
15 is unknown, message is silently dropped.
17 Change-Id: I57eccbf973525fd51369c7d4e58908292f44da80
19 bus/activation.c | 79 +++++++++++++++--
20 bus/check.c | 109 ++++++++++++++++++++++--
23 bus/dispatch.c | 183 ++++++++++++++++++++++++++++++++++++----
26 dbus/dbus-connection-internal.h | 9 ++
27 dbus/dbus-connection.c | 125 +++++++++++++++++++++++++--
28 dbus/dbus-list.c | 29 +++++++
29 dbus/dbus-list.h | 2 +
30 dbus/dbus-shared.h | 3 +-
31 12 files changed, 522 insertions(+), 43 deletions(-)
33 diff --git a/bus/activation.c b/bus/activation.c
34 index ecd19bb..8c43941 100644
35 --- a/bus/activation.c
36 +++ b/bus/activation.c
41 +#include <dbus/dbus-connection-internal.h>
42 #include <dbus/dbus-internals.h>
43 #include <dbus/dbus-hash.h>
44 #include <dbus/dbus-list.h>
45 @@ -91,6 +92,8 @@ struct BusPendingActivationEntry
46 DBusConnection *connection;
48 dbus_bool_t auto_activation;
50 + dbus_bool_t is_put_back;
54 @@ -1180,20 +1183,23 @@ bus_activation_send_pending_auto_activation_messages (BusActivation *activation
55 BusPendingActivationEntry *entry = link->data;
56 DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
58 - if (entry->auto_activation && (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
59 + if (entry->auto_activation && !entry->is_put_back &&
60 + (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
62 DBusConnection *addressed_recipient;
66 dbus_error_init (&error);
68 addressed_recipient = bus_service_get_primary_owners_connection (service);
70 /* Resume dispatching where we left off in bus_dispatch() */
71 - if (!bus_dispatch_matches (transaction,
73 - addressed_recipient,
74 - entry->activation_message, &error))
75 + res = bus_dispatch_matches (transaction,
77 + addressed_recipient,
78 + entry->activation_message, &error);
79 + if (res == BUS_RESULT_FALSE)
81 /* If permission is denied, we just want to return the error
82 * to the original method invoker; in particular, we don't
83 @@ -1205,9 +1211,40 @@ bus_activation_send_pending_auto_activation_messages (BusActivation *activation
84 bus_connection_send_oom_error (entry->connection,
85 entry->activation_message);
88 + else if (res == BUS_RESULT_LATER)
90 + DBusList *putback_message_link = link;
91 + DBusMessage *last_inserted_message = NULL;
93 + /* NULL entry->connection implies sending pending ActivationRequest message to systemd */
94 + if (entry->connection == NULL)
96 + _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly when sender is NULL");
104 + * Getting here means that policy check result is not yet available and dispatching
105 + * messages from entry->connection has been disabled.
106 + * Let's put back all messages for the given connection in the incoming queue and mark
107 + * this entry as put back so they are not handled twice.
109 + while (putback_message_link != NULL)
111 + BusPendingActivationEntry *putback_message = putback_message_link->data;
112 + if (putback_message->connection == entry->connection)
114 + if (!_dbus_connection_putback_message (putback_message->connection, last_inserted_message,
115 + putback_message->activation_message, &error))
117 + last_inserted_message = putback_message->activation_message;
118 + putback_message->is_put_back = TRUE;
121 + putback_message_link = _dbus_list_get_next_link(&pending_activation->entries, putback_message_link);
126 @@ -1225,6 +1262,19 @@ bus_activation_send_pending_auto_activation_messages (BusActivation *activation
130 + /* remove all messages that have been put to connections' incoming queues */
131 + link = _dbus_list_get_first_link (&pending_activation->entries);
132 + while (link != NULL)
134 + BusPendingActivationEntry *entry = link->data;
135 + if (entry->is_put_back)
137 + _dbus_connection_remove_message(entry->connection, entry->activation_message);
138 + entry->is_put_back = FALSE;
140 + link = _dbus_list_get_next_link(&pending_activation->entries, link);
146 @@ -2009,13 +2059,24 @@ bus_activation_activate_service (BusActivation *activation,
151 bus_context_log (activation->context,
152 DBUS_SYSTEM_LOG_INFO, "Activating via systemd: service name='%s' unit='%s'",
154 entry->systemd_service);
155 /* Wonderful, systemd is connected, let's just send the msg */
156 - retval = bus_dispatch_matches (activation_transaction, NULL, bus_service_get_primary_owners_connection (service),
158 + res = bus_dispatch_matches (activation_transaction, NULL, bus_service_get_primary_owners_connection (service),
161 + if (res == BUS_RESULT_TRUE)
163 + else if (res == BUS_RESULT_FALSE)
165 + else if (res == BUS_RESULT_LATER)
167 + _dbus_verbose("Unexpectedly need time to check message from bus driver to systemd - dropping the message.\n");
173 diff --git a/bus/check.c b/bus/check.c
174 index d2f418a..cd6a74b 100644
177 @@ -55,6 +55,8 @@ typedef struct BusDeferredMessage
178 BusCheckResponseFunc response_callback;
179 } BusDeferredMessage;
181 +static dbus_int32_t deferred_message_data_slot = -1;
184 bus_check_new (BusContext *context, DBusError *error)
186 @@ -67,11 +69,19 @@ bus_check_new (BusContext *context, DBusError *error)
190 + if (!dbus_message_allocate_data_slot(&deferred_message_data_slot))
193 + BUS_SET_OOM(error);
198 check->context = context;
199 check->cynara = bus_cynara_new(check, error);
200 if (dbus_error_is_set(error))
202 + dbus_message_free_data_slot(&deferred_message_data_slot);
206 @@ -98,6 +108,7 @@ bus_check_unref (BusCheck *check)
207 if (check->refcount == 0)
209 bus_cynara_unref(check->cynara);
210 + dbus_message_free_data_slot(&deferred_message_data_slot);
214 @@ -114,6 +125,45 @@ bus_check_get_cynara (BusCheck *check)
215 return check->cynara;
219 +bus_check_enable_dispatch_callback (BusDeferredMessage *deferred_message,
222 + _dbus_verbose("bus_check_enable_dispatch_callback called deferred_message=%p\n", deferred_message);
224 + deferred_message->response = result;
225 + _dbus_connection_enable_dispatch(deferred_message->sender);
229 +deferred_message_free_function(void *data)
231 + BusDeferredMessage *deferred_message = (BusDeferredMessage *)data;
232 + bus_deferred_message_unref(deferred_message);
236 +bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message)
238 + _dbus_assert(deferred_message != NULL);
239 + _dbus_assert(deferred_message->sender != NULL);
241 + if (dbus_message_get_data(deferred_message->message, deferred_message_data_slot) == NULL)
243 + if (dbus_message_set_data(deferred_message->message, deferred_message_data_slot, deferred_message,
244 + deferred_message_free_function))
245 + bus_deferred_message_ref(deferred_message);
248 + _dbus_connection_disable_dispatch(deferred_message->sender);
249 + deferred_message->response_callback = bus_check_enable_dispatch_callback;
252 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
253 +dbus_bool_t (*bus_check_test_override) (DBusConnection *connection,
254 + const char *privilege);
258 bus_check_privilege (BusCheck *check,
259 DBusMessage *message,
260 @@ -124,6 +174,7 @@ bus_check_privilege (BusCheck *check,
261 BusDeferredMessageStatus check_type,
262 BusDeferredMessage **deferred_message)
264 + BusDeferredMessage *previous_deferred_message;
265 BusResult result = BUS_RESULT_FALSE;
267 DBusConnection *connection;
268 @@ -135,16 +186,54 @@ bus_check_privilege (BusCheck *check,
269 return BUS_RESULT_FALSE;
272 - /* ask policy checkers */
273 -#ifdef DBUS_ENABLE_CYNARA
274 - cynara = bus_check_get_cynara(check);
275 - result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
276 - proposed_recipient, privilege, check_type, deferred_message);
277 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
278 + if (bus_check_test_override)
279 + return bus_check_test_override (connection, privilege);
282 - if (result == BUS_RESULT_LATER && deferred_message != NULL)
283 + previous_deferred_message = dbus_message_get_data(message, deferred_message_data_slot);
284 + /* check if message blocked at sender's queue is being processed */
285 + if (previous_deferred_message != NULL)
287 + if ((check_type & BUS_DEFERRED_MESSAGE_CHECK_SEND) &&
288 + !(previous_deferred_message->status & BUS_DEFERRED_MESSAGE_CHECK_SEND))
291 + * Message has been deferred due to receive or own rule which means that sending this message
292 + * is allowed - it must have been checked previously.
293 + * This might happen when client calls RequestName method which depending on security
294 + * policy might result in both "can_send" and "can_own" Cynara checks.
296 + result = BUS_RESULT_TRUE;
300 + result = previous_deferred_message->response;
301 + if (result == BUS_RESULT_LATER)
303 + /* result is still not known - reuse deferred message object */
304 + if (deferred_message != NULL)
305 + *deferred_message = previous_deferred_message;
309 + /* result is available - we can remove deferred message from the processed message */
310 + dbus_message_set_data(message, deferred_message_data_slot, NULL, NULL);
316 - (*deferred_message)->status |= check_type;
317 + /* ask policy checkers */
318 +#ifdef DBUS_ENABLE_CYNARA
319 + cynara = bus_check_get_cynara(check);
320 + result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
321 + proposed_recipient, privilege, check_type, deferred_message);
323 + if (result == BUS_RESULT_LATER && deferred_message != NULL)
325 + (*deferred_message)->status |= check_type;
330 @@ -204,6 +293,12 @@ bus_deferred_message_unref (BusDeferredMessage *deferred_message)
334 +BusDeferredMessageStatus
335 +bus_deferred_message_get_status (BusDeferredMessage *deferred_message)
337 + return deferred_message->status;
341 bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
343 diff --git a/bus/check.h b/bus/check.h
344 index c3fcaf9..f381789 100644
347 @@ -55,6 +55,7 @@ BusResult bus_check_privilege (BusCheck *check,
348 BusDeferredMessageStatus check_type,
349 BusDeferredMessage **deferred_message);
352 BusDeferredMessage *bus_deferred_message_new (DBusMessage *message,
353 DBusConnection *sender,
354 DBusConnection *addressed_recipient,
355 @@ -65,4 +66,13 @@ BusDeferredMessage *bus_deferred_message_ref (BusDeferredMessage
356 void bus_deferred_message_unref (BusDeferredMessage *deferred_message);
357 void bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
359 +void bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message);
361 +BusDeferredMessageStatus bus_deferred_message_get_status (BusDeferredMessage *deferred_message);
363 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
364 +extern dbus_bool_t (*bus_check_test_override) (DBusConnection *connection,
365 + const char *privilege);
368 #endif /* BUS_CHECK_H */
369 diff --git a/bus/cynara.c b/bus/cynara.c
370 index 57a4c45..77aed62 100644
374 #include <cynara-client-async.h>
378 #ifdef DBUS_ENABLE_CYNARA
379 typedef struct BusCynara
381 diff --git a/bus/dispatch.c b/bus/dispatch.c
382 index ce4076d..6b0eadc 100644
385 @@ -81,7 +81,7 @@ send_one_message (DBusConnection *connection,
391 bus_dispatch_matches (BusTransaction *transaction,
392 DBusConnection *sender,
393 DBusConnection *addressed_recipient,
394 @@ -117,13 +117,29 @@ bus_dispatch_matches (BusTransaction *transaction,
397 if (res == BUS_RESULT_FALSE)
399 + return BUS_RESULT_FALSE;
400 else if (res == BUS_RESULT_LATER)
402 - dbus_set_error (error,
403 - DBUS_ERROR_ACCESS_DENIED,
404 - "Rejecting message because time is needed to check security policy");
406 + BusDeferredMessageStatus status;
407 + status = bus_deferred_message_get_status(deferred_message);
409 + if (status & BUS_DEFERRED_MESSAGE_CHECK_SEND)
411 + /* send rule result not available - disable dispatching messages from the sender */
412 + bus_deferred_message_disable_sender(deferred_message);
413 + return BUS_RESULT_LATER;
415 + else if (status & BUS_DEFERRED_MESSAGE_CHECK_RECEIVE)
417 + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
418 + "Rejecting message because time is needed to check security policy");
419 + return BUS_RESULT_FALSE;
423 + _dbus_verbose("deferred message has no status field set to send or receive unexpectedly\n");
424 + return BUS_RESULT_FALSE;
428 if (dbus_message_contains_unix_fds (message) &&
429 @@ -134,14 +150,14 @@ bus_dispatch_matches (BusTransaction *transaction,
430 DBUS_ERROR_NOT_SUPPORTED,
431 "Tried to send message with Unix file descriptors"
432 "to a client that doesn't support that.");
435 + return BUS_RESULT_FALSE;
438 /* Dispatch the message */
439 if (!bus_transaction_send (transaction, addressed_recipient, message))
443 + return BUS_RESULT_FALSE;
447 @@ -156,7 +172,7 @@ bus_dispatch_matches (BusTransaction *transaction,
452 + return BUS_RESULT_FALSE;
455 link = _dbus_list_get_first_link (&recipients);
456 @@ -178,10 +194,10 @@ bus_dispatch_matches (BusTransaction *transaction,
457 if (dbus_error_is_set (&tmp_error))
459 dbus_move_error (&tmp_error, error);
461 + return BUS_RESULT_FALSE;
465 + return BUS_RESULT_TRUE;
468 static DBusHandlerResult
469 @@ -298,10 +314,12 @@ bus_dispatch (DBusConnection *connection,
471 else if (res == BUS_RESULT_LATER)
473 - dbus_set_error (&error,
474 - DBUS_ERROR_ACCESS_DENIED,
475 - "Rejecting message because time is needed to check security policy");
476 - _dbus_verbose ("Security policy needs time to check policy. Dropping message\n");
477 + /* Disable dispatching messages from the sender,
478 + * roll back and dispatch the message once the policy result is available */
479 + bus_deferred_message_disable_sender(deferred_message);
480 + bus_transaction_cancel_and_free (transaction);
481 + transaction = NULL;
482 + result = DBUS_HANDLER_RESULT_LATER;
486 @@ -366,8 +384,14 @@ bus_dispatch (DBusConnection *connection,
487 * addressed_recipient == NULL), and match it against other connections'
490 - if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
492 + if (BUS_RESULT_LATER == bus_dispatch_matches (transaction, connection, addressed_recipient,
495 + /* Roll back and dispatch the message once the policy result is available */
496 + bus_transaction_cancel_and_free (transaction);
497 + transaction = NULL;
498 + result = DBUS_HANDLER_RESULT_LATER;
502 if (dbus_error_is_set (&error))
503 @@ -4714,9 +4738,132 @@ bus_dispatch_test_conf_fail (const DBusString *test_data_dir,
508 + DBusTimeout *timeout;
509 + DBusConnection *connection;
510 + dbus_bool_t timedout;
514 +static BusTestCheckData *cdata;
517 +bus_dispatch_test_check_timeout (void *data)
519 + _dbus_verbose ("timeout triggered - pretend that privilege check result is available\n");
521 + /* should only happen once during the test */
522 + _dbus_assert (!cdata->timedout);
523 + cdata->timedout = TRUE;
524 + _dbus_connection_enable_dispatch (cdata->connection);
526 + /* don't call this again */
527 + _dbus_loop_remove_timeout (bus_connection_get_loop (cdata->connection),
529 + dbus_connection_unref (cdata->connection);
530 + cdata->connection = NULL;
535 +bus_dispatch_test_check_override (DBusConnection *connection,
536 + const char *privilege)
538 + _dbus_verbose ("overriding privilege check %s #%d\n", privilege, cdata->check_counter);
539 + cdata->check_counter++;
540 + if (!cdata->timedout)
544 + /* Should be the first privilege check for the "Echo" method. */
545 + _dbus_assert (cdata->check_counter == 1);
546 + cdata->timeout = _dbus_timeout_new (1, bus_dispatch_test_check_timeout,
548 + _dbus_assert (cdata->timeout);
549 + added = _dbus_loop_add_timeout (bus_connection_get_loop (connection),
551 + _dbus_assert (added);
552 + cdata->connection = connection;
553 + dbus_connection_ref (connection);
554 + _dbus_connection_disable_dispatch (connection);
555 + return BUS_RESULT_LATER;
559 + /* Should only be checked one more time, and this time succeeds. */
560 + _dbus_assert (cdata->check_counter == 2);
561 + return BUS_RESULT_TRUE;
566 +bus_dispatch_test_check (const DBusString *test_data_dir)
568 + const char *filename = "valid-config-files/debug-check-some.conf";
569 + BusContext *context;
570 + DBusConnection *foo;
572 + dbus_bool_t result = TRUE;
573 + BusTestCheckData data;
575 + /* save the config name for the activation helper */
576 + if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename))
577 + _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG");
579 + dbus_error_init (&error);
581 + context = bus_context_new_test (test_data_dir, filename);
582 + if (context == NULL)
585 + foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
587 + _dbus_assert_not_reached ("could not alloc connection");
589 + if (!bus_setup_debug_client (foo))
590 + _dbus_assert_not_reached ("could not set up connection");
592 + spin_connection_until_authenticated (context, foo);
594 + if (!check_hello_message (context, foo))
595 + _dbus_assert_not_reached ("hello message failed");
597 + if (!check_double_hello_message (context, foo))
598 + _dbus_assert_not_reached ("double hello message failed");
600 + if (!check_add_match_all (context, foo))
601 + _dbus_assert_not_reached ("AddMatch message failed");
604 + * Cause bus_check_send_privilege() to return BUS_RESULT_LATER in the
605 + * first call, then BUS_RESULT_TRUE.
608 + memset (cdata, 0, sizeof(*cdata));
609 + bus_check_test_override = bus_dispatch_test_check_override;
611 + result = check_existent_service_auto_start (context, foo);
613 + _dbus_assert (cdata->check_counter == 2);
614 + _dbus_assert (cdata->timedout);
615 + _dbus_assert (cdata->timeout);
616 + _dbus_assert (!cdata->connection);
617 + _dbus_timeout_unref (cdata->timeout);
619 + kill_client_connection_unchecked (foo);
621 + bus_context_unref (context);
627 bus_dispatch_test (const DBusString *test_data_dir)
629 + _dbus_verbose ("<check> tests\n");
630 + if (!bus_dispatch_test_check (test_data_dir))
633 /* run normal activation tests */
634 _dbus_verbose ("Normal activation tests\n");
635 if (!bus_dispatch_test_conf (test_data_dir,
636 diff --git a/bus/dispatch.h b/bus/dispatch.h
637 index fb5ba7a..afba6a2 100644
642 dbus_bool_t bus_dispatch_add_connection (DBusConnection *connection);
643 void bus_dispatch_remove_connection (DBusConnection *connection);
644 -dbus_bool_t bus_dispatch_matches (BusTransaction *transaction,
645 +BusResult bus_dispatch_matches (BusTransaction *transaction,
646 DBusConnection *sender,
647 DBusConnection *recipient,
648 DBusMessage *message,
649 diff --git a/bus/driver.c b/bus/driver.c
650 index 11706f8..4dbce3d 100644
653 @@ -97,6 +97,7 @@ bus_driver_send_service_owner_changed (const char *service_name,
655 DBusMessage *message;
658 const char *null_service;
660 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
661 @@ -129,7 +130,17 @@ bus_driver_send_service_owner_changed (const char *service_name,
663 _dbus_assert (dbus_message_has_signature (message, "sss"));
665 - retval = bus_dispatch_matches (transaction, NULL, NULL, message, error);
666 + res = bus_dispatch_matches (transaction, NULL, NULL, message, error);
667 + if (res == BUS_RESULT_TRUE)
669 + else if (res == BUS_RESULT_FALSE)
671 + else if (res == BUS_RESULT_LATER)
673 + /* should never happen */
674 + _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly");
677 dbus_message_unref (message);
680 diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
681 index 64ef336..4fcd118 100644
682 --- a/dbus/dbus-connection-internal.h
683 +++ b/dbus/dbus-connection-internal.h
684 @@ -109,6 +109,15 @@ void _dbus_connection_set_pending_fds_function (DBusConnectio
686 dbus_bool_t _dbus_connection_get_linux_security_label (DBusConnection *connection,
688 +void _dbus_connection_enable_dispatch (DBusConnection *connection);
689 +void _dbus_connection_disable_dispatch (DBusConnection *connection);
690 +dbus_bool_t _dbus_connection_putback_message (DBusConnection *connection,
691 + DBusMessage *after_message,
692 + DBusMessage *message,
695 +dbus_bool_t _dbus_connection_remove_message (DBusConnection *connection,
696 + DBusMessage *message);
698 /* if DBUS_ENABLE_STATS */
699 void _dbus_connection_get_stats (DBusConnection *connection,
700 diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
701 index 8952b75..5d8d943 100644
702 --- a/dbus/dbus-connection.c
703 +++ b/dbus/dbus-connection.c
704 @@ -311,7 +311,8 @@ struct DBusConnection
706 dbus_bool_t dispatch_acquired; /**< Someone has dispatch path (can drain incoming queue) */
707 dbus_bool_t io_path_acquired; /**< Someone has transport io path (can use the transport to read/write messages) */
710 + unsigned int dispatch_disabled : 1; /**< if true, then dispatching incoming messages is stopped until enabled again */
711 unsigned int shareable : 1; /**< #TRUE if libdbus owns a reference to the connection and can return it from dbus_connection_open() more than once */
713 unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
714 @@ -439,6 +440,39 @@ _dbus_connection_wakeup_mainloop (DBusConnection *connection)
715 (*connection->wakeup_main_function) (connection->wakeup_main_data);
719 +_dbus_connection_set_dispatch(DBusConnection *connection,
720 + dbus_bool_t disabled)
722 + CONNECTION_LOCK (connection);
723 + if (connection->dispatch_disabled != disabled)
725 + DBusDispatchStatus status;
727 + connection->dispatch_disabled = disabled;
728 + status = _dbus_connection_get_dispatch_status_unlocked (connection);
729 + _dbus_connection_update_dispatch_status_and_unlock (connection, status);
733 + CONNECTION_UNLOCK (connection);
739 +_dbus_connection_enable_dispatch (DBusConnection *connection)
741 + _dbus_connection_set_dispatch (connection, FALSE);
745 + _dbus_connection_disable_dispatch (DBusConnection *connection)
747 + _dbus_connection_set_dispatch (connection, TRUE);
751 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
753 * Gets the locks so we can examine them
754 @@ -4068,6 +4102,82 @@ _dbus_connection_putback_message_link_unlocked (DBusConnection *connection,
755 "_dbus_connection_putback_message_link_unlocked");
759 +_dbus_connection_putback_message (DBusConnection *connection,
760 + DBusMessage *after_message,
761 + DBusMessage *message,
764 + DBusDispatchStatus status;
765 + DBusList *message_link = _dbus_list_alloc_link (message);
766 + DBusList *after_link;
767 + if (message_link == NULL)
769 + _DBUS_SET_OOM (error);
772 + dbus_message_ref (message);
774 + CONNECTION_LOCK (connection);
775 + _dbus_connection_acquire_dispatch (connection);
776 + HAVE_LOCK_CHECK (connection);
778 + after_link = _dbus_list_find_first(&connection->incoming_messages, after_message);
779 + _dbus_list_insert_after_link (&connection->incoming_messages, after_link, message_link);
780 + connection->n_incoming += 1;
782 + _dbus_verbose ("Message %p (%s %s %s '%s') put back into queue %p, %d incoming\n",
783 + message_link->data,
784 + dbus_message_type_to_string (dbus_message_get_type (message_link->data)),
785 + dbus_message_get_interface (message_link->data) ?
786 + dbus_message_get_interface (message_link->data) :
788 + dbus_message_get_member (message_link->data) ?
789 + dbus_message_get_member (message_link->data) :
791 + dbus_message_get_signature (message_link->data),
792 + connection, connection->n_incoming);
794 + _dbus_message_trace_ref (message_link->data, -1, -1,
795 + "_dbus_connection_putback_message");
797 + _dbus_connection_release_dispatch (connection);
799 + status = _dbus_connection_get_dispatch_status_unlocked (connection);
800 + _dbus_connection_update_dispatch_status_and_unlock (connection, status);
806 +_dbus_connection_remove_message (DBusConnection *connection,
807 + DBusMessage *message)
809 + DBusDispatchStatus status;
810 + dbus_bool_t removed;
812 + CONNECTION_LOCK (connection);
813 + _dbus_connection_acquire_dispatch (connection);
814 + HAVE_LOCK_CHECK (connection);
816 + removed = _dbus_list_remove(&connection->incoming_messages, message);
820 + connection->n_incoming -= 1;
821 + dbus_message_unref(message);
822 + _dbus_verbose ("Message %p removed from incoming queue\n", message);
825 + _dbus_verbose ("Message %p not found in the incoming queue\n", message);
827 + _dbus_connection_release_dispatch (connection);
829 + status = _dbus_connection_get_dispatch_status_unlocked (connection);
830 + _dbus_connection_update_dispatch_status_and_unlock (connection, status);
835 * Returns the first-received message from the incoming message queue,
836 * removing it from the queue. The caller owns a reference to the
837 @@ -4251,8 +4361,9 @@ static DBusDispatchStatus
838 _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection)
840 HAVE_LOCK_CHECK (connection);
842 - if (connection->n_incoming > 0)
843 + if (connection->dispatch_disabled && dbus_connection_get_is_connected(connection))
844 + return DBUS_DISPATCH_COMPLETE;
845 + else if (connection->n_incoming > 0)
846 return DBUS_DISPATCH_DATA_REMAINS;
847 else if (!_dbus_transport_queue_messages (connection->transport))
848 return DBUS_DISPATCH_NEED_MEMORY;
849 @@ -4689,6 +4800,8 @@ dbus_connection_dispatch (DBusConnection *connection)
851 CONNECTION_LOCK (connection);
853 + if (result == DBUS_HANDLER_RESULT_LATER)
855 if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
857 _dbus_verbose ("No memory\n");
858 @@ -4811,9 +4924,11 @@ dbus_connection_dispatch (DBusConnection *connection)
862 - if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
863 + if (result == DBUS_HANDLER_RESULT_LATER ||
864 + result == DBUS_HANDLER_RESULT_NEED_MEMORY)
866 - _dbus_verbose ("out of memory\n");
867 + if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
868 + _dbus_verbose ("out of memory\n");
870 /* Put message back, and we'll start over.
871 * Yes this means handlers must be idempotent if they
872 diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c
873 index c4c1856..f84918b 100644
874 --- a/dbus/dbus-list.c
875 +++ b/dbus/dbus-list.c
876 @@ -459,6 +459,35 @@ _dbus_list_remove_last (DBusList **list,
880 + * Finds a value in the list. Returns the first link
881 + * with value equal to the given data pointer.
882 + * This is a linear-time operation.
883 + * Returns #NULL if no value found that matches.
885 + * @param list address of the list head.
886 + * @param data the value to find.
887 + * @returns the link if found
890 +_dbus_list_find_first (DBusList **list,
895 + link = _dbus_list_get_first_link (list);
897 + while (link != NULL)
899 + if (link->data == data)
902 + link = _dbus_list_get_next_link (list, link);
909 * Finds a value in the list. Returns the last link
910 * with value equal to the given data pointer.
911 * This is a linear-time operation.
912 diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h
913 index 910d738..abe8331 100644
914 --- a/dbus/dbus-list.h
915 +++ b/dbus/dbus-list.h
916 @@ -59,6 +59,8 @@ dbus_bool_t _dbus_list_remove_last (DBusList **list,
918 void _dbus_list_remove_link (DBusList **list,
920 +DBusList* _dbus_list_find_first (DBusList **list,
922 DBusList* _dbus_list_find_last (DBusList **list,
924 void _dbus_list_clear (DBusList **list);
925 diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h
926 index 6a57670..5371d88 100644
927 --- a/dbus/dbus-shared.h
928 +++ b/dbus/dbus-shared.h
929 @@ -67,7 +67,8 @@ typedef enum
931 DBUS_HANDLER_RESULT_HANDLED, /**< Message has had its effect - no need to run more handlers. */
932 DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see if other handlers want it. */
933 - 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. */
934 + 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. */
935 + DBUS_HANDLER_RESULT_LATER /**< Message dispatch deferred due to pending policy check */