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