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   if (handler == NULL)
369     {
370       message_handler_slot_unref ();
371       return FALSE;
372     }
373   
374   if (!dbus_connection_add_filter (connection, handler))
375     {
376       dbus_message_handler_unref (handler);
377       message_handler_slot_unref ();
378       
379       return FALSE;
380     }
381
382   if (!dbus_connection_set_data (connection,
383                                  message_handler_slot,
384                                  handler,
385                                  (DBusFreeFunction)dbus_message_handler_unref))
386     {
387       dbus_connection_remove_filter (connection, handler);
388       dbus_message_handler_unref (handler);
389       message_handler_slot_unref ();
390
391       return FALSE;
392     }
393
394   return TRUE;
395 }
396
397 void
398 bus_dispatch_remove_connection (DBusConnection *connection)
399 {
400   /* Here we tell the bus driver that we want to get off. */
401   bus_driver_remove_connection (connection);
402
403   dbus_connection_set_data (connection,
404                             message_handler_slot,
405                             NULL, NULL);
406
407   message_handler_slot_unref ();
408 }
409
410 #ifdef DBUS_BUILD_TESTS
411
412 typedef dbus_bool_t (* Check1Func) (BusContext     *context);
413 typedef dbus_bool_t (* Check2Func) (BusContext     *context,
414                                     DBusConnection *connection);
415
416 static dbus_bool_t check_no_leftovers (BusContext *context);
417
418 static void
419 flush_bus (BusContext *context)
420 {
421   /* This is race condition city, obviously. since we're all in one
422    * process we can't block, we just have to wait for data we put in
423    * one end of the debug pipe to come out the other end...
424    * a more robust setup would be good.
425    */
426   
427   while (bus_loop_iterate (FALSE))
428     ;
429   _dbus_sleep_milliseconds (15);
430   while (bus_loop_iterate (FALSE))
431     ;
432 }
433
434 typedef struct
435 {
436   const char *expected_service_name;
437   dbus_bool_t failed;
438 } CheckServiceDeletedData;
439
440 static dbus_bool_t
441 check_service_deleted_foreach (DBusConnection *connection,
442                                void           *data)
443 {
444   CheckServiceDeletedData *d = data;
445   DBusMessage *message;
446   DBusError error;
447   char *service_name;
448
449   dbus_error_init (&error);
450   d->failed = TRUE;
451   service_name = NULL;
452   
453   message = dbus_connection_pop_message (connection);
454   if (message == NULL)
455     {
456       _dbus_warn ("Did not receive a message on %p, expecting %s\n",
457                   connection, DBUS_MESSAGE_SERVICE_DELETED);
458       goto out;
459     }
460   else if (!dbus_message_name_is (message, DBUS_MESSAGE_SERVICE_DELETED))
461     {
462       _dbus_warn ("Received message %s on %p, expecting %s\n",
463                   dbus_message_get_name (message),
464                   connection, DBUS_MESSAGE_SERVICE_DELETED);
465       goto out;
466     }
467   else
468     {
469       if (!dbus_message_get_args (message, &error,
470                                   DBUS_TYPE_STRING, &service_name,
471                                   DBUS_TYPE_INVALID))
472         {
473           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
474             {
475               _dbus_verbose ("no memory to get service name arg\n");
476             }
477           else
478             {
479               _dbus_assert (dbus_error_is_set (&error));
480               _dbus_warn ("Did not get the expected single string argument\n");
481               goto out;
482             }
483         }
484       else if (strcmp (service_name, d->expected_service_name) != 0)
485         {
486           _dbus_warn ("expected deletion of service %s, got deletion of %s\n",
487                       d->expected_service_name,
488                       service_name);
489           goto out;
490         }
491     }
492
493   d->failed = FALSE;
494   
495  out:
496   dbus_free (service_name);
497   dbus_error_free (&error);
498   
499   if (message)
500     dbus_message_unref (message);
501
502   return !d->failed;
503 }
504
505 static void
506 kill_client_connection (BusContext     *context,
507                         DBusConnection *connection)
508 {
509   char *base_service;
510   const char *s;
511   CheckServiceDeletedData csdd;
512
513   _dbus_verbose ("killing connection %p\n", connection);
514   
515   s = dbus_bus_get_base_service (connection);
516   _dbus_assert (s != NULL);
517
518   while ((base_service = _dbus_strdup (s)) == NULL)
519     bus_wait_for_memory ();
520
521   dbus_connection_ref (connection);
522   
523   /* kick in the disconnect handler that unrefs the connection */
524   dbus_connection_disconnect (connection);
525
526   flush_bus (context);
527
528   _dbus_assert (bus_test_client_listed (connection));
529   
530   /* Run disconnect handler in test.c */
531   if (bus_connection_dispatch_one_message (connection))
532     _dbus_assert_not_reached ("something received on connection being killed other than the disconnect");
533   
534   _dbus_assert (!dbus_connection_get_is_connected (connection));
535   dbus_connection_unref (connection);
536   connection = NULL;
537   _dbus_assert (!bus_test_client_listed (connection));
538   
539   csdd.expected_service_name = base_service;
540   csdd.failed = FALSE;
541
542   bus_test_clients_foreach (check_service_deleted_foreach,
543                             &csdd);
544
545   dbus_free (base_service);
546   
547   if (csdd.failed)
548     _dbus_assert_not_reached ("didn't get the expected ServiceDeleted messages");
549   
550   if (!check_no_leftovers (context))
551     _dbus_assert_not_reached ("stuff left in message queues after disconnecting a client");
552 }
553
554 typedef struct
555 {
556   dbus_bool_t failed;
557 } CheckNoMessagesData;
558
559 static dbus_bool_t
560 check_no_messages_foreach (DBusConnection *connection,
561                            void           *data)
562 {
563   CheckNoMessagesData *d = data;
564   DBusMessage *message;
565
566   message = dbus_connection_pop_message (connection);
567   if (message != NULL)
568     {
569       _dbus_warn ("Received message %s on %p, expecting no messages\n",
570                   dbus_message_get_name (message), connection);
571       d->failed = TRUE;
572     }
573
574   if (message)
575     dbus_message_unref (message);
576   return !d->failed;
577 }
578
579 typedef struct
580 {
581   DBusConnection *skip_connection;
582   const char *expected_service_name;
583   dbus_bool_t failed;
584 } CheckServiceCreatedData;
585
586 static dbus_bool_t
587 check_service_created_foreach (DBusConnection *connection,
588                                void           *data)
589 {
590   CheckServiceCreatedData *d = data;
591   DBusMessage *message;
592   DBusError error;
593   char *service_name;
594
595   if (connection == d->skip_connection)
596     return TRUE;
597
598   dbus_error_init (&error);
599   d->failed = TRUE;
600   service_name = NULL;
601   
602   message = dbus_connection_pop_message (connection);
603   if (message == NULL)
604     {
605       _dbus_warn ("Did not receive a message on %p, expecting %s\n",
606                   connection, DBUS_MESSAGE_SERVICE_CREATED);
607       goto out;
608     }
609   else if (!dbus_message_name_is (message, DBUS_MESSAGE_SERVICE_CREATED))
610     {
611       _dbus_warn ("Received message %s on %p, expecting %s\n",
612                   dbus_message_get_name (message),
613                   connection, DBUS_MESSAGE_SERVICE_CREATED);
614       goto out;
615     }
616   else
617     {
618       if (!dbus_message_get_args (message, &error,
619                                   DBUS_TYPE_STRING, &service_name,
620                                   DBUS_TYPE_INVALID))
621         {
622           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
623             {
624               _dbus_verbose ("no memory to get service name arg\n");
625             }
626           else
627             {
628               _dbus_assert (dbus_error_is_set (&error));
629               _dbus_warn ("Did not get the expected single string argument\n");
630               goto out;
631             }
632         }
633       else if (strcmp (service_name, d->expected_service_name) != 0)
634         {
635           _dbus_warn ("expected creation of service %s, got creation of %s\n",
636                       d->expected_service_name,
637                       service_name);
638           goto out;
639         }
640     }
641
642   d->failed = FALSE;
643   
644  out:
645   dbus_free (service_name);
646   dbus_error_free (&error);
647   
648   if (message)
649     dbus_message_unref (message);
650
651   return !d->failed;
652 }
653
654 static dbus_bool_t
655 check_no_leftovers (BusContext *context)
656 {
657   CheckNoMessagesData nmd;
658
659   nmd.failed = FALSE;
660   bus_test_clients_foreach (check_no_messages_foreach,
661                             &nmd);
662   
663   if (nmd.failed)
664     return FALSE;
665   else
666     return TRUE;
667 }
668
669 /* returns TRUE if the correct thing happens,
670  * but the correct thing may include OOM errors.
671  */
672 static dbus_bool_t
673 check_hello_message (BusContext     *context,
674                      DBusConnection *connection)
675 {
676   DBusMessage *message;
677   dbus_int32_t serial;
678   dbus_bool_t retval;
679   DBusError error;
680   char *name;
681   char *acquired;
682   
683   dbus_error_init (&error);
684   name = NULL;
685   acquired = NULL;
686   
687   message = dbus_message_new (DBUS_SERVICE_DBUS,
688                               DBUS_MESSAGE_HELLO);
689
690   if (message == NULL)
691     return TRUE;
692
693   if (!dbus_connection_send (connection, message, &serial))
694     return TRUE;
695
696   dbus_message_unref (message);
697   message = NULL;
698   
699   flush_bus (context);
700
701   if (!dbus_connection_get_is_connected (connection))
702     {
703       _dbus_verbose ("connection was disconnected\n");
704       return TRUE;
705     }
706   
707   retval = FALSE;
708   
709   message = dbus_connection_pop_message (connection);
710   if (message == NULL)
711     {
712       _dbus_warn ("Did not receive a reply to %s %d on %p\n",
713                   DBUS_MESSAGE_HELLO, serial, connection);
714       goto out;
715     }
716
717   _dbus_verbose ("Received %s on %p\n",
718                  dbus_message_get_name (message), connection);
719
720   if (dbus_message_get_is_error (message))
721     {
722       if (dbus_message_name_is (message,
723                                 DBUS_ERROR_NO_MEMORY))
724         {
725           ; /* good, this is a valid response */
726         }
727       else
728         {
729           _dbus_warn ("Did not expect error %s\n",
730                       dbus_message_get_name (message));
731           goto out;
732         }
733     }
734   else
735     {
736       CheckServiceCreatedData scd;
737       
738       if (dbus_message_name_is (message,
739                                 DBUS_MESSAGE_HELLO))
740         {
741           ; /* good, expected */
742         }
743       else
744         {
745           _dbus_warn ("Did not expect reply %s\n",
746                       dbus_message_get_name (message));
747           goto out;
748         }
749
750     retry_get_hello_name:
751       if (!dbus_message_get_args (message, &error,
752                                   DBUS_TYPE_STRING, &name,
753                                   DBUS_TYPE_INVALID))
754         {
755           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
756             {
757               _dbus_verbose ("no memory to get service name arg from hello\n");
758               dbus_error_free (&error);
759               bus_wait_for_memory ();
760               goto retry_get_hello_name;
761             }
762           else
763             {
764               _dbus_assert (dbus_error_is_set (&error));
765               _dbus_warn ("Did not get the expected single string argument to hello\n");
766               goto out;
767             }
768         }
769
770       _dbus_verbose ("Got hello name: %s\n", name);
771
772       while (!dbus_bus_set_base_service (connection, name))
773         bus_wait_for_memory ();
774       
775       scd.skip_connection = NULL;
776       scd.failed = FALSE;
777       scd.expected_service_name = name;
778       bus_test_clients_foreach (check_service_created_foreach,
779                                 &scd);
780       
781       if (scd.failed)
782         goto out;
783       
784       /* Client should also have gotten ServiceAcquired */
785       dbus_message_unref (message);
786       message = dbus_connection_pop_message (connection);
787       if (message == NULL)
788         {
789           _dbus_warn ("Expecting %s, got nothing\n",
790                       DBUS_MESSAGE_SERVICE_ACQUIRED);
791           goto out;
792         }
793       
794     retry_get_acquired_name:
795       if (!dbus_message_get_args (message, &error,
796                                   DBUS_TYPE_STRING, &acquired,
797                                   DBUS_TYPE_INVALID))
798         {
799           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
800             {
801               _dbus_verbose ("no memory to get service name arg from acquired\n");
802               dbus_error_free (&error);
803               bus_wait_for_memory ();
804               goto retry_get_acquired_name;
805             }
806           else
807             {
808               _dbus_assert (dbus_error_is_set (&error));
809               _dbus_warn ("Did not get the expected single string argument to ServiceAcquired\n");
810               goto out;
811             }
812         }
813
814       _dbus_verbose ("Got acquired name: %s\n", acquired);
815
816       if (strcmp (acquired, name) != 0)
817         {
818           _dbus_warn ("Acquired name is %s but expected %s\n",
819                       acquired, name);
820           goto out;
821         }
822     }
823
824   if (!check_no_leftovers (context))
825     goto out;
826   
827   retval = TRUE;
828   
829  out:
830   dbus_error_free (&error);
831   
832   dbus_free (name);
833   dbus_free (acquired);
834   
835   if (message)
836     dbus_message_unref (message);
837   
838   return retval;
839 }
840
841 /* returns TRUE if the correct thing happens,
842  * but the correct thing may include OOM errors.
843  */
844 static dbus_bool_t
845 check_hello_connection (BusContext *context)
846 {
847   DBusConnection *connection;
848   DBusResultCode result;
849
850   result = DBUS_RESULT_SUCCESS;
851   connection = dbus_connection_open ("debug-pipe:name=test-server", &result);
852   if (connection == NULL)
853     {
854       _dbus_assert (result != DBUS_RESULT_SUCCESS);
855       return TRUE;
856     }
857
858   if (!bus_setup_debug_client (connection))
859     {
860       dbus_connection_disconnect (connection);
861       dbus_connection_unref (connection);
862       return TRUE;
863     }
864
865   if (!check_hello_message (context, connection))
866     return FALSE;
867
868   if (dbus_bus_get_base_service (connection) == NULL)
869     {
870       /* We didn't successfully register, so we can't
871        * do the usual kill_client_connection() checks
872        */
873       dbus_connection_ref (connection);
874       dbus_connection_disconnect (connection);
875       /* dispatching disconnect handler will unref once */
876       if (bus_connection_dispatch_one_message (connection))
877         _dbus_assert_not_reached ("message other than disconnect dispatched after failure to register");
878       dbus_connection_unref (connection);
879       _dbus_assert (!bus_test_client_listed (connection));
880       
881       return TRUE;
882     }
883   else
884     {
885       kill_client_connection (context, connection);
886     }
887
888   return TRUE;
889 }
890
891 static void
892 check1_try_iterations (BusContext *context,
893                        const char *description,
894                        Check1Func  func)
895 {
896   int approx_mallocs;
897
898   /* Run once to see about how many mallocs are involved */
899   
900   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
901   
902   if (! (*func) (context))
903     _dbus_assert_not_reached ("test failed");
904
905   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
906
907   _dbus_verbose ("=================\n%s: about %d mallocs total\n=================\n",
908                  description, approx_mallocs);
909   
910   approx_mallocs += 10; /* fudge factor */
911   
912   /* Now run failing each malloc */
913   
914   while (approx_mallocs >= 0)
915     {
916       _dbus_set_fail_alloc_counter (approx_mallocs);
917
918       _dbus_verbose ("\n===\n %s: (will fail malloc %d)\n===\n",
919                      description, approx_mallocs);
920
921       if (! (*func) (context))
922         _dbus_assert_not_reached ("test failed");
923
924       if (!check_no_leftovers (context))
925         _dbus_assert_not_reached ("Messages were left over, should be covered by test suite");
926       
927       approx_mallocs -= 1;
928     }
929
930   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
931 }
932
933 dbus_bool_t
934 bus_dispatch_test (const DBusString *test_data_dir)
935 {
936   BusContext *context;
937   DBusError error;
938   const char *activation_dirs[] = { NULL, NULL };
939   DBusConnection *foo;
940   DBusConnection *bar;
941   DBusConnection *baz;
942   DBusResultCode result;
943
944   dbus_error_init (&error);
945   context = bus_context_new ("debug-pipe:name=test-server",
946                              activation_dirs,
947                              &error);
948   if (context == NULL)
949     _dbus_assert_not_reached ("could not alloc context");
950   
951   foo = dbus_connection_open ("debug-pipe:name=test-server", &result);
952   if (foo == NULL)
953     _dbus_assert_not_reached ("could not alloc connection");
954
955   if (!bus_setup_debug_client (foo))
956     _dbus_assert_not_reached ("could not set up connection");
957
958   if (!check_hello_message (context, foo))
959     _dbus_assert_not_reached ("hello message failed");
960   
961   bar = dbus_connection_open ("debug-pipe:name=test-server", &result);
962   if (bar == NULL)
963     _dbus_assert_not_reached ("could not alloc connection");
964
965   if (!bus_setup_debug_client (bar))
966     _dbus_assert_not_reached ("could not set up connection");
967
968   if (!check_hello_message (context, bar))
969     _dbus_assert_not_reached ("hello message failed");
970   
971   baz = dbus_connection_open ("debug-pipe:name=test-server", &result);
972   if (baz == NULL)
973     _dbus_assert_not_reached ("could not alloc connection");
974
975   if (!bus_setup_debug_client (baz))
976     _dbus_assert_not_reached ("could not set up connection");
977
978   if (!check_hello_message (context, baz))
979     _dbus_assert_not_reached ("hello message failed");
980
981   check1_try_iterations (context, "create_and_hello",
982                          check_hello_connection);
983
984   dbus_connection_disconnect (foo);
985   if (bus_connection_dispatch_one_message (foo))
986     _dbus_assert_not_reached ("extra message in queue");
987   _dbus_assert (!bus_test_client_listed (foo));
988
989   dbus_connection_disconnect (bar);
990   if (bus_connection_dispatch_one_message (bar))
991     _dbus_assert_not_reached ("extra message in queue");
992   _dbus_assert (!bus_test_client_listed (bar));
993
994   dbus_connection_disconnect (baz);
995   if (bus_connection_dispatch_one_message (baz))
996     _dbus_assert_not_reached ("extra message in queue");
997   _dbus_assert (!bus_test_client_listed (baz));
998
999   bus_context_unref (context);
1000   
1001   return TRUE;
1002 }
1003 #endif /* DBUS_BUILD_TESTS */