ff671c589ac2944578e5a9a7f80fd85557eb63fd
[platform/upstream/dbus.git] / bus / connection.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* connection.c  Client connections
3  *
4  * Copyright (C) 2003  Red Hat, Inc.
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 #include "connection.h"
24 #include "dispatch.h"
25 #include "loop.h"
26 #include "services.h"
27 #include "utils.h"
28 #include <dbus/dbus-list.h>
29
30 static void bus_connection_remove_transactions (DBusConnection *connection);
31
32 static int connection_data_slot;
33 static DBusList *connections = NULL;
34
35 typedef struct
36 {
37   DBusConnection *connection;
38   DBusList *services_owned;
39   char *name;
40   DBusList *transaction_messages; /**< Stuff we need to send as part of a transaction */
41   DBusMessage *oom_message;
42   DBusPreallocatedSend *oom_preallocated;
43 } BusConnectionData;
44
45 #define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
46
47 void
48 bus_connection_disconnected (DBusConnection *connection)
49 {
50   BusConnectionData *d;
51   BusService *service;
52   
53   _dbus_warn ("Disconnected\n");
54
55   d = BUS_CONNECTION_DATA (connection);
56   _dbus_assert (d != NULL);  
57
58   /* Drop any service ownership. FIXME Unfortunately, this requires
59    * memory allocation and there doesn't seem to be a good way to
60    * handle it other than sleeping; we can't "fail" the operation of
61    * disconnecting a client, and preallocating a broadcast "service is
62    * now gone" message for every client-service pair seems kind of
63    * involved. Probably we need to do that though, and also
64    * extend BusTransaction to be able to revert generic
65    * stuff, not just sending a message (so we can e.g. revert
66    * removal of service owners).
67    */
68   {
69     BusTransaction *transaction;
70     DBusError error;
71
72     dbus_error_init (&error);
73
74     transaction = NULL;
75     while (transaction == NULL)
76       {
77         transaction = bus_transaction_new ();
78         bus_wait_for_memory ();
79       }
80     
81     while ((service = _dbus_list_get_last (&d->services_owned)))
82       {
83       retry:
84         if (!bus_service_remove_owner (service, connection,
85                                        transaction, &error))
86           {
87             if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
88               {
89                 dbus_error_free (&error);
90                 bus_wait_for_memory ();
91                 goto retry;
92               }
93             else
94               _dbus_assert_not_reached ("Removing service owner failed for non-memory-related reason");
95           }
96       }
97
98     bus_transaction_execute_and_free (transaction);
99   }
100
101   bus_dispatch_remove_connection (connection);
102   
103   /* no more watching */
104   dbus_connection_set_watch_functions (connection,
105                                        NULL, NULL,
106                                        connection,
107                                        NULL);
108
109   bus_connection_remove_transactions (connection);
110   
111   dbus_connection_set_data (connection,
112                             connection_data_slot,
113                             NULL, NULL);
114   
115   _dbus_list_remove (&connections, connection);
116   dbus_connection_unref (connection);
117 }
118
119 static void
120 connection_watch_callback (DBusWatch     *watch,
121                            unsigned int   condition,
122                            void          *data)
123 {
124   DBusConnection *connection = data;
125
126   dbus_connection_ref (connection);
127   
128   dbus_connection_handle_watch (connection, watch, condition);
129
130   while (dbus_connection_dispatch_message (connection));
131   dbus_connection_unref (connection);
132 }
133
134 static void
135 add_connection_watch (DBusWatch      *watch,
136                       DBusConnection *connection)
137 {
138   bus_loop_add_watch (watch, connection_watch_callback, connection,
139                       NULL);
140 }
141
142 static void
143 remove_connection_watch (DBusWatch      *watch,
144                          DBusConnection *connection)
145 {
146   bus_loop_remove_watch (watch, connection_watch_callback, connection);
147 }
148
149 static void
150 free_connection_data (void *data)
151 {
152   BusConnectionData *d = data;
153
154   /* services_owned should be NULL since we should be disconnected */
155   _dbus_assert (d->services_owned == NULL);
156   /* similarly */
157   _dbus_assert (d->transaction_messages == NULL);
158
159   if (d->oom_preallocated)
160     dbus_connection_free_preallocated_send (d->connection, d->oom_preallocated);
161   if (d->oom_message)
162     dbus_message_unref (d->oom_message);
163   
164   dbus_free (d->name);
165   
166   dbus_free (d);
167 }
168
169 dbus_bool_t
170 bus_connection_init (void)
171 {
172   connection_data_slot = dbus_connection_allocate_data_slot ();
173
174   if (connection_data_slot < 0)
175     return FALSE;
176
177   return TRUE;
178 }
179
180 dbus_bool_t
181 bus_connection_setup (DBusConnection *connection)
182 {
183   BusConnectionData *d;
184
185   d = dbus_new0 (BusConnectionData, 1);
186   
187   if (d == NULL)
188     return FALSE;
189
190   d->connection = connection;
191   
192   if (!dbus_connection_set_data (connection,
193                                  connection_data_slot,
194                                  d, free_connection_data))
195     {
196       dbus_free (d);
197       return FALSE;
198     }
199   
200   if (!_dbus_list_append (&connections, connection))
201     {
202       /* this will free our data when connection gets finalized */
203       dbus_connection_disconnect (connection);
204       return FALSE;
205     }
206
207   dbus_connection_ref (connection);
208   
209   dbus_connection_set_watch_functions (connection,
210                                        (DBusAddWatchFunction) add_connection_watch,
211                                        (DBusRemoveWatchFunction) remove_connection_watch,
212                                        connection,
213                                        NULL);
214   
215   /* Setup the connection with the dispatcher */
216   if (!bus_dispatch_add_connection (connection))
217     return FALSE;
218   
219   return TRUE;
220 }
221
222 /**
223  * Checks whether the connection is registered with the message bus.
224  *
225  * @param connection the connection
226  * @returns #TRUE if we're an active message bus participant
227  */
228 dbus_bool_t
229 bus_connection_is_active (DBusConnection *connection)
230 {
231   BusConnectionData *d;
232
233   d = BUS_CONNECTION_DATA (connection);
234   
235   return d != NULL && d->name != NULL;
236 }
237
238 dbus_bool_t
239 bus_connection_preallocate_oom_error (DBusConnection *connection)
240 {
241   DBusMessage *message;
242   DBusPreallocatedSend *preallocated;
243   BusConnectionData *d;
244
245   d = BUS_CONNECTION_DATA (connection);  
246
247   _dbus_assert (d != NULL);
248
249   if (d->oom_preallocated != NULL)
250     return TRUE;
251   
252   preallocated = dbus_connection_preallocate_send (connection);
253   if (preallocated == NULL)
254     return FALSE;
255
256   message = dbus_message_new (DBUS_SERVICE_DBUS,
257                               DBUS_ERROR_NO_MEMORY);
258   if (message == NULL)
259     {
260       dbus_connection_free_preallocated_send (connection, preallocated);
261       return FALSE;
262     }
263
264   /* set reply serial to placeholder value just so space is already allocated
265    * for it.
266    */
267   if (!dbus_message_set_reply_serial (message, 14))
268     {
269       dbus_connection_free_preallocated_send (connection, preallocated);
270       dbus_message_unref (message);
271       return FALSE;
272     }
273
274   d->oom_message = message;
275   d->oom_preallocated = preallocated;
276   
277   return TRUE;
278 }
279
280 void
281 bus_connection_send_oom_error (DBusConnection *connection,
282                                DBusMessage    *in_reply_to)
283 {
284   BusConnectionData *d;
285
286   d = BUS_CONNECTION_DATA (connection);  
287
288   _dbus_assert (d != NULL);  
289   _dbus_assert (d->oom_message != NULL);
290
291   /* should always succeed since we set it to a placeholder earlier */
292   if (!dbus_message_set_reply_serial (d->oom_message,
293                                       dbus_message_get_serial (in_reply_to)))
294     _dbus_assert_not_reached ("Failed to set reply serial for preallocated oom message");
295
296   dbus_connection_send_preallocated (connection, d->oom_preallocated,
297                                      d->oom_message, NULL);
298
299   dbus_message_unref (d->oom_message);
300   d->oom_message = NULL;
301   d->oom_preallocated = NULL;
302 }
303
304 dbus_bool_t
305 bus_connection_add_owned_service (DBusConnection *connection,
306                                   BusService     *service)
307 {
308   BusConnectionData *d;
309
310   d = BUS_CONNECTION_DATA (connection);
311   _dbus_assert (d != NULL);
312
313   if (!_dbus_list_append (&d->services_owned,
314                           service))
315     return FALSE;
316
317   return TRUE;
318 }
319
320 void
321 bus_connection_remove_owned_service (DBusConnection *connection,
322                                      BusService     *service)
323 {
324   BusConnectionData *d;
325
326   d = BUS_CONNECTION_DATA (connection);
327   _dbus_assert (d != NULL);
328
329   _dbus_list_remove_last (&d->services_owned, service);
330 }
331
332 dbus_bool_t
333 bus_connection_set_name (DBusConnection   *connection,
334                          const DBusString *name)
335 {
336   const char *c_name;
337   BusConnectionData *d;
338   
339   d = BUS_CONNECTION_DATA (connection);
340   _dbus_assert (d != NULL);
341   _dbus_assert (d->name == NULL);
342
343   _dbus_string_get_const_data (name, &c_name);
344
345   d->name = _dbus_strdup (c_name);
346
347   if (d->name == NULL)
348     return FALSE;
349
350   return TRUE;
351 }
352
353 const char *
354 bus_connection_get_name (DBusConnection *connection)
355 {
356   BusConnectionData *d;
357   
358   d = BUS_CONNECTION_DATA (connection);
359   _dbus_assert (d != NULL);
360   
361   return d->name;
362 }
363
364 /**
365  * Calls function on each connection; if the function returns
366  * #FALSE, stops iterating.
367  *
368  * @param function the function
369  * @param data data to pass to it as a second arg
370  */
371 void
372 bus_connection_foreach (BusConnectionForeachFunction  function,
373                         void                         *data)
374 {
375   DBusList *link;
376   
377   link = _dbus_list_get_first_link (&connections);
378   while (link != NULL)
379     {
380       DBusConnection *connection = link->data;
381       DBusList *next = _dbus_list_get_next_link (&connections, link);
382
383       if (!(* function) (connection, data))
384         break;
385       
386       link = next;
387     }
388 }
389
390 typedef struct
391 {
392   BusTransaction *transaction;
393   DBusMessage    *message;
394   DBusPreallocatedSend *preallocated;
395 } MessageToSend;
396
397 struct BusTransaction
398 {
399   DBusList *connections;
400
401 };
402
403 static void
404 message_to_send_free (DBusConnection *connection,
405                       MessageToSend  *to_send)
406 {
407   if (to_send->message)
408     dbus_message_unref (to_send->message);
409
410   if (to_send->preallocated)
411     dbus_connection_free_preallocated_send (connection, to_send->preallocated);
412
413   dbus_free (to_send);
414 }
415
416 BusTransaction*
417 bus_transaction_new (void)
418 {
419   BusTransaction *transaction;
420
421   transaction = dbus_new0 (BusTransaction, 1);
422   if (transaction == NULL)
423     return NULL;
424
425   return transaction;
426 }
427
428 dbus_bool_t
429 bus_transaction_send_message (BusTransaction *transaction,
430                               DBusConnection *connection,
431                               DBusMessage    *message)
432 {
433   MessageToSend *to_send;
434   BusConnectionData *d;
435   DBusList *link;
436
437   if (!dbus_connection_get_is_connected (connection))
438     return TRUE; /* silently ignore disconnected connections */
439   
440   d = BUS_CONNECTION_DATA (connection);
441   _dbus_assert (d != NULL);
442   
443   to_send = dbus_new (MessageToSend, 1);
444   if (to_send == NULL)
445     {
446       return FALSE;
447     }
448
449   to_send->preallocated = dbus_connection_preallocate_send (connection);
450   if (to_send->preallocated == NULL)
451     {
452       dbus_free (to_send);
453       return FALSE;
454     }  
455   
456   dbus_message_ref (message);
457   to_send->message = message;
458   to_send->transaction = transaction;
459
460   if (!_dbus_list_prepend (&d->transaction_messages, to_send))
461     {
462       message_to_send_free (connection, to_send);
463       return FALSE;
464     }
465   
466   /* See if we already had this connection in the list
467    * for this transaction. If we have a pending message,
468    * then we should already be in transaction->connections
469    */
470   link = _dbus_list_get_first_link (&d->transaction_messages);
471   _dbus_assert (link->data == to_send);
472   link = _dbus_list_get_next_link (&d->transaction_messages, link);
473   while (link != NULL)
474     {
475       MessageToSend *m = link->data;
476       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
477       
478       if (m->transaction == transaction)
479         break;
480         
481       link = next;
482     }
483
484   if (link == NULL)
485     {
486       if (!_dbus_list_prepend (&transaction->connections, connection))
487         {
488           _dbus_list_remove (&d->transaction_messages, to_send);
489           message_to_send_free (connection, to_send);
490           return FALSE;
491         }
492     }
493
494   return TRUE;
495 }
496
497 static void
498 connection_cancel_transaction (DBusConnection *connection,
499                                BusTransaction *transaction)
500 {
501   DBusList *link;
502   BusConnectionData *d;
503   
504   d = BUS_CONNECTION_DATA (connection);
505   _dbus_assert (d != NULL);
506   
507   link = _dbus_list_get_first_link (&d->transaction_messages);
508   while (link != NULL)
509     {
510       MessageToSend *m = link->data;
511       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
512       
513       if (m->transaction == transaction)
514         {
515           _dbus_list_remove_link (&d->transaction_messages,
516                                   link);
517           
518           message_to_send_free (connection, m);
519         }
520         
521       link = next;
522     }
523 }
524
525 void
526 bus_transaction_cancel_and_free (BusTransaction *transaction)
527 {
528   DBusConnection *connection;
529   
530   while ((connection = _dbus_list_pop_first (&transaction->connections)))
531     connection_cancel_transaction (connection, transaction);
532
533   _dbus_assert (transaction->connections == NULL);
534
535   dbus_free (transaction);
536 }
537
538 static void
539 connection_execute_transaction (DBusConnection *connection,
540                                 BusTransaction *transaction)
541 {
542   DBusList *link;
543   BusConnectionData *d;
544   
545   d = BUS_CONNECTION_DATA (connection);
546   _dbus_assert (d != NULL);
547
548   /* Send the queue in order (FIFO) */
549   link = _dbus_list_get_last_link (&d->transaction_messages);
550   while (link != NULL)
551     {
552       MessageToSend *m = link->data;
553       DBusList *prev = _dbus_list_get_prev_link (&d->transaction_messages, link);
554       
555       if (m->transaction == transaction)
556         {
557           _dbus_list_remove_link (&d->transaction_messages,
558                                   link);
559
560           dbus_connection_send_preallocated (connection,
561                                              m->preallocated,
562                                              m->message,
563                                              NULL);
564
565           m->preallocated = NULL; /* so we don't double-free it */
566           
567           message_to_send_free (connection, m);
568         }
569         
570       link = prev;
571     }
572 }
573
574 void
575 bus_transaction_execute_and_free (BusTransaction *transaction)
576 {
577   /* For each connection in transaction->connections
578    * send the messages
579    */
580   DBusConnection *connection;
581   
582   while ((connection = _dbus_list_pop_first (&transaction->connections)))
583     connection_execute_transaction (connection, transaction);
584
585   _dbus_assert (transaction->connections == NULL);
586
587   dbus_free (transaction);
588 }
589
590 static void
591 bus_connection_remove_transactions (DBusConnection *connection)
592 {
593   MessageToSend *to_send;
594   BusConnectionData *d;
595   
596   d = BUS_CONNECTION_DATA (connection);
597   _dbus_assert (d != NULL);
598   
599   while ((to_send = _dbus_list_get_first (&d->transaction_messages)))
600     {
601       /* only has an effect for the first MessageToSend listing this transaction */
602       _dbus_list_remove (&to_send->transaction->connections,
603                          connection);
604
605       _dbus_list_remove (&d->transaction_messages, to_send);
606       message_to_send_free (connection, to_send);
607     }
608 }
609
610 /**
611  * Converts the DBusError to a message reply
612  */
613 dbus_bool_t
614 bus_transaction_send_error_reply (BusTransaction  *transaction,
615                                   DBusConnection  *connection,
616                                   const DBusError *error,
617                                   DBusMessage     *in_reply_to)
618 {
619   DBusMessage *reply;
620
621   _dbus_assert (error != NULL);
622   _DBUS_ASSERT_ERROR_IS_SET (error);
623   
624   reply = dbus_message_new_error_reply (in_reply_to,
625                                         error->name,
626                                         error->message);
627   if (reply == NULL)
628     return FALSE;
629
630   if (!bus_transaction_send_message (transaction, connection, reply))
631     {
632       dbus_message_unref (reply);
633       return FALSE;
634     }
635
636   return TRUE;
637 }