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