2003-03-16 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / bus / dispatch.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dispatch.c  Message dispatcher
3  *
4  * Copyright (C) 2003  CodeFactory AB
5  * Copyright (C) 2003  Red Hat, Inc.
6  *
7  * Licensed under the Academic Free License version 1.2
8  * 
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  * 
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #include "dispatch.h"
26 #include "connection.h"
27 #include "driver.h"
28 #include "services.h"
29 #include "utils.h"
30 #include "bus.h"
31 #include "test.h"
32 #include "loop.h"
33 #include <dbus/dbus-internals.h>
34 #include <string.h>
35
36 static int message_handler_slot;
37 static int message_handler_slot_refcount;
38
39 typedef struct
40 {
41   DBusMessage    *message;
42   BusTransaction *transaction;
43   DBusError      *error;
44 } SendMessageData;
45
46 static dbus_bool_t
47 send_one_message (DBusConnection *connection, void *data)
48 {
49   SendMessageData *d = data;
50   
51   if (!bus_connection_is_active (connection))
52     return TRUE;
53
54   if (!bus_transaction_send_message (d->transaction,
55                                      connection,
56                                      d->message))
57     {
58       BUS_SET_OOM (d->error);
59       return FALSE;
60     }
61
62   return TRUE;
63 }
64
65 dbus_bool_t
66 bus_dispatch_broadcast_message (BusTransaction *transaction,
67                                 DBusMessage    *message,
68                                 DBusError      *error)
69 {
70   DBusError tmp_error;
71   SendMessageData d;
72   BusConnections *connections;
73   
74   _dbus_assert (dbus_message_get_sender (message) != NULL);
75
76   connections = bus_transaction_get_connections (transaction);
77   
78   dbus_error_init (&tmp_error);
79   d.message = message;
80   d.transaction = transaction;
81   d.error = &tmp_error;
82   
83   bus_connections_foreach (connections, send_one_message, &d);
84
85   if (dbus_error_is_set (&tmp_error))
86     {
87       dbus_move_error (&tmp_error, error);
88       return FALSE;
89     }
90   else
91     return TRUE;
92 }
93
94 static dbus_bool_t
95 send_service_nonexistent_error (BusTransaction *transaction,
96                                 DBusConnection *connection,
97                                 const char     *service_name,
98                                 DBusMessage    *in_reply_to,
99                                 DBusError      *error)
100 {
101   DBusMessage *error_reply;
102   DBusString error_message;
103   const char *error_str;
104           
105   /* Trying to send a message to a non-existant service,
106    * bounce back an error message.
107    */
108           
109   if (!_dbus_string_init (&error_message, _DBUS_INT_MAX))
110     {
111       BUS_SET_OOM (error);
112       return FALSE;
113     }
114
115   if (!_dbus_string_append (&error_message, "Service \"") ||
116       !_dbus_string_append (&error_message, service_name) ||
117       !_dbus_string_append (&error_message, "\" does not exist"))
118     {
119       _dbus_string_free (&error_message);
120       BUS_SET_OOM (error);
121       return FALSE;
122     }
123               
124   _dbus_string_get_const_data (&error_message, &error_str);
125   error_reply = dbus_message_new_error_reply (in_reply_to,
126                                               DBUS_ERROR_SERVICE_DOES_NOT_EXIST,
127                                               error_str);
128
129   _dbus_string_free (&error_message);
130               
131   if (error_reply == NULL)
132     {
133       BUS_SET_OOM (error);
134       return FALSE;
135     }
136               
137   if (!bus_transaction_send_message (transaction, connection, error_reply))
138     {
139       dbus_message_unref (error_reply);
140       BUS_SET_OOM (error);
141       return FALSE;
142     }
143               
144   dbus_message_unref (error_reply);
145
146   return TRUE;
147 }
148
149 static void
150 bus_dispatch (DBusConnection *connection,
151               DBusMessage    *message)
152 {
153   const char *sender, *service_name, *message_name;
154   DBusError error;
155   BusTransaction *transaction;
156   BusContext *context;
157   
158   transaction = NULL;
159   dbus_error_init (&error);
160
161   context = bus_connection_get_context (connection);
162   _dbus_assert (context != NULL);
163   
164   /* If we can't even allocate an OOM error, we just go to sleep
165    * until we can.
166    */
167   while (!bus_connection_preallocate_oom_error (connection))
168     bus_wait_for_memory ();
169   
170   /* Ref connection in case we disconnect it at some point in here */
171   dbus_connection_ref (connection);
172
173   service_name = dbus_message_get_service (message);
174   message_name = dbus_message_get_name (message);
175
176   _dbus_assert (message_name != NULL); /* DBusMessageLoader is supposed to check this */
177
178   _dbus_verbose ("DISPATCH: %s to %s\n",
179                  message_name, service_name ? service_name : "peer");
180   
181   /* If service_name is NULL, this is a message to the bus daemon, not intended
182    * to actually go "on the bus"; e.g. a peer-to-peer ping. Handle these
183    * immediately, especially disconnection messages.
184    */
185   if (service_name == NULL)
186     {      
187       if (strcmp (message_name, DBUS_MESSAGE_LOCAL_DISCONNECT) == 0)
188         bus_connection_disconnected (connection);
189
190       /* DBusConnection also handles some of these automatically, we leave
191        * it to do so.
192        */
193       goto out;
194     }
195
196   _dbus_assert (service_name != NULL); /* this message is intended for bus routing */
197   
198   /* Create our transaction */
199   transaction = bus_transaction_new (context);
200   if (transaction == NULL)
201     {
202       BUS_SET_OOM (&error);
203       goto out;
204     }
205   
206   /* Assign a sender to the message */
207   if (bus_connection_is_active (connection))
208     {
209       sender = bus_connection_get_name (connection);
210       _dbus_assert (sender != NULL);
211       
212       if (!dbus_message_set_sender (message, sender))
213         {
214           BUS_SET_OOM (&error);
215           goto out;
216         }
217     }
218
219   if (strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */
220     {
221       if (!bus_driver_handle_message (connection, transaction, message, &error))
222         goto out;
223     }
224   else if (!bus_connection_is_active (connection)) /* clients must talk to bus driver first */
225     {
226       _dbus_verbose ("Received message from non-registered client. Disconnecting.\n");
227       dbus_connection_disconnect (connection);
228     }
229   /* FIXME what if we un-special-case this service and just have a flag
230    * on services that all service owners will get messages to it, not just
231    * the primary owner.
232    */
233   else if (strcmp (service_name, DBUS_SERVICE_BROADCAST) == 0) /* spam! */
234     {
235       if (!bus_dispatch_broadcast_message (transaction, message, &error))
236         goto out;
237     }
238   else  /* route to named service */
239     {
240       DBusString service_string;
241       BusService *service;
242       BusRegistry *registry;
243
244       registry = bus_connection_get_registry (connection);
245       
246       _dbus_string_init_const (&service_string, service_name);
247       service = bus_registry_lookup (registry, &service_string);
248
249       if (service == NULL)
250         {
251           if (!send_service_nonexistent_error (transaction, connection,
252                                                service_name,
253                                                message, &error))
254             goto out;
255         }
256       else
257         {
258           _dbus_assert (bus_service_get_primary_owner (service) != NULL);
259       
260           /* Dispatch the message */
261           if (!bus_transaction_send_message (transaction,
262                                              bus_service_get_primary_owner (service),
263                                              message))
264             {
265               BUS_SET_OOM (&error);
266               goto out;
267             }
268         }
269     }
270   
271  out:
272   if (dbus_error_is_set (&error))
273     {
274       if (!dbus_connection_get_is_connected (connection))
275         {
276           /* If we disconnected it, we won't bother to send it any error
277            * messages.
278            */
279         }
280       else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
281         {
282           bus_connection_send_oom_error (connection, message);
283
284           /* cancel transaction due to OOM */
285           if (transaction != NULL)
286             {
287               bus_transaction_cancel_and_free (transaction);
288               transaction = NULL;
289             }
290         }
291       else
292         {
293           /* Try to send the real error, if no mem to do that, send
294            * the OOM error
295            */
296           _dbus_assert (transaction != NULL);
297           
298           if (!bus_transaction_send_error_reply (transaction, connection,
299                                                  &error, message))
300             {
301               bus_connection_send_oom_error (connection, message);
302
303               /* cancel transaction due to OOM */
304               if (transaction != NULL)
305                 {
306                   bus_transaction_cancel_and_free (transaction);
307                   transaction = NULL;
308                 }
309             }
310         }
311       
312       dbus_error_free (&error);
313     }
314
315   if (transaction != NULL)
316     {
317       bus_transaction_execute_and_free (transaction);
318     }
319
320   dbus_connection_unref (connection);
321 }
322
323 static DBusHandlerResult
324 bus_dispatch_message_handler (DBusMessageHandler *handler,
325                               DBusConnection     *connection,
326                               DBusMessage        *message,
327                               void               *user_data)
328 {
329   bus_dispatch (connection, message);
330   
331   return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
332 }
333
334 static dbus_bool_t
335 message_handler_slot_ref (void)
336 {
337   message_handler_slot = dbus_connection_allocate_data_slot ();
338
339   if (message_handler_slot < 0)
340     return FALSE;
341
342   message_handler_slot_refcount += 1;
343
344   return TRUE;
345 }
346
347 static void
348 message_handler_slot_unref (void)
349 {
350   _dbus_assert (message_handler_slot_refcount > 0);
351   message_handler_slot_refcount -= 1;
352   if (message_handler_slot_refcount == 0)
353     {
354       dbus_connection_free_data_slot (message_handler_slot);
355       message_handler_slot = -1;
356     }
357 }
358
359 dbus_bool_t
360 bus_dispatch_add_connection (DBusConnection *connection)
361 {
362   DBusMessageHandler *handler;
363
364   if (!message_handler_slot_ref ())
365     return FALSE;
366   
367   handler = dbus_message_handler_new (bus_dispatch_message_handler, NULL, NULL);  
368
369   if (!dbus_connection_add_filter (connection, handler))
370     {
371       dbus_message_handler_unref (handler);
372       message_handler_slot_unref ();
373       
374       return FALSE;
375     }
376
377   if (!dbus_connection_set_data (connection,
378                                  message_handler_slot,
379                                  handler,
380                                  (DBusFreeFunction)dbus_message_handler_unref))
381     {
382       dbus_connection_remove_filter (connection, handler);
383       dbus_message_handler_unref (handler);
384       message_handler_slot_unref ();
385
386       return FALSE;
387     }
388
389   return TRUE;
390 }
391
392 void
393 bus_dispatch_remove_connection (DBusConnection *connection)
394 {
395   /* Here we tell the bus driver that we want to get off. */
396   bus_driver_remove_connection (connection);
397
398   dbus_connection_set_data (connection,
399                             message_handler_slot,
400                             NULL, NULL);
401
402   message_handler_slot_unref ();
403 }
404
405 #ifdef DBUS_BUILD_TESTS
406
407 typedef dbus_bool_t (* Check1Func) (BusContext     *context);
408 typedef dbus_bool_t (* Check2Func) (BusContext     *context,
409                                     DBusConnection *connection);
410
411 static dbus_bool_t check_no_leftovers (BusContext *context);
412
413 static void
414 flush_bus (BusContext *context)
415 {  
416   while (bus_loop_iterate (FALSE))
417     ;
418 }
419
420 typedef struct
421 {
422   const char *expected_service_name;
423   dbus_bool_t failed;
424 } CheckServiceDeletedData;
425
426 static dbus_bool_t
427 check_service_deleted_foreach (DBusConnection *connection,
428                                void           *data)
429 {
430   CheckServiceDeletedData *d = data;
431   DBusMessage *message;
432   DBusError error;
433   char *service_name;
434
435   dbus_error_init (&error);
436   d->failed = TRUE;
437   service_name = NULL;
438   
439   message = dbus_connection_pop_message (connection);
440   if (message == NULL)
441     {
442       _dbus_warn ("Did not receive a message on %p, expecting %s\n",
443                   connection, DBUS_MESSAGE_SERVICE_DELETED);
444       goto out;
445     }
446   else if (!dbus_message_name_is (message, DBUS_MESSAGE_SERVICE_DELETED))
447     {
448       _dbus_warn ("Received message %s on %p, expecting %s\n",
449                   dbus_message_get_name (message),
450                   connection, DBUS_MESSAGE_SERVICE_DELETED);
451       goto out;
452     }
453   else
454     {
455       if (!dbus_message_get_args (message, &error,
456                                   DBUS_TYPE_STRING, &service_name,
457                                   DBUS_TYPE_INVALID))
458         {
459           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
460             {
461               _dbus_verbose ("no memory to get service name arg\n");
462             }
463           else
464             {
465               _dbus_assert (dbus_error_is_set (&error));
466               _dbus_warn ("Did not get the expected single string argument\n");
467               goto out;
468             }
469         }
470       else if (strcmp (service_name, d->expected_service_name) != 0)
471         {
472           _dbus_warn ("expected deletion of service %s, got deletion of %s\n",
473                       d->expected_service_name,
474                       service_name);
475           goto out;
476         }
477     }
478
479   d->failed = FALSE;
480   
481  out:
482   dbus_free (service_name);
483   dbus_error_free (&error);
484   
485   if (message)
486     dbus_message_unref (message);
487
488   return !d->failed;
489 }
490
491 static void
492 kill_client_connection (BusContext     *context,
493                         DBusConnection *connection)
494 {
495   char *base_service;
496   const char *s;
497   CheckServiceDeletedData csdd;
498
499   _dbus_verbose ("killing connection %p\n", connection);
500   
501   s = dbus_bus_get_base_service (connection);
502   _dbus_assert (s != NULL);
503
504   while ((base_service = _dbus_strdup (s)) == NULL)
505     bus_wait_for_memory ();
506
507   dbus_connection_ref (connection);
508   
509   /* kick in the disconnect handler that unrefs the connection */
510   dbus_connection_disconnect (connection);
511
512   flush_bus (context);
513
514   _dbus_assert (bus_test_client_listed (connection));
515   
516   /* Run disconnect handler in test.c */
517   if (bus_connection_dispatch_one_message (connection))
518     _dbus_assert_not_reached ("something received on connection being killed other than the disconnect");
519   
520   _dbus_assert (!dbus_connection_get_is_connected (connection));
521   dbus_connection_unref (connection);
522   connection = NULL;
523   _dbus_assert (!bus_test_client_listed (connection));
524   
525   csdd.expected_service_name = base_service;
526   csdd.failed = FALSE;
527
528   bus_test_clients_foreach (check_service_deleted_foreach,
529                             &csdd);
530
531   dbus_free (base_service);
532   
533   if (csdd.failed)
534     _dbus_assert_not_reached ("didn't get the expected ServiceDeleted messages");
535   
536   if (!check_no_leftovers (context))
537     _dbus_assert_not_reached ("stuff left in message queues after disconnecting a client");
538 }
539
540 typedef struct
541 {
542   dbus_bool_t failed;
543 } CheckNoMessagesData;
544
545 static dbus_bool_t
546 check_no_messages_foreach (DBusConnection *connection,
547                            void           *data)
548 {
549   CheckNoMessagesData *d = data;
550   DBusMessage *message;
551
552   message = dbus_connection_pop_message (connection);
553   if (message != NULL)
554     {
555       _dbus_warn ("Received message %s on %p, expecting no messages\n",
556                   dbus_message_get_name (message), connection);
557       d->failed = TRUE;
558     }
559
560   if (message)
561     dbus_message_unref (message);
562   return !d->failed;
563 }
564
565 typedef struct
566 {
567   DBusConnection *skip_connection;
568   const char *expected_service_name;
569   dbus_bool_t failed;
570 } CheckServiceCreatedData;
571
572 static dbus_bool_t
573 check_service_created_foreach (DBusConnection *connection,
574                                void           *data)
575 {
576   CheckServiceCreatedData *d = data;
577   DBusMessage *message;
578   DBusError error;
579   char *service_name;
580
581   if (connection == d->skip_connection)
582     return TRUE;
583
584   dbus_error_init (&error);
585   d->failed = TRUE;
586   service_name = NULL;
587   
588   message = dbus_connection_pop_message (connection);
589   if (message == NULL)
590     {
591       _dbus_warn ("Did not receive a message on %p, expecting %s\n",
592                   connection, DBUS_MESSAGE_SERVICE_CREATED);
593       goto out;
594     }
595   else if (!dbus_message_name_is (message, DBUS_MESSAGE_SERVICE_CREATED))
596     {
597       _dbus_warn ("Received message %s on %p, expecting %s\n",
598                   dbus_message_get_name (message),
599                   connection, DBUS_MESSAGE_SERVICE_CREATED);
600       goto out;
601     }
602   else
603     {
604       if (!dbus_message_get_args (message, &error,
605                                   DBUS_TYPE_STRING, &service_name,
606                                   DBUS_TYPE_INVALID))
607         {
608           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
609             {
610               _dbus_verbose ("no memory to get service name arg\n");
611             }
612           else
613             {
614               _dbus_assert (dbus_error_is_set (&error));
615               _dbus_warn ("Did not get the expected single string argument\n");
616               goto out;
617             }
618         }
619       else if (strcmp (service_name, d->expected_service_name) != 0)
620         {
621           _dbus_warn ("expected creation of service %s, got creation of %s\n",
622                       d->expected_service_name,
623                       service_name);
624           goto out;
625         }
626     }
627
628   d->failed = FALSE;
629   
630  out:
631   dbus_free (service_name);
632   dbus_error_free (&error);
633   
634   if (message)
635     dbus_message_unref (message);
636
637   return !d->failed;
638 }
639
640 static dbus_bool_t
641 check_no_leftovers (BusContext *context)
642 {
643   CheckNoMessagesData nmd;
644
645   nmd.failed = FALSE;
646   bus_test_clients_foreach (check_no_messages_foreach,
647                             &nmd);
648   
649   if (nmd.failed)
650     return FALSE;
651   else
652     return TRUE;
653 }
654
655 /* returns TRUE if the correct thing happens,
656  * but the correct thing may include OOM errors.
657  */
658 static dbus_bool_t
659 check_hello_message (BusContext     *context,
660                      DBusConnection *connection)
661 {
662   DBusMessage *message;
663   dbus_int32_t serial;
664   dbus_bool_t retval;
665   DBusError error;
666   char *name;
667   char *acquired;
668   
669   dbus_error_init (&error);
670   name = NULL;
671   acquired = NULL;
672   
673   message = dbus_message_new (DBUS_SERVICE_DBUS,
674                               DBUS_MESSAGE_HELLO);
675
676   if (message == NULL)
677     return TRUE;
678
679   if (!dbus_connection_send (connection, message, &serial))
680     return TRUE;
681
682   dbus_message_unref (message);
683   message = NULL;
684   
685   flush_bus (context);
686
687   if (!dbus_connection_get_is_connected (connection))
688     {
689       _dbus_verbose ("connection was disconnected\n");
690       return TRUE;
691     }
692   
693   retval = FALSE;
694   
695   message = dbus_connection_pop_message (connection);
696   if (message == NULL)
697     {
698       _dbus_warn ("Did not receive a reply to %s %d on %p\n",
699                   DBUS_MESSAGE_HELLO, serial, connection);
700       goto out;
701     }
702
703   _dbus_verbose ("Received %s on %p\n",
704                  dbus_message_get_name (message), connection);
705
706   if (dbus_message_get_is_error (message))
707     {
708       if (dbus_message_name_is (message,
709                                 DBUS_ERROR_NO_MEMORY))
710         {
711           ; /* good, this is a valid response */
712         }
713       else
714         {
715           _dbus_warn ("Did not expect error %s\n",
716                       dbus_message_get_name (message));
717           goto out;
718         }
719     }
720   else
721     {
722       CheckServiceCreatedData scd;
723       
724       if (dbus_message_name_is (message,
725                                 DBUS_MESSAGE_HELLO))
726         {
727           ; /* good, expected */
728         }
729       else
730         {
731           _dbus_warn ("Did not expect reply %s\n",
732                       dbus_message_get_name (message));
733           goto out;
734         }
735
736     retry_get_hello_name:
737       if (!dbus_message_get_args (message, &error,
738                                   DBUS_TYPE_STRING, &name,
739                                   DBUS_TYPE_INVALID))
740         {
741           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
742             {
743               _dbus_verbose ("no memory to get service name arg from hello\n");
744               dbus_error_free (&error);
745               bus_wait_for_memory ();
746               goto retry_get_hello_name;
747             }
748           else
749             {
750               _dbus_assert (dbus_error_is_set (&error));
751               _dbus_warn ("Did not get the expected single string argument to hello\n");
752               goto out;
753             }
754         }
755
756       _dbus_verbose ("Got hello name: %s\n", name);
757
758       while (!dbus_bus_set_base_service (connection, name))
759         bus_wait_for_memory ();
760       
761       scd.skip_connection = NULL;
762       scd.failed = FALSE;
763       scd.expected_service_name = name;
764       bus_test_clients_foreach (check_service_created_foreach,
765                                 &scd);
766       
767       if (scd.failed)
768         goto out;
769       
770       /* Client should also have gotten ServiceAcquired */
771       dbus_message_unref (message);
772       message = dbus_connection_pop_message (connection);
773       if (message == NULL)
774         {
775           _dbus_warn ("Expecting %s, got nothing\n",
776                       DBUS_MESSAGE_SERVICE_ACQUIRED);
777           goto out;
778         }
779       
780     retry_get_acquired_name:
781       if (!dbus_message_get_args (message, &error,
782                                   DBUS_TYPE_STRING, &acquired,
783                                   DBUS_TYPE_INVALID))
784         {
785           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
786             {
787               _dbus_verbose ("no memory to get service name arg from acquired\n");
788               dbus_error_free (&error);
789               bus_wait_for_memory ();
790               goto retry_get_acquired_name;
791             }
792           else
793             {
794               _dbus_assert (dbus_error_is_set (&error));
795               _dbus_warn ("Did not get the expected single string argument to ServiceAcquired\n");
796               goto out;
797             }
798         }
799
800       _dbus_verbose ("Got acquired name: %s\n", acquired);
801
802       if (strcmp (acquired, name) != 0)
803         {
804           _dbus_warn ("Acquired name is %s but expected %s\n",
805                       acquired, name);
806           goto out;
807         }
808     }
809
810   if (!check_no_leftovers (context))
811     goto out;
812   
813   retval = TRUE;
814   
815  out:
816   dbus_error_free (&error);
817   
818   dbus_free (name);
819   dbus_free (acquired);
820   
821   if (message)
822     dbus_message_unref (message);
823   
824   return retval;
825 }
826
827 /* returns TRUE if the correct thing happens,
828  * but the correct thing may include OOM errors.
829  */
830 static dbus_bool_t
831 check_hello_connection (BusContext *context)
832 {
833   DBusConnection *connection;
834   DBusResultCode result;
835
836   result = DBUS_RESULT_SUCCESS;
837   connection = dbus_connection_open ("debug-pipe:name=test-server", &result);
838   if (connection == NULL)
839     {
840       _dbus_assert (result != DBUS_RESULT_SUCCESS);
841       return TRUE;
842     }
843
844   if (!bus_setup_debug_client (connection))
845     {
846       dbus_connection_disconnect (connection);
847       dbus_connection_unref (connection);
848       return TRUE;
849     }
850
851   if (!check_hello_message (context, connection))
852     return FALSE;
853
854   if (dbus_bus_get_base_service (connection) == NULL)
855     {
856       /* We didn't successfully register, so we can't
857        * do the usual kill_client_connection() checks
858        */
859       dbus_connection_ref (connection);
860       dbus_connection_disconnect (connection);
861       /* dispatching disconnect handler will unref once */
862       if (bus_connection_dispatch_one_message (connection))
863         _dbus_assert_not_reached ("message other than disconnect dispatched after failure to register");
864       dbus_connection_unref (connection);
865       _dbus_assert (!bus_test_client_listed (connection));
866       return TRUE;
867     }
868   else
869     {
870       kill_client_connection (context, connection);
871     }
872
873   return TRUE;
874 }
875
876 static void
877 check1_try_iterations (BusContext *context,
878                        const char *description,
879                        Check1Func  func)
880 {
881   int approx_mallocs;
882
883   /* Run once to see about how many mallocs are involved */
884   
885   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
886   
887   if (! (*func) (context))
888     _dbus_assert_not_reached ("test failed");
889
890   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
891
892   _dbus_verbose ("=================\n%s: about %d mallocs total\n=================\n",
893                  description, approx_mallocs);
894   
895   approx_mallocs += 10; /* fudge factor */
896   
897   /* Now run failing each malloc */
898   
899   while (approx_mallocs >= 0)
900     {
901       _dbus_set_fail_alloc_counter (approx_mallocs);
902
903       _dbus_verbose ("\n===\n %s: (will fail malloc %d)\n===\n",
904                      description, approx_mallocs);
905
906       if (! (*func) (context))
907         _dbus_assert_not_reached ("test failed");
908
909       if (!check_no_leftovers (context))
910         _dbus_assert_not_reached ("Messages were left over, should be covered by test suite");
911       
912       approx_mallocs -= 1;
913     }
914
915   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
916 }
917
918 dbus_bool_t
919 bus_dispatch_test (const DBusString *test_data_dir)
920 {
921   BusContext *context;
922   DBusError error;
923   const char *activation_dirs[] = { NULL, NULL };
924   DBusConnection *foo;
925   DBusConnection *bar;
926   DBusConnection *baz;
927   DBusResultCode result;
928
929   dbus_error_init (&error);
930   context = bus_context_new ("debug-pipe:name=test-server",
931                              activation_dirs,
932                              &error);
933   if (context == NULL)
934     _dbus_assert_not_reached ("could not alloc context");
935   
936   foo = dbus_connection_open ("debug-pipe:name=test-server", &result);
937   if (foo == NULL)
938     _dbus_assert_not_reached ("could not alloc connection");
939
940   if (!bus_setup_debug_client (foo))
941     _dbus_assert_not_reached ("could not set up connection");
942
943   if (!check_hello_message (context, foo))
944     _dbus_assert_not_reached ("hello message failed");
945   
946   bar = dbus_connection_open ("debug-pipe:name=test-server", &result);
947   if (bar == NULL)
948     _dbus_assert_not_reached ("could not alloc connection");
949
950   if (!bus_setup_debug_client (bar))
951     _dbus_assert_not_reached ("could not set up connection");
952
953   if (!check_hello_message (context, bar))
954     _dbus_assert_not_reached ("hello message failed");
955   
956   baz = dbus_connection_open ("debug-pipe:name=test-server", &result);
957   if (baz == NULL)
958     _dbus_assert_not_reached ("could not alloc connection");
959
960   if (!bus_setup_debug_client (baz))
961     _dbus_assert_not_reached ("could not set up connection");
962
963   if (!check_hello_message (context, baz))
964     _dbus_assert_not_reached ("hello message failed");
965
966   check1_try_iterations (context, "create_and_hello",
967                          check_hello_connection);
968
969   dbus_connection_disconnect (foo);
970   if (bus_connection_dispatch_one_message (foo))
971     _dbus_assert_not_reached ("extra message in queue");
972   dbus_connection_unref (foo);
973   _dbus_assert (!bus_test_client_listed (foo));
974
975   dbus_connection_disconnect (bar);
976   if (bus_connection_dispatch_one_message (bar))
977     _dbus_assert_not_reached ("extra message in queue");
978   dbus_connection_unref (bar);
979   _dbus_assert (!bus_test_client_listed (bar));
980
981   dbus_connection_disconnect (baz);
982   if (bus_connection_dispatch_one_message (baz))
983     _dbus_assert_not_reached ("extra message in queue");
984   dbus_connection_unref (baz);
985   _dbus_assert (!bus_test_client_listed (baz));
986
987   bus_context_unref (context);
988   
989   return TRUE;
990 }
991 #endif /* DBUS_BUILD_TESTS */