2003-03-15 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  *
6  * Licensed under the Academic Free License version 1.2
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dispatch.h"
25 #include "connection.h"
26 #include "driver.h"
27 #include "services.h"
28 #include "utils.h"
29 #include "bus.h"
30 #include "test.h"
31 #include "loop.h"
32 #include <dbus/dbus-internals.h>
33 #include <string.h>
34
35 static int message_handler_slot;
36
37 typedef struct
38 {
39   DBusMessage    *message;
40   BusTransaction *transaction;
41   DBusError      *error;
42 } SendMessageData;
43
44 static dbus_bool_t
45 send_one_message (DBusConnection *connection, void *data)
46 {
47   SendMessageData *d = data;
48   
49   if (!bus_connection_is_active (connection))
50     return TRUE;
51
52   if (!bus_transaction_send_message (d->transaction,
53                                      connection,
54                                      d->message))
55     {
56       BUS_SET_OOM (d->error);
57       return FALSE;
58     }
59
60   return TRUE;
61 }
62
63 dbus_bool_t
64 bus_dispatch_broadcast_message (BusTransaction *transaction,
65                                 DBusMessage    *message,
66                                 DBusError      *error)
67 {
68   DBusError tmp_error;
69   SendMessageData d;
70   BusConnections *connections;
71   
72   _dbus_assert (dbus_message_get_sender (message) != NULL);
73
74   connections = bus_transaction_get_connections (transaction);
75   
76   dbus_error_init (&tmp_error);
77   d.message = message;
78   d.transaction = transaction;
79   d.error = &tmp_error;
80   
81   bus_connections_foreach (connections, send_one_message, &d);
82
83   if (dbus_error_is_set (&tmp_error))
84     {
85       dbus_move_error (&tmp_error, error);
86       return FALSE;
87     }
88   else
89     return TRUE;
90 }
91
92 static dbus_bool_t
93 send_service_nonexistent_error (BusTransaction *transaction,
94                                 DBusConnection *connection,
95                                 const char     *service_name,
96                                 DBusMessage    *in_reply_to,
97                                 DBusError      *error)
98 {
99   DBusMessage *error_reply;
100   DBusString error_message;
101   const char *error_str;
102           
103   /* Trying to send a message to a non-existant service,
104    * bounce back an error message.
105    */
106           
107   if (!_dbus_string_init (&error_message, _DBUS_INT_MAX))
108     {
109       BUS_SET_OOM (error);
110       return FALSE;
111     }
112
113   if (!_dbus_string_append (&error_message, "Service \"") ||
114       !_dbus_string_append (&error_message, service_name) ||
115       !_dbus_string_append (&error_message, "\" does not exist"))
116     {
117       _dbus_string_free (&error_message);
118       BUS_SET_OOM (error);
119       return FALSE;
120     }
121               
122   _dbus_string_get_const_data (&error_message, &error_str);
123   error_reply = dbus_message_new_error_reply (in_reply_to,
124                                               DBUS_ERROR_SERVICE_DOES_NOT_EXIST,
125                                               error_str);
126
127   _dbus_string_free (&error_message);
128               
129   if (error_reply == NULL)
130     {
131       BUS_SET_OOM (error);
132       return FALSE;
133     }
134               
135   if (!bus_transaction_send_message (transaction, connection, error_reply))
136     {
137       dbus_message_unref (error_reply);
138       BUS_SET_OOM (error);
139       return FALSE;
140     }
141               
142   dbus_message_unref (error_reply);
143
144   return TRUE;
145 }
146
147 static void
148 bus_dispatch (DBusConnection *connection,
149               DBusMessage    *message)
150 {
151   const char *sender, *service_name, *message_name;
152   DBusError error;
153   BusTransaction *transaction;
154   BusContext *context;
155   
156   transaction = NULL;
157   dbus_error_init (&error);
158
159   context = bus_connection_get_context (connection);
160   _dbus_assert (context != NULL);
161   
162   /* If we can't even allocate an OOM error, we just go to sleep
163    * until we can.
164    */
165   while (!bus_connection_preallocate_oom_error (connection))
166     bus_wait_for_memory ();
167   
168   /* Ref connection in case we disconnect it at some point in here */
169   dbus_connection_ref (connection);
170
171   service_name = dbus_message_get_service (message);
172   message_name = dbus_message_get_name (message);
173
174   _dbus_assert (message_name != NULL); /* DBusMessageLoader is supposed to check this */
175
176   _dbus_verbose ("DISPATCH: %s to %s\n",
177                  message_name, service_name ? service_name : "peer");
178   
179   /* If service_name is NULL, this is a message to the bus daemon, not intended
180    * to actually go "on the bus"; e.g. a peer-to-peer ping. Handle these
181    * immediately, especially disconnection messages.
182    */
183   if (service_name == NULL)
184     {      
185       if (strcmp (message_name, DBUS_MESSAGE_LOCAL_DISCONNECT) == 0)
186         bus_connection_disconnected (connection);
187
188       /* DBusConnection also handles some of these automatically, we leave
189        * it to do so.
190        */
191       goto out;
192     }
193
194   _dbus_assert (service_name != NULL); /* this message is intended for bus routing */
195   
196   /* Create our transaction */
197   transaction = bus_transaction_new (context);
198   if (transaction == NULL)
199     {
200       BUS_SET_OOM (&error);
201       goto out;
202     }
203   
204   /* Assign a sender to the message */
205   if (bus_connection_is_active (connection))
206     {
207       sender = bus_connection_get_name (connection);
208       _dbus_assert (sender != NULL);
209       
210       if (!dbus_message_set_sender (message, sender))
211         {
212           BUS_SET_OOM (&error);
213           goto out;
214         }
215     }
216
217   if (strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */
218     {
219       if (!bus_driver_handle_message (connection, transaction, message, &error))
220         goto out;
221     }
222   else if (!bus_connection_is_active (connection)) /* clients must talk to bus driver first */
223     {
224       _dbus_verbose ("Received message from non-registered client. Disconnecting.\n");
225       dbus_connection_disconnect (connection);
226     }
227   /* FIXME what if we un-special-case this service and just have a flag
228    * on services that all service owners will get messages to it, not just
229    * the primary owner.
230    */
231   else if (strcmp (service_name, DBUS_SERVICE_BROADCAST) == 0) /* spam! */
232     {
233       if (!bus_dispatch_broadcast_message (transaction, message, &error))
234         goto out;
235     }
236   else  /* route to named service */
237     {
238       DBusString service_string;
239       BusService *service;
240       BusRegistry *registry;
241
242       registry = bus_connection_get_registry (connection);
243       
244       _dbus_string_init_const (&service_string, service_name);
245       service = bus_registry_lookup (registry, &service_string);
246
247       if (service == NULL)
248         {
249           if (!send_service_nonexistent_error (transaction, connection,
250                                                service_name,
251                                                message, &error))
252             goto out;
253         }
254       else
255         {
256           _dbus_assert (bus_service_get_primary_owner (service) != NULL);
257       
258           /* Dispatch the message */
259           if (!bus_transaction_send_message (transaction,
260                                              bus_service_get_primary_owner (service),
261                                              message))
262             {
263               BUS_SET_OOM (&error);
264               goto out;
265             }
266         }
267     }
268   
269  out:
270   if (dbus_error_is_set (&error))
271     {
272       if (!dbus_connection_get_is_connected (connection))
273         {
274           /* If we disconnected it, we won't bother to send it any error
275            * messages.
276            */
277         }
278       else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
279         {
280           bus_connection_send_oom_error (connection, message);
281
282           /* cancel transaction due to OOM */
283           if (transaction != NULL)
284             {
285               bus_transaction_cancel_and_free (transaction);
286               transaction = NULL;
287             }
288         }
289       else
290         {
291           /* Try to send the real error, if no mem to do that, send
292            * the OOM error
293            */
294           _dbus_assert (transaction != NULL);
295           
296           if (!bus_transaction_send_error_reply (transaction, connection,
297                                                  &error, message))
298             {
299               bus_connection_send_oom_error (connection, message);
300
301               /* cancel transaction due to OOM */
302               if (transaction != NULL)
303                 {
304                   bus_transaction_cancel_and_free (transaction);
305                   transaction = NULL;
306                 }
307             }
308         }
309       
310       dbus_error_free (&error);
311     }
312
313   if (transaction != NULL)
314     {
315       bus_transaction_execute_and_free (transaction);
316     }
317
318   dbus_connection_unref (connection);
319 }
320
321 static DBusHandlerResult
322 bus_dispatch_message_handler (DBusMessageHandler *handler,
323                               DBusConnection     *connection,
324                               DBusMessage        *message,
325                               void               *user_data)
326 {
327   bus_dispatch (connection, message);
328   
329   return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
330 }
331
332 dbus_bool_t
333 bus_dispatch_add_connection (DBusConnection *connection)
334 {
335   DBusMessageHandler *handler;
336   
337   message_handler_slot = dbus_connection_allocate_data_slot ();
338
339   if (message_handler_slot < 0)
340     return FALSE;
341
342   handler = dbus_message_handler_new (bus_dispatch_message_handler, NULL, NULL);  
343
344   if (!dbus_connection_add_filter (connection, handler))
345     {
346       dbus_message_handler_unref (handler);
347
348       return FALSE;
349     }
350
351   if (!dbus_connection_set_data (connection,
352                                  message_handler_slot,
353                                  handler,
354                                  (DBusFreeFunction)dbus_message_handler_unref))
355     {
356       dbus_connection_remove_filter (connection, handler);
357       dbus_message_handler_unref (handler);
358
359       return FALSE;
360     }
361
362   return TRUE;
363 }
364
365 void
366 bus_dispatch_remove_connection (DBusConnection *connection)
367 {
368   /* Here we tell the bus driver that we want to get off. */
369   bus_driver_remove_connection (connection);
370
371   dbus_connection_set_data (connection,
372                             message_handler_slot,
373                             NULL, NULL);
374 }
375
376
377
378 #ifdef DBUS_BUILD_TESTS
379
380 typedef dbus_bool_t (* Check1Func) (BusContext     *context);
381 typedef dbus_bool_t (* Check2Func) (BusContext     *context,
382                                     DBusConnection *connection);
383
384 static void
385 flush_bus (BusContext *context)
386 {  
387   while (bus_loop_iterate (FALSE))
388     ;
389 }
390
391 static void
392 kill_client_connection (DBusConnection *connection)
393 {
394   /* kick in the disconnect handler that unrefs the connection */
395   dbus_connection_disconnect (connection);
396   while (dbus_connection_dispatch_message (connection))
397     ;
398 }
399
400 /* returns TRUE if the correct thing happens,
401  * but the correct thing may include OOM errors.
402  */
403 static dbus_bool_t
404 check_hello_message (BusContext     *context,
405                      DBusConnection *connection)
406 {
407   DBusMessage *message;
408   dbus_int32_t serial;
409   dbus_bool_t retval;
410   DBusError error;
411
412   dbus_error_init (&error);
413   
414   message = dbus_message_new (DBUS_SERVICE_DBUS,
415                               DBUS_MESSAGE_HELLO);
416
417   if (message == NULL)
418     return TRUE;
419
420   if (!dbus_connection_send (connection, message, &serial))
421     return TRUE;
422
423   dbus_message_unref (message);
424   message = NULL;
425   
426   flush_bus (context);
427
428   if (!dbus_connection_get_is_connected (connection))
429     {
430       _dbus_verbose ("connection was disconnected\n");
431       return TRUE;
432     }
433   
434   retval = FALSE;
435   
436   message = dbus_connection_pop_message (connection);
437   if (message == NULL)
438     {
439       _dbus_warn ("Did not receive a reply to %s %d on %p\n",
440                   DBUS_MESSAGE_HELLO, serial, connection);
441       goto out;
442     }
443
444   _dbus_verbose ("Received %s on %p\n",
445                  dbus_message_get_name (message), connection);
446
447   if (dbus_message_get_is_error (message))
448     {
449       if (dbus_message_name_is (message,
450                                 DBUS_ERROR_NO_MEMORY))
451         {
452           ; /* good, this is a valid response */
453         }
454       else
455         {
456           _dbus_warn ("Did not expect error %s\n",
457                       dbus_message_get_name (message));
458           goto out;
459         }
460     }
461   else
462     {
463       char *str;
464       
465       if (dbus_message_name_is (message,
466                                 DBUS_MESSAGE_HELLO))
467         {
468           ; /* good, expected */
469         }
470       else
471         {
472           _dbus_warn ("Did not expect reply %s\n",
473                       dbus_message_get_name (message));
474           goto out;
475         }
476
477       if (!dbus_message_get_args (message, &error,
478                                   DBUS_TYPE_STRING, &str,
479                                   DBUS_TYPE_INVALID))
480         {
481           _dbus_warn ("Did not get the expected single string argument\n");
482           goto out;
483         }
484
485       _dbus_verbose ("Got hello name: %s\n", str);
486       dbus_free (str);
487     }
488       
489   retval = TRUE;
490   
491  out:
492   if (message)
493     dbus_message_unref (message);
494   
495   return retval;
496 }
497
498 /* returns TRUE if the correct thing happens,
499  * but the correct thing may include OOM errors.
500  */
501 static dbus_bool_t
502 check_hello_connection (BusContext *context)
503 {
504   DBusConnection *connection;
505   DBusResultCode result;
506
507   result = DBUS_RESULT_SUCCESS;
508   connection = dbus_connection_open ("debug-pipe:name=test-server", &result);
509   if (connection == NULL)
510     {
511       _dbus_assert (result != DBUS_RESULT_SUCCESS);
512       return TRUE;
513     }
514
515   if (!bus_setup_debug_client (connection))
516     {
517       dbus_connection_disconnect (connection);
518       dbus_connection_unref (connection);
519       return TRUE;
520     }
521
522   if (!check_hello_message (context, connection))
523     return FALSE;
524
525   kill_client_connection (connection);
526
527   return TRUE;
528 }
529
530 static void
531 check1_try_iterations (BusContext *context,
532                        const char *description,
533                        Check1Func  func)
534 {
535   int approx_mallocs;
536
537   /* Run once to see about how many mallocs are involved */
538   
539   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
540   
541   if (! (*func) (context))
542     _dbus_assert_not_reached ("test failed");
543
544   approx_mallocs = _DBUS_INT_MAX - _dbus_get_fail_alloc_counter ();
545
546   _dbus_verbose ("=================\n%s: about %d mallocs total\n=================\n",
547                  description, approx_mallocs);
548   
549   approx_mallocs += 10; /* fudge factor */
550   
551   /* Now run failing each malloc */
552   
553   while (approx_mallocs >= 0)
554     {
555       _dbus_set_fail_alloc_counter (approx_mallocs);
556
557       _dbus_verbose ("\n===\n %s: (will fail malloc %d)\n===\n",
558                      description, approx_mallocs);
559
560       if (! (*func) (context))
561         _dbus_assert_not_reached ("test failed");
562
563       approx_mallocs -= 1;
564     }
565
566   _dbus_set_fail_alloc_counter (_DBUS_INT_MAX);
567 }
568
569 dbus_bool_t
570 bus_dispatch_test (const DBusString *test_data_dir)
571 {
572   BusContext *context;
573   DBusError error;
574   const char *activation_dirs[] = { NULL, NULL };
575   DBusConnection *foo;
576   DBusConnection *bar;
577   DBusConnection *baz;
578   DBusResultCode result;
579
580   dbus_error_init (&error);
581   context = bus_context_new ("debug-pipe:name=test-server",
582                              activation_dirs,
583                              &error);
584   if (context == NULL)
585     _dbus_assert_not_reached ("could not alloc context");
586   
587   foo = dbus_connection_open ("debug-pipe:name=test-server", &result);
588   if (foo == NULL)
589     _dbus_assert_not_reached ("could not alloc connection");
590
591   bar = dbus_connection_open ("debug-pipe:name=test-server", &result);
592   if (bar == NULL)
593     _dbus_assert_not_reached ("could not alloc connection");
594
595   baz = dbus_connection_open ("debug-pipe:name=test-server", &result);
596   if (baz == NULL)
597     _dbus_assert_not_reached ("could not alloc connection");
598
599   if (!bus_setup_debug_client (foo) ||
600       !bus_setup_debug_client (bar) ||
601       !bus_setup_debug_client (baz))
602     _dbus_assert_not_reached ("could not set up connection");
603   
604   if (!check_hello_message (context, foo))
605     _dbus_assert_not_reached ("hello message failed");
606   if (!check_hello_message (context, bar))
607     _dbus_assert_not_reached ("hello message failed");
608   if (!check_hello_message (context, baz))
609     _dbus_assert_not_reached ("hello message failed");
610
611   check1_try_iterations (context, "create_and_hello",
612                          check_hello_connection);
613
614   dbus_connection_disconnect (foo);
615   dbus_connection_unref (foo);
616   dbus_connection_disconnect (bar);
617   dbus_connection_unref (bar);
618   dbus_connection_disconnect (baz);
619   dbus_connection_unref (baz);
620   
621   return TRUE;
622 }
623 #endif /* DBUS_BUILD_TESTS */