2003-03-14 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 static void
381 flush_bus (BusContext *context)
382 {  
383   while (bus_loop_iterate (FALSE))
384     ;
385 }
386
387 /* returns TRUE if the correct thing happens,
388  * but the correct thing may include OOM errors.
389  */
390 static dbus_bool_t
391 check_hello_message (BusContext     *context,
392                      DBusConnection *connection)
393 {
394   DBusMessage *message;
395   dbus_int32_t serial;
396   
397   message = dbus_message_new (DBUS_SERVICE_DBUS,
398                               DBUS_MESSAGE_HELLO);
399
400   if (message == NULL)
401     return TRUE;
402
403   if (!dbus_connection_send (connection, message, &serial))
404     return TRUE;
405
406   dbus_message_unref (message);
407   
408   flush_bus (context);
409
410   message = dbus_connection_pop_message (connection);
411   if (message == NULL)
412     {
413       _dbus_warn ("Did not receive a reply to %s %d on %p\n",
414                   DBUS_MESSAGE_HELLO, serial, connection);
415       return FALSE;
416     }
417
418   _dbus_verbose ("Received %s on %p\n",
419                  dbus_message_get_name (message), connection);
420   
421   dbus_message_unref (message);
422   
423   return TRUE;
424 }
425
426 dbus_bool_t
427 bus_dispatch_test (const DBusString *test_data_dir)
428 {
429   BusContext *context;
430   DBusError error;
431   const char *activation_dirs[] = { NULL, NULL };
432   DBusConnection *foo;
433   DBusConnection *bar;
434   DBusConnection *baz;
435   DBusResultCode result;
436
437   dbus_error_init (&error);
438   context = bus_context_new ("debug-pipe:name=test-server",
439                              activation_dirs,
440                              &error);
441   if (context == NULL)
442     _dbus_assert_not_reached ("could not alloc context");
443   
444   foo = dbus_connection_open ("debug-pipe:name=test-server", &result);
445   if (foo == NULL)
446     _dbus_assert_not_reached ("could not alloc connection");
447
448   bar = dbus_connection_open ("debug-pipe:name=test-server", &result);
449   if (bar == NULL)
450     _dbus_assert_not_reached ("could not alloc connection");
451
452   baz = dbus_connection_open ("debug-pipe:name=test-server", &result);
453   if (baz == NULL)
454     _dbus_assert_not_reached ("could not alloc connection");
455
456   if (!bus_setup_debug_client (foo) ||
457       !bus_setup_debug_client (bar) ||
458       !bus_setup_debug_client (baz))
459     _dbus_assert_not_reached ("could not set up connection");
460   
461   if (!check_hello_message (context, foo))
462     _dbus_assert_not_reached ("hello message failed");
463   if (!check_hello_message (context, bar))
464     _dbus_assert_not_reached ("hello message failed");
465   if (!check_hello_message (context, baz))
466     _dbus_assert_not_reached ("hello message failed");
467   
468   return TRUE;
469 }
470 #endif /* DBUS_BUILD_TESTS */