2003-03-12 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 <dbus/dbus-internals.h>
31 #include <string.h>
32
33 static int message_handler_slot;
34
35 typedef struct
36 {
37   DBusMessage    *message;
38   BusTransaction *transaction;
39   DBusError      *error;
40 } SendMessageData;
41
42 static dbus_bool_t
43 send_one_message (DBusConnection *connection, void *data)
44 {
45   SendMessageData *d = data;
46   
47   if (!bus_connection_is_active (connection))
48     return TRUE;
49
50   if (!bus_transaction_send_message (d->transaction,
51                                      connection,
52                                      d->message))
53     {
54       BUS_SET_OOM (d->error);
55       return FALSE;
56     }
57
58   return TRUE;
59 }
60
61 dbus_bool_t
62 bus_dispatch_broadcast_message (BusTransaction *transaction,
63                                 DBusMessage    *message,
64                                 DBusError      *error)
65 {
66   DBusError tmp_error;
67   SendMessageData d;
68   BusConnections *connections;
69   
70   _dbus_assert (dbus_message_get_sender (message) != NULL);
71
72   connections = bus_transaction_get_connections (transaction);
73   
74   dbus_error_init (&tmp_error);
75   d.message = message;
76   d.transaction = transaction;
77   d.error = &tmp_error;
78   
79   bus_connections_foreach (connections, send_one_message, &d);
80
81   if (dbus_error_is_set (&tmp_error))
82     {
83       dbus_move_error (&tmp_error, error);
84       return FALSE;
85     }
86   else
87     return TRUE;
88 }
89
90 static dbus_bool_t
91 send_service_nonexistent_error (BusTransaction *transaction,
92                                 DBusConnection *connection,
93                                 const char     *service_name,
94                                 DBusMessage    *in_reply_to,
95                                 DBusError      *error)
96 {
97   DBusMessage *error_reply;
98   DBusString error_message;
99   const char *error_str;
100           
101   /* Trying to send a message to a non-existant service,
102    * bounce back an error message.
103    */
104           
105   if (!_dbus_string_init (&error_message, _DBUS_INT_MAX))
106     {
107       BUS_SET_OOM (error);
108       return FALSE;
109     }
110
111   if (!_dbus_string_append (&error_message, "Service \"") ||
112       !_dbus_string_append (&error_message, service_name) ||
113       !_dbus_string_append (&error_message, "does not exist"))
114     {
115       _dbus_string_free (&error_message);
116       BUS_SET_OOM (error);
117       return FALSE;
118     }
119               
120   _dbus_string_get_const_data (&error_message, &error_str);
121   error_reply = dbus_message_new_error_reply (in_reply_to,
122                                               DBUS_ERROR_SERVICE_DOES_NOT_EXIST,
123                                               error_str);
124
125   _dbus_string_free (&error_message);
126               
127   if (error_reply == NULL)
128     {
129       BUS_SET_OOM (error);
130       return FALSE;
131     }
132               
133   if (!bus_transaction_send_message (transaction, connection, error_reply))
134     {
135       dbus_message_unref (error_reply);
136       BUS_SET_OOM (error);
137       return FALSE;
138     }
139               
140   dbus_message_unref (error_reply);
141
142   return TRUE;
143 }
144
145 static DBusHandlerResult
146 bus_dispatch_message_handler (DBusMessageHandler *handler,
147                               DBusConnection     *connection,
148                               DBusMessage        *message,
149                               void               *user_data)
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   /* If service_name is NULL, this is a message to the bus daemon, not intended
177    * to actually go "on the bus"; e.g. a peer-to-peer ping. Handle these
178    * immediately, especially disconnection messages.
179    */
180   if (service_name == NULL)
181     {
182       if (strcmp (message_name, DBUS_MESSAGE_LOCAL_DISCONNECT) == 0)
183         bus_connection_disconnected (connection);
184
185       /* DBusConnection also handles some of these automatically, we leave
186        * it to do so.
187        */
188       goto out;
189     }
190
191   _dbus_assert (service_name != NULL); /* this message is intended for bus routing */
192   
193   /* Create our transaction */
194   transaction = bus_transaction_new (context);
195   if (transaction == NULL)
196     {
197       BUS_SET_OOM (&error);
198       goto out;
199     }
200   
201   /* Assign a sender to the message */
202   if (bus_connection_is_active (connection))
203     {
204       sender = bus_connection_get_name (connection);
205       _dbus_assert (sender != NULL);
206       
207       if (!dbus_message_set_sender (message, sender))
208         {
209           BUS_SET_OOM (&error);
210           goto out;
211         }
212     }
213
214   if (strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */
215     {
216       if (!bus_driver_handle_message (connection, transaction, message, &error))
217         goto out;
218     }
219   else if (!bus_connection_is_active (connection)) /* clients must talk to bus driver first */
220     {
221       _dbus_verbose ("Received message from non-registered client. Disconnecting.\n");
222       dbus_connection_disconnect (connection);
223     }
224   /* FIXME what if we un-special-case this service and just have a flag
225    * on services that all service owners will get messages to it, not just
226    * the primary owner.
227    */
228   else if (strcmp (service_name, DBUS_SERVICE_BROADCAST) == 0) /* spam! */
229     {
230       if (!bus_dispatch_broadcast_message (transaction, message, &error))
231         goto out;
232     }
233   else  /* route to named service */
234     {
235       DBusString service_string;
236       BusService *service;
237       BusRegistry *registry;
238
239       registry = bus_connection_get_registry (connection);
240       
241       _dbus_string_init_const (&service_string, service_name);
242       service = bus_registry_lookup (registry, &service_string);
243
244       if (service == NULL)
245         {
246           if (!send_service_nonexistent_error (transaction, connection,
247                                                service_name,
248                                                message, &error))
249             goto out;
250         }
251       else
252         {
253           _dbus_assert (bus_service_get_primary_owner (service) != NULL);
254       
255           /* Dispatch the message */
256           if (!bus_transaction_send_message (transaction,
257                                              bus_service_get_primary_owner (service),
258                                              message))
259             {
260               BUS_SET_OOM (&error);
261               goto out;
262             }
263         }
264     }
265   
266  out:
267   if (dbus_error_is_set (&error))
268     {
269       if (!dbus_connection_get_is_connected (connection))
270         {
271           /* If we disconnected it, we won't bother to send it any error
272            * messages.
273            */
274         }
275       else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
276         {
277           bus_connection_send_oom_error (connection, message);
278
279           /* cancel transaction due to OOM */
280           if (transaction != NULL)
281             {
282               bus_transaction_cancel_and_free (transaction);
283               transaction = NULL;
284             }
285         }
286       else
287         {
288           /* Try to send the real error, if no mem to do that, send
289            * the OOM error
290            */
291           _dbus_assert (transaction != NULL);
292           
293           if (!bus_transaction_send_error_reply (transaction, connection,
294                                                  &error, message))
295             {
296               bus_connection_send_oom_error (connection, message);
297
298               /* cancel transaction due to OOM */
299               if (transaction != NULL)
300                 {
301                   bus_transaction_cancel_and_free (transaction);
302                   transaction = NULL;
303                 }
304             }
305         }
306       
307       dbus_error_free (&error);
308     }
309
310   if (transaction != NULL)
311     {
312       bus_transaction_execute_and_free (transaction);
313     }
314
315   dbus_connection_unref (connection);
316   
317   return DBUS_HANDLER_RESULT_ALLOW_MORE_HANDLERS;
318 }
319
320 dbus_bool_t
321 bus_dispatch_add_connection (DBusConnection *connection)
322 {
323   DBusMessageHandler *handler;
324   
325   message_handler_slot = dbus_connection_allocate_data_slot ();
326
327   if (message_handler_slot < 0)
328     return FALSE;
329
330   handler = dbus_message_handler_new (bus_dispatch_message_handler, NULL, NULL);  
331
332   if (!dbus_connection_add_filter (connection, handler))
333     {
334       dbus_message_handler_unref (handler);
335
336       return FALSE;
337     }
338
339   if (!dbus_connection_set_data (connection,
340                                  message_handler_slot,
341                                  handler,
342                                  (DBusFreeFunction)dbus_message_handler_unref))
343     {
344       dbus_connection_remove_filter (connection, handler);
345       dbus_message_handler_unref (handler);
346
347       return FALSE;
348     }
349
350   return TRUE;
351 }
352
353 void
354 bus_dispatch_remove_connection (DBusConnection *connection)
355 {
356   /* Here we tell the bus driver that we want to get off. */
357   bus_driver_remove_connection (connection);
358
359   dbus_connection_set_data (connection,
360                             message_handler_slot,
361                             NULL, NULL);
362 }
363