meta-agl: split wireplumber to run in multiple instances
[AGL/meta-agl.git] / meta-app-framework / recipes-core / dbus-cynagora / dbus-cynagora / 0002-Disable-message-dispatching-when-send-rule-result-is.patch
1 From c2f4ba585c777b731df6b6b8a165b6cc4dc5d639 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 2/8] Disable message dispatching when send rule result is not
5  known
6 MIME-Version: 1.0
7 Content-Type: text/plain; charset=UTF-8
8 Content-Transfer-Encoding: 8bit
9
10 When unicast message is sent to addressed recipient and policy result
11 is not available message dispatch from the sender is disabled.
12 This also means that any further messages from the given connection are
13 put into the incoming queue without being processed. If response is received
14 message dispatching is resumed. This time answer is attached to the message
15 which is now processed synchronously.
16 Receive rule result unavailability is not yet handled - such messages are
17 rejected. Also, if message is sent to non-addressed recipient and policy result
18 is unknown, message is silently dropped.
19
20 Cherry-picked from b1b87ad9f20b2052c28431b48e81073078a745ce
21 by Jose Bollo.
22
23 Updated for dbus 1.10.20 by Scott Murray and José Bollo
24
25 Signed-off-by: José Bollo <jose.bollo@iot.bzh>
26 Signed-off-by: Scott Murray <scott.murray@konsulko.com>
27 ---
28  bus/activation.c                |  76 +++++++++++--
29  bus/check.c                     | 109 +++++++++++++++++--
30  bus/check.h                     |  10 ++
31  bus/cynara.c                    |   1 -
32  bus/dispatch.c                  | 184 ++++++++++++++++++++++++++++----
33  bus/dispatch.h                  |   2 +-
34  bus/driver.c                    |  12 ++-
35  dbus/dbus-connection-internal.h |  15 +++
36  dbus/dbus-connection.c          | 125 +++++++++++++++++++++-
37  dbus/dbus-list.c                |  29 +++++
38  dbus/dbus-list.h                |   3 +
39  dbus/dbus-shared.h              |   3 +-
40  12 files changed, 528 insertions(+), 41 deletions(-)
41
42 diff --git a/bus/activation.c b/bus/activation.c
43 index f9c6c62..8301b59 100644
44 --- a/bus/activation.c
45 +++ b/bus/activation.c
46 @@ -32,6 +32,7 @@
47  #include "services.h"
48  #include "test.h"
49  #include "utils.h"
50 +#include <dbus/dbus-connection-internal.h>
51  #include <dbus/dbus-internals.h>
52  #include <dbus/dbus-hash.h>
53  #include <dbus/dbus-list.h>
54 @@ -94,6 +95,8 @@ struct BusPendingActivationEntry
55    DBusConnection *connection;
56  
57    dbus_bool_t auto_activation;
58 +
59 +  dbus_bool_t is_put_back;
60  };
61  
62  typedef struct
63 @@ -1241,20 +1244,23 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
64        BusPendingActivationEntry *entry = link->data;
65        DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
66  
67 -      if (entry->auto_activation && (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
68 +      if (entry->auto_activation && !entry->is_put_back &&
69 +          (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
70          {
71            DBusConnection *addressed_recipient;
72            DBusError error;
73 +          BusResult res;
74  
75            dbus_error_init (&error);
76  
77            addressed_recipient = bus_service_get_primary_owners_connection (service);
78  
79            /* Resume dispatching where we left off in bus_dispatch() */
80 -          if (!bus_dispatch_matches (transaction,
81 -                                     entry->connection,
82 -                                     addressed_recipient,
83 -                                     entry->activation_message, &error))
84 +          res = bus_dispatch_matches (transaction,
85 +                                      entry->connection,
86 +                                      addressed_recipient,
87 +                                      entry->activation_message, &error);
88 +          if (res == BUS_RESULT_FALSE)
89              {
90                /* If permission is denied, we just want to return the error
91                 * to the original method invoker; in particular, we don't
92 @@ -1266,11 +1272,44 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
93                    bus_connection_send_oom_error (entry->connection,
94                                                   entry->activation_message);
95                  }
96 -
97                dbus_error_free (&error);
98                link = next;
99                continue;
100              }
101 +          else if (res == BUS_RESULT_LATER)
102 +            {
103 +              DBusList *putback_message_link = link;
104 +              DBusMessage *last_inserted_message = NULL;
105 +
106 +              /* NULL entry->connection implies sending pending ActivationRequest message to systemd */
107 +              if (entry->connection == NULL)
108 +                {
109 +                  _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly when sender is NULL");
110 +                  link = next;
111 +                  continue;
112 +                }
113 +
114 +              /**
115 +               * Getting here means that policy check result is not yet available and dispatching
116 +               * messages from entry->connection has been disabled.
117 +               * Let's put back all messages for the given connection in the incoming queue and mark
118 +               * this entry as put back so they are not handled twice.
119 +               */
120 +              while (putback_message_link != NULL)
121 +                {
122 +                  BusPendingActivationEntry *putback_message = putback_message_link->data;
123 +                  if (putback_message->connection == entry->connection)
124 +                    {
125 +                      if (!_dbus_connection_putback_message (putback_message->connection, last_inserted_message,
126 +                            putback_message->activation_message, &error))
127 +                        goto error;
128 +                      last_inserted_message = putback_message->activation_message;
129 +                      putback_message->is_put_back = TRUE;
130 +                    }
131 +
132 +                  putback_message_link = _dbus_list_get_next_link(&pending_activation->entries, putback_message_link);
133 +                }
134 +            }
135          }
136  
137        link = next;
138 @@ -1287,6 +1326,19 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
139    return TRUE;
140  
141   error:
142 +  /* remove all messages that have been put to connections' incoming queues */
143 +  link = _dbus_list_get_first_link (&pending_activation->entries);
144 +  while (link != NULL)
145 +    {
146 +      BusPendingActivationEntry *entry = link->data;
147 +      if (entry->is_put_back)
148 +        {
149 +          _dbus_connection_remove_message(entry->connection, entry->activation_message);
150 +          entry->is_put_back = FALSE;
151 +        }
152 +      link = _dbus_list_get_next_link(&pending_activation->entries, link);
153 +    }
154 +
155    return FALSE;
156  }
157  
158 @@ -2079,6 +2131,7 @@ bus_activation_activate_service (BusActivation  *activation,
159  
160            if (service != NULL)
161              {
162 +              BusResult res;
163                bus_context_log (activation->context,
164                                 DBUS_SYSTEM_LOG_INFO, "Activating via systemd: service name='%s' unit='%s' requested by '%s' (%s)",
165                                 service_name,
166 @@ -2086,8 +2139,17 @@ bus_activation_activate_service (BusActivation  *activation,
167                                 bus_connection_get_name (connection),
168                                 bus_connection_get_loginfo (connection));
169                /* Wonderful, systemd is connected, let's just send the msg */
170 -              retval = bus_dispatch_matches (activation_transaction, NULL,
171 +              res = bus_dispatch_matches (activation_transaction, NULL,
172                                               systemd, message, error);
173 +
174 +              if (res == BUS_RESULT_TRUE)
175 +                retval = TRUE;
176 +              else
177 +                {
178 +                  retval = FALSE;
179 +                  if (res == BUS_RESULT_LATER)
180 +                    _dbus_verbose("Unexpectedly need time to check message from bus driver to systemd - dropping the message.\n");
181 +                }
182              }
183            else
184              {
185 diff --git a/bus/check.c b/bus/check.c
186 index 5b72d31..4b8a699 100644
187 --- a/bus/check.c
188 +++ b/bus/check.c
189 @@ -55,6 +55,8 @@ typedef struct BusDeferredMessage
190    BusCheckResponseFunc response_callback;
191  } BusDeferredMessage;
192  
193 +static dbus_int32_t deferred_message_data_slot = -1;
194 +
195  BusCheck *
196  bus_check_new (BusContext *context, DBusError *error)
197  {
198 @@ -67,11 +69,19 @@ bus_check_new (BusContext *context, DBusError *error)
199        return NULL;
200      }
201  
202 +  if (!dbus_message_allocate_data_slot(&deferred_message_data_slot))
203 +    {
204 +      dbus_free(check);
205 +      BUS_SET_OOM(error);
206 +      return NULL;
207 +    }
208 +
209    check->refcount = 1;
210    check->context = context;
211    check->cynara = bus_cynara_new(check, error);
212    if (dbus_error_is_set(error))
213      {
214 +      dbus_message_free_data_slot(&deferred_message_data_slot);
215        dbus_free(check);
216        return NULL;
217      }
218 @@ -98,6 +108,7 @@ bus_check_unref (BusCheck *check)
219    if (check->refcount == 0)
220      {
221        bus_cynara_unref(check->cynara);
222 +      dbus_message_free_data_slot(&deferred_message_data_slot);
223        dbus_free(check);
224      }
225  }
226 @@ -114,6 +125,45 @@ bus_check_get_cynara (BusCheck *check)
227    return check->cynara;
228  }
229  
230 +static void
231 +bus_check_enable_dispatch_callback (BusDeferredMessage *deferred_message,
232 +                                    BusResult result)
233 +{
234 +  _dbus_verbose("bus_check_enable_dispatch_callback called deferred_message=%p\n", deferred_message);
235 +
236 +  deferred_message->response = result;
237 +  _dbus_connection_enable_dispatch(deferred_message->sender);
238 +}
239 +
240 +static void
241 +deferred_message_free_function(void *data)
242 +{
243 +  BusDeferredMessage *deferred_message = (BusDeferredMessage *)data;
244 +  bus_deferred_message_unref(deferred_message);
245 +}
246 +
247 +void
248 +bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message)
249 +{
250 +  _dbus_assert(deferred_message != NULL);
251 +  _dbus_assert(deferred_message->sender != NULL);
252 +
253 +  if (dbus_message_get_data(deferred_message->message, deferred_message_data_slot) == NULL)
254 +    {
255 +      if (dbus_message_set_data(deferred_message->message, deferred_message_data_slot, deferred_message,
256 +          deferred_message_free_function))
257 +        bus_deferred_message_ref(deferred_message);
258 +    }
259 +
260 +  _dbus_connection_disable_dispatch(deferred_message->sender);
261 +  deferred_message->response_callback = bus_check_enable_dispatch_callback;
262 +}
263 +
264 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
265 +BusResult (*bus_check_test_override) (DBusConnection *connection,
266 +                                        const char *privilege);
267 +#endif
268 +
269  BusResult
270  bus_check_privilege (BusCheck *check,
271                       DBusMessage *message,
272 @@ -124,6 +174,7 @@ bus_check_privilege (BusCheck *check,
273                       BusDeferredMessageStatus check_type,
274                       BusDeferredMessage **deferred_message)
275  {
276 +  BusDeferredMessage *previous_deferred_message;
277    BusResult result = BUS_RESULT_FALSE;
278  #ifdef DBUS_ENABLE_CYNARA
279    BusCynara *cynara;
280 @@ -137,16 +188,54 @@ bus_check_privilege (BusCheck *check,
281        return BUS_RESULT_FALSE;
282      }
283  
284 -  /* ask policy checkers */
285 -#ifdef DBUS_ENABLE_CYNARA
286 -  cynara = bus_check_get_cynara(check);
287 -  result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
288 -      proposed_recipient, privilege, check_type, deferred_message);
289 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
290 +  if (bus_check_test_override)
291 +    return bus_check_test_override (connection, privilege);
292  #endif
293  
294 -  if (result == BUS_RESULT_LATER && deferred_message != NULL)
295 +  previous_deferred_message = dbus_message_get_data(message, deferred_message_data_slot);
296 +  /* check if message blocked at sender's queue is being processed */
297 +  if (previous_deferred_message != NULL)
298 +    {
299 +      if ((check_type & BUS_DEFERRED_MESSAGE_CHECK_SEND) &&
300 +          !(previous_deferred_message->status & BUS_DEFERRED_MESSAGE_CHECK_SEND))
301 +        {
302 +          /**
303 +           * Message has been deferred due to receive or own rule which means that sending this message
304 +           * is allowed - it must have been checked previously.
305 +           * This might happen when client calls RequestName method which depending on security
306 +           * policy might result in both "can_send" and "can_own" Cynara checks.
307 +           */
308 +          result = BUS_RESULT_TRUE;
309 +        }
310 +      else
311 +        {
312 +          result = previous_deferred_message->response;
313 +          if (result == BUS_RESULT_LATER)
314 +            {
315 +              /* result is still not known - reuse deferred message object */
316 +              if (deferred_message != NULL)
317 +                *deferred_message = previous_deferred_message;
318 +            }
319 +          else
320 +            {
321 +              /* result is available - we can remove deferred message from the processed message */
322 +              dbus_message_set_data(message, deferred_message_data_slot, NULL, NULL);
323 +            }
324 +        }
325 +    }
326 +  else
327      {
328 -      (*deferred_message)->status |= check_type;
329 +      /* ask policy checkers */
330 +#ifdef DBUS_ENABLE_CYNARA
331 +      cynara = bus_check_get_cynara(check);
332 +      result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
333 +          proposed_recipient, privilege, check_type, deferred_message);
334 +#endif
335 +      if (result == BUS_RESULT_LATER && deferred_message != NULL)
336 +        {
337 +          (*deferred_message)->status |= check_type;
338 +        }
339      }
340    return result;
341  }
342 @@ -206,6 +295,12 @@ bus_deferred_message_unref (BusDeferredMessage *deferred_message)
343       }
344  }
345  
346 +BusDeferredMessageStatus
347 +bus_deferred_message_get_status (BusDeferredMessage *deferred_message)
348 +{
349 +  return deferred_message->status;
350 +}
351 +
352  void
353  bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
354                                          BusResult result)
355 diff --git a/bus/check.h b/bus/check.h
356 index c3fcaf9..d177549 100644
357 --- a/bus/check.h
358 +++ b/bus/check.h
359 @@ -55,6 +55,7 @@ BusResult   bus_check_privilege   (BusCheck *check,
360                                     BusDeferredMessageStatus check_type,
361                                     BusDeferredMessage **deferred_message);
362  
363 +
364  BusDeferredMessage *bus_deferred_message_new                (DBusMessage *message,
365                                                               DBusConnection *sender,
366                                                               DBusConnection *addressed_recipient,
367 @@ -65,4 +66,13 @@ BusDeferredMessage *bus_deferred_message_ref                (BusDeferredMessage
368  void                bus_deferred_message_unref              (BusDeferredMessage *deferred_message);
369  void                bus_deferred_message_response_received  (BusDeferredMessage *deferred_message,
370                                                               BusResult result);
371 +void                bus_deferred_message_disable_sender     (BusDeferredMessage *deferred_message);
372 +
373 +BusDeferredMessageStatus  bus_deferred_message_get_status   (BusDeferredMessage *deferred_message);
374 +
375 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
376 +extern BusResult (*bus_check_test_override) (DBusConnection *connection,
377 +                                               const char *privilege);
378 +#endif
379 +
380  #endif /* BUS_CHECK_H */
381 diff --git a/bus/cynara.c b/bus/cynara.c
382 index 57a4c45..77aed62 100644
383 --- a/bus/cynara.c
384 +++ b/bus/cynara.c
385 @@ -36,7 +36,6 @@
386  #include <cynara-client-async.h>
387  #endif
388  
389 -
390  #ifdef DBUS_ENABLE_CYNARA
391  typedef struct BusCynara
392  {
393 diff --git a/bus/dispatch.c b/bus/dispatch.c
394 index d3867f7..50a22a3 100644
395 --- a/bus/dispatch.c
396 +++ b/bus/dispatch.c
397 @@ -35,6 +35,7 @@
398  #include "signals.h"
399  #include "test.h"
400  #include <dbus/dbus-internals.h>
401 +#include <dbus/dbus-connection-internal.h>
402  #include <dbus/dbus-misc.h>
403  #include <string.h>
404  
405 @@ -122,7 +123,7 @@ send_one_message (DBusConnection *connection,
406    return TRUE;
407  }
408  
409 -dbus_bool_t
410 +BusResult
411  bus_dispatch_matches (BusTransaction *transaction,
412                        DBusConnection *sender,
413                        DBusConnection *addressed_recipient,
414 @@ -158,13 +159,29 @@ bus_dispatch_matches (BusTransaction *transaction,
415                                                 message, NULL, error,
416                                                 &deferred_message);
417        if (res == BUS_RESULT_FALSE)
418 -        return FALSE;
419 +        return BUS_RESULT_FALSE;
420        else if (res == BUS_RESULT_LATER)
421          {
422 -          dbus_set_error (error,
423 -                          DBUS_ERROR_ACCESS_DENIED,
424 -                          "Rejecting message because time is needed to check security policy");
425 -          return FALSE;
426 +          BusDeferredMessageStatus status;
427 +          status = bus_deferred_message_get_status(deferred_message);
428 +
429 +          if (status & BUS_DEFERRED_MESSAGE_CHECK_SEND)
430 +            {
431 +              /* send rule result not available - disable dispatching messages from the sender */
432 +              bus_deferred_message_disable_sender(deferred_message);
433 +              return BUS_RESULT_LATER;
434 +            }
435 +          else if (status & BUS_DEFERRED_MESSAGE_CHECK_RECEIVE)
436 +            {
437 +              dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
438 +                              "Rejecting message because time is needed to check security policy");
439 +              return BUS_RESULT_FALSE;
440 +            }
441 +          else
442 +            {
443 +              _dbus_verbose("deferred message has no status field set to send or receive unexpectedly\n");
444 +              return BUS_RESULT_FALSE;
445 +            }
446          }
447  
448        if (dbus_message_contains_unix_fds (message) &&
449 @@ -175,14 +192,14 @@ bus_dispatch_matches (BusTransaction *transaction,
450                            DBUS_ERROR_NOT_SUPPORTED,
451                            "Tried to send message with Unix file descriptors"
452                            "to a client that doesn't support that.");
453 -          return FALSE;
454 -      }
455 +          return BUS_RESULT_FALSE;
456 +        }
457  
458        /* Dispatch the message */
459        if (!bus_transaction_send (transaction, addressed_recipient, message))
460          {
461            BUS_SET_OOM (error);
462 -          return FALSE;
463 +          return BUS_RESULT_FALSE;
464          }
465      }
466  
467 @@ -197,7 +214,7 @@ bus_dispatch_matches (BusTransaction *transaction,
468                                        &recipients))
469      {
470        BUS_SET_OOM (error);
471 -      return FALSE;
472 +      return BUS_RESULT_FALSE;
473      }
474  
475    link = _dbus_list_get_first_link (&recipients);
476 @@ -219,10 +236,10 @@ bus_dispatch_matches (BusTransaction *transaction,
477    if (dbus_error_is_set (&tmp_error))
478      {
479        dbus_move_error (&tmp_error, error);
480 -      return FALSE;
481 +      return BUS_RESULT_FALSE;
482      }
483    else
484 -    return TRUE;
485 +    return BUS_RESULT_TRUE;
486  }
487  
488  static DBusHandlerResult
489 @@ -409,10 +426,12 @@ bus_dispatch (DBusConnection *connection,
490          }
491        else if (res == BUS_RESULT_LATER)
492          {
493 -          dbus_set_error (&error,
494 -                          DBUS_ERROR_ACCESS_DENIED,
495 -                          "Rejecting message because time is needed to check security policy");
496 -          _dbus_verbose ("Security policy needs time to check policy. Dropping message\n");
497 +          /* Disable dispatching messages from the sender,
498 +           * roll back and dispatch the message once the policy result is available */
499 +          bus_deferred_message_disable_sender(deferred_message);
500 +          bus_transaction_cancel_and_free (transaction);
501 +          transaction = NULL;
502 +          result = DBUS_HANDLER_RESULT_LATER;
503            goto out;
504          }
505  
506 @@ -514,8 +533,14 @@ bus_dispatch (DBusConnection *connection,
507     * addressed_recipient == NULL), and match it against other connections'
508     * match rules.
509     */
510 -  if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
511 -    goto out;
512 +  if (BUS_RESULT_LATER == bus_dispatch_matches (transaction, connection, addressed_recipient,
513 +                                                message, &error))
514 +    {
515 +      /* Roll back and dispatch the message once the policy result is available */
516 +      bus_transaction_cancel_and_free (transaction);
517 +      transaction = NULL;
518 +      result = DBUS_HANDLER_RESULT_LATER;
519 +    }
520  
521   out:
522    if (dbus_error_is_set (&error))
523 @@ -5060,9 +5085,132 @@ bus_dispatch_test_conf_fail (const DBusString *test_data_dir,
524  }
525  #endif
526  
527 +typedef struct {
528 +  DBusTimeout *timeout;
529 +  DBusConnection *connection;
530 +  dbus_bool_t timedout;
531 +  int check_counter;
532 +} BusTestCheckData;
533 +
534 +static BusTestCheckData *cdata;
535 +
536 +static dbus_bool_t
537 +bus_dispatch_test_check_timeout (void *data)
538 +{
539 +  _dbus_verbose ("timeout triggered - pretend that privilege check result is available\n");
540 +
541 +  /* should only happen once during the test */
542 +  _dbus_assert (!cdata->timedout);
543 +  cdata->timedout = TRUE;
544 +  _dbus_connection_enable_dispatch (cdata->connection);
545 +
546 +  /* don't call this again */
547 +  _dbus_loop_remove_timeout (bus_connection_get_loop (cdata->connection),
548 +                             cdata->timeout);
549 +  dbus_connection_unref (cdata->connection);
550 +  cdata->connection = NULL;
551 +  return TRUE;
552 +}
553 +
554 +static BusResult
555 +bus_dispatch_test_check_override (DBusConnection *connection,
556 +                                  const char *privilege)
557 +{
558 +  _dbus_verbose ("overriding privilege check %s #%d\n", privilege, cdata->check_counter);
559 +  cdata->check_counter++;
560 +  if (!cdata->timedout)
561 +    {
562 +      dbus_bool_t added;
563 +
564 +      /* Should be the first privilege check for the "Echo" method. */
565 +      _dbus_assert (cdata->check_counter == 1);
566 +      cdata->timeout = _dbus_timeout_new (1, bus_dispatch_test_check_timeout,
567 +                                          NULL, NULL);
568 +      _dbus_assert (cdata->timeout);
569 +      added = _dbus_loop_add_timeout (bus_connection_get_loop (connection),
570 +                                      cdata->timeout);
571 +      _dbus_assert (added);
572 +      cdata->connection = connection;
573 +      dbus_connection_ref (connection);
574 +      _dbus_connection_disable_dispatch (connection);
575 +      return BUS_RESULT_LATER;
576 +    }
577 +  else
578 +    {
579 +      /* Should only be checked one more time, and this time succeeds. */
580 +      _dbus_assert (cdata->check_counter == 2);
581 +      return BUS_RESULT_TRUE;
582 +    }
583 +}
584 +
585 +static dbus_bool_t
586 +bus_dispatch_test_check (const DBusString *test_data_dir)
587 +{
588 +  const char *filename = "valid-config-files/debug-check-some.conf";
589 +  BusContext *context;
590 +  DBusConnection *foo;
591 +  DBusError error;
592 +  dbus_bool_t result = TRUE;
593 +  BusTestCheckData data;
594 +
595 +  /* save the config name for the activation helper */
596 +  if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename))
597 +    _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG");
598 +
599 +  dbus_error_init (&error);
600 +
601 +  context = bus_context_new_test (test_data_dir, filename);
602 +  if (context == NULL)
603 +    return FALSE;
604 +
605 +  foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
606 +  if (foo == NULL)
607 +    _dbus_assert_not_reached ("could not alloc connection");
608 +
609 +  if (!bus_setup_debug_client (foo))
610 +    _dbus_assert_not_reached ("could not set up connection");
611 +
612 +  spin_connection_until_authenticated (context, foo);
613 +
614 +  if (!check_hello_message (context, foo))
615 +    _dbus_assert_not_reached ("hello message failed");
616 +
617 +  if (!check_double_hello_message (context, foo))
618 +    _dbus_assert_not_reached ("double hello message failed");
619 +
620 +  if (!check_add_match (context, foo, ""))
621 +    _dbus_assert_not_reached ("AddMatch message failed");
622 +
623 +  /*
624 +   * Cause bus_check_send_privilege() to return BUS_RESULT_LATER in the
625 +   * first call, then BUS_RESULT_TRUE.
626 +   */
627 +  cdata = &data;
628 +  memset (cdata, 0, sizeof(*cdata));
629 +  bus_check_test_override = bus_dispatch_test_check_override;
630 +
631 +  result = check_existent_service_auto_start (context, foo);
632 +
633 +  _dbus_assert (cdata->check_counter == 2);
634 +  _dbus_assert (cdata->timedout);
635 +  _dbus_assert (cdata->timeout);
636 +  _dbus_assert (!cdata->connection);
637 +  _dbus_timeout_unref (cdata->timeout);
638 +
639 +  kill_client_connection_unchecked (foo);
640 +
641 +  bus_context_unref (context);
642 +
643 +  return result;
644 +}
645 +
646  dbus_bool_t
647  bus_dispatch_test (const DBusString *test_data_dir)
648  {
649 +  _dbus_verbose ("<check> tests\n");
650 +  if (!bus_dispatch_test_check (test_data_dir))
651 +    return FALSE;
652 +
653    /* run normal activation tests */
654    _dbus_verbose ("Normal activation tests\n");
655    if (!bus_dispatch_test_conf (test_data_dir,
656 diff --git a/bus/dispatch.h b/bus/dispatch.h
657 index fb5ba7a..afba6a2 100644
658 --- a/bus/dispatch.h
659 +++ b/bus/dispatch.h
660 @@ -29,7 +29,7 @@
661  
662  dbus_bool_t bus_dispatch_add_connection    (DBusConnection *connection);
663  void        bus_dispatch_remove_connection (DBusConnection *connection);
664 -dbus_bool_t bus_dispatch_matches           (BusTransaction *transaction,
665 +BusResult   bus_dispatch_matches           (BusTransaction *transaction,
666                                              DBusConnection *sender,
667                                              DBusConnection *recipient,
668                                              DBusMessage    *message,
669 diff --git a/bus/driver.c b/bus/driver.c
670 index cd0a714..f414f64 100644
671 --- a/bus/driver.c
672 +++ b/bus/driver.c
673 @@ -218,6 +218,7 @@ bus_driver_send_service_owner_changed (const char     *service_name,
674  {
675    DBusMessage *message;
676    dbus_bool_t retval;
677 +  BusResult res;
678    const char *null_service;
679  
680    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
681 @@ -253,7 +254,16 @@ bus_driver_send_service_owner_changed (const char     *service_name,
682    if (!bus_transaction_capture (transaction, NULL, NULL, message))
683      goto oom;
684  
685 -  retval = bus_dispatch_matches (transaction, NULL, NULL, message, error);
686 +  res = bus_dispatch_matches (transaction, NULL, NULL, message, error);
687 +  if (res == BUS_RESULT_TRUE)
688 +    retval = TRUE;
689 +  else
690 +    {
691 +      retval = FALSE;
692 +      if (res == BUS_RESULT_LATER)
693 +        /* should never happen */
694 +        _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly");
695 +    }
696    dbus_message_unref (message);
697  
698    return retval;
699 diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
700 index 4835732..94b1c95 100644
701 --- a/dbus/dbus-connection-internal.h
702 +++ b/dbus/dbus-connection-internal.h
703 @@ -118,6 +118,21 @@ DBUS_PRIVATE_EXPORT
704  dbus_bool_t       _dbus_connection_get_linux_security_label       (DBusConnection  *connection,
705                                                                     char           **label_p);
706  
707 +DBUS_PRIVATE_EXPORT
708 +void              _dbus_connection_enable_dispatch                (DBusConnection *connection);
709 +DBUS_PRIVATE_EXPORT
710 +void              _dbus_connection_disable_dispatch               (DBusConnection *connection);
711 +
712 +DBUS_PRIVATE_EXPORT
713 +dbus_bool_t       _dbus_connection_putback_message                (DBusConnection *connection,
714 +                                                                   DBusMessage    *after_message,
715 +                                                                   DBusMessage    *message,
716 +                                                                   DBusError      *error);
717 +
718 +DBUS_PRIVATE_EXPORT
719 +dbus_bool_t       _dbus_connection_remove_message                 (DBusConnection *connection,
720 +                                                                   DBusMessage    *message);
721 +
722  /* if DBUS_ENABLE_STATS */
723  DBUS_PRIVATE_EXPORT
724  void _dbus_connection_get_stats (DBusConnection *connection,
725 diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
726 index c525b6d..958968c 100644
727 --- a/dbus/dbus-connection.c
728 +++ b/dbus/dbus-connection.c
729 @@ -311,7 +311,8 @@ struct DBusConnection
730     */
731    dbus_bool_t dispatch_acquired; /**< Someone has dispatch path (can drain incoming queue) */
732    dbus_bool_t io_path_acquired;  /**< Someone has transport io path (can use the transport to read/write messages) */
733 -  
734 +
735 +  unsigned int dispatch_disabled : 1;  /**< if true, then dispatching incoming messages is stopped until enabled again */
736    unsigned int shareable : 1; /**< #TRUE if libdbus owns a reference to the connection and can return it from dbus_connection_open() more than once */
737    
738    unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
739 @@ -439,6 +440,39 @@ _dbus_connection_wakeup_mainloop (DBusConnection *connection)
740      (*connection->wakeup_main_function) (connection->wakeup_main_data);
741  }
742  
743 +static void
744 +_dbus_connection_set_dispatch(DBusConnection *connection,
745 +                              dbus_bool_t disabled)
746 +{
747 +  CONNECTION_LOCK (connection);
748 +  if (connection->dispatch_disabled != disabled)
749 +    {
750 +      DBusDispatchStatus status;
751 +
752 +      connection->dispatch_disabled = disabled;
753 +      status = _dbus_connection_get_dispatch_status_unlocked (connection);
754 +      _dbus_connection_update_dispatch_status_and_unlock (connection, status);
755 +    }
756 +  else
757 +    {
758 +      CONNECTION_UNLOCK (connection);
759 +    }
760 +}
761 +
762 +
763 +void
764 +_dbus_connection_enable_dispatch (DBusConnection *connection)
765 +{
766 +  _dbus_connection_set_dispatch (connection, FALSE);
767 +}
768 +
769 +void
770 + _dbus_connection_disable_dispatch (DBusConnection *connection)
771 +{
772 +  _dbus_connection_set_dispatch (connection, TRUE);
773 +}
774 +
775 +
776  #ifdef DBUS_ENABLE_EMBEDDED_TESTS
777  /**
778   * Gets the locks so we can examine them
779 @@ -4069,6 +4103,82 @@ _dbus_connection_putback_message_link_unlocked (DBusConnection *connection,
780        "_dbus_connection_putback_message_link_unlocked");
781  }
782  
783 +dbus_bool_t
784 +_dbus_connection_putback_message (DBusConnection *connection,
785 +                                  DBusMessage    *after_message,
786 +                                  DBusMessage    *message,
787 +                                  DBusError      *error)
788 +{
789 +  DBusDispatchStatus status;
790 +  DBusList *message_link = _dbus_list_alloc_link (message);
791 +  DBusList *after_link;
792 +  if (message_link == NULL)
793 +    {
794 +      _DBUS_SET_OOM (error);
795 +      return FALSE;
796 +    }
797 +  dbus_message_ref (message);
798 +
799 +  CONNECTION_LOCK (connection);
800 +  _dbus_connection_acquire_dispatch (connection);
801 +  HAVE_LOCK_CHECK (connection);
802 +
803 +  after_link = _dbus_list_find_first(&connection->incoming_messages, after_message);
804 +  _dbus_list_insert_after_link (&connection->incoming_messages, after_link, message_link);
805 +  connection->n_incoming += 1;
806 +
807 +  _dbus_verbose ("Message %p (%s %s %s '%s') put back into queue %p, %d incoming\n",
808 +                 message_link->data,
809 +                 dbus_message_type_to_string (dbus_message_get_type (message_link->data)),
810 +                 dbus_message_get_interface (message_link->data) ?
811 +                 dbus_message_get_interface (message_link->data) :
812 +                 "no interface",
813 +                 dbus_message_get_member (message_link->data) ?
814 +                 dbus_message_get_member (message_link->data) :
815 +                 "no member",
816 +                 dbus_message_get_signature (message_link->data),
817 +                 connection, connection->n_incoming);
818 +
819 +  _dbus_message_trace_ref (message_link->data, -1, -1,
820 +      "_dbus_connection_putback_message");
821 +
822 +  _dbus_connection_release_dispatch (connection);
823 +
824 +  status = _dbus_connection_get_dispatch_status_unlocked (connection);
825 +  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
826 +
827 +  return TRUE;
828 +}
829 +
830 +dbus_bool_t
831 +_dbus_connection_remove_message (DBusConnection *connection,
832 +                                 DBusMessage *message)
833 +{
834 +  DBusDispatchStatus status;
835 +  dbus_bool_t removed;
836 +
837 +  CONNECTION_LOCK (connection);
838 +  _dbus_connection_acquire_dispatch (connection);
839 +  HAVE_LOCK_CHECK (connection);
840 +
841 +  removed = _dbus_list_remove(&connection->incoming_messages, message);
842 +
843 +  if (removed)
844 +    {
845 +      connection->n_incoming -= 1;
846 +      dbus_message_unref(message);
847 +      _dbus_verbose ("Message %p removed from incoming queue\n", message);
848 +    }
849 +  else
850 +      _dbus_verbose ("Message %p not found in the incoming queue\n", message);
851 +
852 +  _dbus_connection_release_dispatch (connection);
853 +
854 +  status = _dbus_connection_get_dispatch_status_unlocked (connection);
855 +  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
856 +  return removed;
857 +}
858 +
859  /**
860   * Returns the first-received message from the incoming message queue,
861   * removing it from the queue. The caller owns a reference to the
862 @@ -4252,8 +4362,9 @@ static DBusDispatchStatus
863  _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection)
864  {
865    HAVE_LOCK_CHECK (connection);
866 -  
867 -  if (connection->n_incoming > 0)
868 +  if (connection->dispatch_disabled && _dbus_connection_get_is_connected_unlocked(connection))
869 +    return DBUS_DISPATCH_COMPLETE;
870 +  else if (connection->n_incoming > 0)
871      return DBUS_DISPATCH_DATA_REMAINS;
872    else if (!_dbus_transport_queue_messages (connection->transport))
873      return DBUS_DISPATCH_NEED_MEMORY;
874 @@ -4716,6 +4827,8 @@ dbus_connection_dispatch (DBusConnection *connection)
875    
876    CONNECTION_LOCK (connection);
877  
878 +  if (result == DBUS_HANDLER_RESULT_LATER)
879 +      goto out;
880    if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
881      {
882        _dbus_verbose ("No memory\n");
883 @@ -4838,9 +4951,11 @@ dbus_connection_dispatch (DBusConnection *connection)
884                   connection);
885    
886   out:
887 -  if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
888 +  if (result == DBUS_HANDLER_RESULT_LATER ||
889 +      result == DBUS_HANDLER_RESULT_NEED_MEMORY)
890      {
891 -      _dbus_verbose ("out of memory\n");
892 +      if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
893 +        _dbus_verbose ("out of memory\n");
894        
895        /* Put message back, and we'll start over.
896         * Yes this means handlers must be idempotent if they
897 diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c
898 index 8e713c0..32ea871 100644
899 --- a/dbus/dbus-list.c
900 +++ b/dbus/dbus-list.c
901 @@ -458,6 +458,35 @@ _dbus_list_remove_last (DBusList **list,
902      return FALSE;
903  }
904  
905 +/**
906 + * Finds a value in the list. Returns the first link
907 + * with value equal to the given data pointer.
908 + * This is a linear-time operation.
909 + * Returns #NULL if no value found that matches.
910 + *
911 + * @param list address of the list head.
912 + * @param data the value to find.
913 + * @returns the link if found
914 + */
915 +DBusList*
916 +_dbus_list_find_first (DBusList **list,
917 +                       void      *data)
918 +{
919 +  DBusList *link;
920 +
921 +  link = _dbus_list_get_first_link (list);
922 +
923 +  while (link != NULL)
924 +    {
925 +      if (link->data == data)
926 +        return link;
927 +
928 +      link = _dbus_list_get_next_link (list, link);
929 +    }
930 +
931 +  return NULL;
932 +}
933 +
934  /**
935   * Finds a value in the list. Returns the last link
936   * with value equal to the given data pointer.
937 diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h
938 index 9350a0d..fee9f1b 100644
939 --- a/dbus/dbus-list.h
940 +++ b/dbus/dbus-list.h
941 @@ -68,6 +68,9 @@ DBUS_PRIVATE_EXPORT
942  void        _dbus_list_remove_link        (DBusList **list,
943                                             DBusList  *link);
944  DBUS_PRIVATE_EXPORT
945 +DBusList*   _dbus_list_find_first         (DBusList **list,
946 +                                           void      *data);
947 +DBUS_PRIVATE_EXPORT
948  DBusList*   _dbus_list_find_last          (DBusList **list,
949                                             void      *data);
950  DBUS_PRIVATE_EXPORT
951 diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h
952 index 7ab9103..e5bfbed 100644
953 --- a/dbus/dbus-shared.h
954 +++ b/dbus/dbus-shared.h
955 @@ -67,7 +67,8 @@ typedef enum
956  {
957    DBUS_HANDLER_RESULT_HANDLED,         /**< Message has had its effect - no need to run more handlers. */ 
958    DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see if other handlers want it. */
959 -  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. */
960 +  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. */
961 +  DBUS_HANDLER_RESULT_LATER            /**< Message dispatch deferred due to pending policy check */
962  } DBusHandlerResult;
963  
964  /* Bus names */
965 -- 
966 2.21.1
967