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