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, 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, 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                                             NULL,
292                                             connection,
293                                             NULL))
294     {
295       dbus_connection_disconnect (connection);
296       return FALSE;
297     }
298   
299   if (!dbus_connection_set_timeout_functions (connection,
300                                               (DBusAddTimeoutFunction) add_connection_timeout,
301                                               (DBusRemoveTimeoutFunction) remove_connection_timeout,
302                                               NULL,
303                                               connection, NULL))
304     {
305       dbus_connection_disconnect (connection);
306       return FALSE;
307     }
308
309   
310   /* Setup the connection with the dispatcher */
311   if (!bus_dispatch_add_connection (connection))
312     {
313       dbus_connection_disconnect (connection);
314       return FALSE;
315     }
316
317   dbus_connection_ref (connection);
318   
319   return TRUE;
320 }
321
322
323 /**
324  * Calls function on each connection; if the function returns
325  * #FALSE, stops iterating.
326  *
327  * @param connections the connections object
328  * @param function the function
329  * @param data data to pass to it as a second arg
330  */
331 void
332 bus_connections_foreach (BusConnections               *connections,
333                          BusConnectionForeachFunction  function,
334                         void                          *data)
335 {
336   DBusList *link;
337   
338   link = _dbus_list_get_first_link (&connections->list);
339   while (link != NULL)
340     {
341       DBusConnection *connection = link->data;
342       DBusList *next = _dbus_list_get_next_link (&connections->list, link);
343
344       if (!(* function) (connection, data))
345         break;
346       
347       link = next;
348     }
349 }
350
351 BusContext*
352 bus_connections_get_context (BusConnections *connections)
353 {
354   return connections->context;
355 }
356
357 BusContext*
358 bus_connection_get_context (DBusConnection *connection)
359 {
360   BusConnectionData *d;
361
362   d = BUS_CONNECTION_DATA (connection);
363
364   _dbus_assert (d != NULL);
365
366   return d->connections->context;
367 }
368
369 BusConnections*
370 bus_connection_get_connections (DBusConnection *connection)
371 {
372   BusConnectionData *d;
373     
374   d = BUS_CONNECTION_DATA (connection);
375
376   _dbus_assert (d != NULL);
377
378   return d->connections;
379 }
380
381 BusRegistry*
382 bus_connection_get_registry (DBusConnection *connection)
383 {
384   BusConnectionData *d;
385
386   d = BUS_CONNECTION_DATA (connection);
387
388   _dbus_assert (d != NULL);
389
390   return bus_context_get_registry (d->connections->context);
391 }
392
393 BusActivation*
394 bus_connection_get_activation (DBusConnection *connection)
395 {
396   BusConnectionData *d;
397
398   d = BUS_CONNECTION_DATA (connection);
399
400   _dbus_assert (d != NULL);
401
402   return bus_context_get_activation (d->connections->context);
403 }
404
405 /**
406  * Checks whether the connection is registered with the message bus.
407  *
408  * @param connection the connection
409  * @returns #TRUE if we're an active message bus participant
410  */
411 dbus_bool_t
412 bus_connection_is_active (DBusConnection *connection)
413 {
414   BusConnectionData *d;
415
416   d = BUS_CONNECTION_DATA (connection);
417   
418   return d != NULL && d->name != NULL;
419 }
420
421 dbus_bool_t
422 bus_connection_preallocate_oom_error (DBusConnection *connection)
423 {
424   DBusMessage *message;
425   DBusPreallocatedSend *preallocated;
426   BusConnectionData *d;
427
428   d = BUS_CONNECTION_DATA (connection);  
429
430   _dbus_assert (d != NULL);
431
432   if (d->oom_preallocated != NULL)
433     return TRUE;
434   
435   preallocated = dbus_connection_preallocate_send (connection);
436   if (preallocated == NULL)
437     return FALSE;
438
439   message = dbus_message_new (DBUS_SERVICE_DBUS,
440                               DBUS_ERROR_NO_MEMORY);
441   if (message == NULL)
442     {
443       dbus_connection_free_preallocated_send (connection, preallocated);
444       return FALSE;
445     }
446
447   dbus_message_set_is_error (message, TRUE);
448   
449   /* set reply serial to placeholder value just so space is already allocated
450    * for it.
451    */
452   if (!dbus_message_set_reply_serial (message, 14))
453     {
454       dbus_connection_free_preallocated_send (connection, preallocated);
455       dbus_message_unref (message);
456       return FALSE;
457     }
458
459   d->oom_message = message;
460   d->oom_preallocated = preallocated;
461   
462   return TRUE;
463 }
464
465 void
466 bus_connection_send_oom_error (DBusConnection *connection,
467                                DBusMessage    *in_reply_to)
468 {
469   BusConnectionData *d;
470
471   d = BUS_CONNECTION_DATA (connection);  
472
473   _dbus_assert (d != NULL);  
474   _dbus_assert (d->oom_message != NULL);
475
476   /* should always succeed since we set it to a placeholder earlier */
477   if (!dbus_message_set_reply_serial (d->oom_message,
478                                       dbus_message_get_serial (in_reply_to)))
479     _dbus_assert_not_reached ("Failed to set reply serial for preallocated oom message");
480
481   dbus_connection_send_preallocated (connection, d->oom_preallocated,
482                                      d->oom_message, NULL);
483
484   dbus_message_unref (d->oom_message);
485   d->oom_message = NULL;
486   d->oom_preallocated = NULL;
487 }
488
489 dbus_bool_t
490 bus_connection_add_owned_service (DBusConnection *connection,
491                                   BusService     *service)
492 {
493   BusConnectionData *d;
494
495   d = BUS_CONNECTION_DATA (connection);
496   _dbus_assert (d != NULL);
497
498   if (!_dbus_list_append (&d->services_owned,
499                           service))
500     return FALSE;
501
502   return TRUE;
503 }
504
505 void
506 bus_connection_remove_owned_service (DBusConnection *connection,
507                                      BusService     *service)
508 {
509   BusConnectionData *d;
510
511   d = BUS_CONNECTION_DATA (connection);
512   _dbus_assert (d != NULL);
513
514   _dbus_list_remove_last (&d->services_owned, service);
515 }
516
517 dbus_bool_t
518 bus_connection_set_name (DBusConnection   *connection,
519                          const DBusString *name)
520 {
521   const char *c_name;
522   BusConnectionData *d;
523   
524   d = BUS_CONNECTION_DATA (connection);
525   _dbus_assert (d != NULL);
526   _dbus_assert (d->name == NULL);
527
528   _dbus_string_get_const_data (name, &c_name);
529
530   d->name = _dbus_strdup (c_name);
531
532   if (d->name == NULL)
533     return FALSE;
534
535   return TRUE;
536 }
537
538 const char *
539 bus_connection_get_name (DBusConnection *connection)
540 {
541   BusConnectionData *d;
542   
543   d = BUS_CONNECTION_DATA (connection);
544   _dbus_assert (d != NULL);
545   
546   return d->name;
547 }
548
549 typedef struct
550 {
551   BusTransaction *transaction;
552   DBusMessage    *message;
553   DBusPreallocatedSend *preallocated;
554 } MessageToSend;
555
556 struct BusTransaction
557 {
558   DBusList *connections;
559   BusContext *context;
560 };
561
562 static void
563 message_to_send_free (DBusConnection *connection,
564                       MessageToSend  *to_send)
565 {
566   if (to_send->message)
567     dbus_message_unref (to_send->message);
568
569   if (to_send->preallocated)
570     dbus_connection_free_preallocated_send (connection, to_send->preallocated);
571
572   dbus_free (to_send);
573 }
574
575 BusTransaction*
576 bus_transaction_new (BusContext *context)
577 {
578   BusTransaction *transaction;
579
580   transaction = dbus_new0 (BusTransaction, 1);
581   if (transaction == NULL)
582     return NULL;
583
584   transaction->context = context;
585   
586   return transaction;
587 }
588
589 BusContext*
590 bus_transaction_get_context (BusTransaction  *transaction)
591 {
592   return transaction->context;
593 }
594
595 BusConnections*
596 bus_transaction_get_connections (BusTransaction  *transaction)
597 {
598   return bus_context_get_connections (transaction->context);
599 }
600
601 dbus_bool_t
602 bus_transaction_send_message (BusTransaction *transaction,
603                               DBusConnection *connection,
604                               DBusMessage    *message)
605 {
606   MessageToSend *to_send;
607   BusConnectionData *d;
608   DBusList *link;
609
610   _dbus_verbose ("  trying to add message %s to transaction\n",
611                  dbus_message_get_name (message));
612   
613   if (!dbus_connection_get_is_connected (connection))
614     return TRUE; /* silently ignore disconnected connections */
615   
616   d = BUS_CONNECTION_DATA (connection);
617   _dbus_assert (d != NULL);
618   
619   to_send = dbus_new (MessageToSend, 1);
620   if (to_send == NULL)
621     {
622       return FALSE;
623     }
624
625   to_send->preallocated = dbus_connection_preallocate_send (connection);
626   if (to_send->preallocated == NULL)
627     {
628       dbus_free (to_send);
629       return FALSE;
630     }  
631   
632   dbus_message_ref (message);
633   to_send->message = message;
634   to_send->transaction = transaction;
635
636   if (!_dbus_list_prepend (&d->transaction_messages, to_send))
637     {
638       message_to_send_free (connection, to_send);
639       return FALSE;
640     }
641   
642   /* See if we already had this connection in the list
643    * for this transaction. If we have a pending message,
644    * then we should already be in transaction->connections
645    */
646   link = _dbus_list_get_first_link (&d->transaction_messages);
647   _dbus_assert (link->data == to_send);
648   link = _dbus_list_get_next_link (&d->transaction_messages, link);
649   while (link != NULL)
650     {
651       MessageToSend *m = link->data;
652       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
653       
654       if (m->transaction == transaction)
655         break;
656         
657       link = next;
658     }
659
660   if (link == NULL)
661     {
662       if (!_dbus_list_prepend (&transaction->connections, connection))
663         {
664           _dbus_list_remove (&d->transaction_messages, to_send);
665           message_to_send_free (connection, to_send);
666           return FALSE;
667         }
668     }
669
670   return TRUE;
671 }
672
673 static void
674 connection_cancel_transaction (DBusConnection *connection,
675                                BusTransaction *transaction)
676 {
677   DBusList *link;
678   BusConnectionData *d;
679   
680   d = BUS_CONNECTION_DATA (connection);
681   _dbus_assert (d != NULL);
682   
683   link = _dbus_list_get_first_link (&d->transaction_messages);
684   while (link != NULL)
685     {
686       MessageToSend *m = link->data;
687       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
688       
689       if (m->transaction == transaction)
690         {
691           _dbus_list_remove_link (&d->transaction_messages,
692                                   link);
693           
694           message_to_send_free (connection, m);
695         }
696         
697       link = next;
698     }
699 }
700
701 void
702 bus_transaction_cancel_and_free (BusTransaction *transaction)
703 {
704   DBusConnection *connection;
705   
706   while ((connection = _dbus_list_pop_first (&transaction->connections)))
707     connection_cancel_transaction (connection, transaction);
708
709   _dbus_assert (transaction->connections == NULL);
710
711   dbus_free (transaction);
712 }
713
714 static void
715 connection_execute_transaction (DBusConnection *connection,
716                                 BusTransaction *transaction)
717 {
718   DBusList *link;
719   BusConnectionData *d;
720   
721   d = BUS_CONNECTION_DATA (connection);
722   _dbus_assert (d != NULL);
723
724   /* Send the queue in order (FIFO) */
725   link = _dbus_list_get_last_link (&d->transaction_messages);
726   while (link != NULL)
727     {
728       MessageToSend *m = link->data;
729       DBusList *prev = _dbus_list_get_prev_link (&d->transaction_messages, link);
730       
731       if (m->transaction == transaction)
732         {
733           _dbus_list_remove_link (&d->transaction_messages,
734                                   link);
735
736           dbus_connection_send_preallocated (connection,
737                                              m->preallocated,
738                                              m->message,
739                                              NULL);
740
741           m->preallocated = NULL; /* so we don't double-free it */
742           
743           message_to_send_free (connection, m);
744         }
745         
746       link = prev;
747     }
748 }
749
750 void
751 bus_transaction_execute_and_free (BusTransaction *transaction)
752 {
753   /* For each connection in transaction->connections
754    * send the messages
755    */
756   DBusConnection *connection;
757   
758   while ((connection = _dbus_list_pop_first (&transaction->connections)))
759     connection_execute_transaction (connection, transaction);
760
761   _dbus_assert (transaction->connections == NULL);
762
763   dbus_free (transaction);
764 }
765
766 static void
767 bus_connection_remove_transactions (DBusConnection *connection)
768 {
769   MessageToSend *to_send;
770   BusConnectionData *d;
771   
772   d = BUS_CONNECTION_DATA (connection);
773   _dbus_assert (d != NULL);
774   
775   while ((to_send = _dbus_list_get_first (&d->transaction_messages)))
776     {
777       /* only has an effect for the first MessageToSend listing this transaction */
778       _dbus_list_remove (&to_send->transaction->connections,
779                          connection);
780
781       _dbus_list_remove (&d->transaction_messages, to_send);
782       message_to_send_free (connection, to_send);
783     }
784 }
785
786 /**
787  * Converts the DBusError to a message reply
788  */
789 dbus_bool_t
790 bus_transaction_send_error_reply (BusTransaction  *transaction,
791                                   DBusConnection  *connection,
792                                   const DBusError *error,
793                                   DBusMessage     *in_reply_to)
794 {
795   DBusMessage *reply;
796
797   _dbus_assert (error != NULL);
798   _DBUS_ASSERT_ERROR_IS_SET (error);
799
800   _dbus_verbose ("  trying to add error %s to transaction\n",
801                  error->name);
802   
803   reply = dbus_message_new_error_reply (in_reply_to,
804                                         error->name,
805                                         error->message);
806   if (reply == NULL)
807     return FALSE;
808
809   if (!bus_transaction_send_message (transaction, connection, reply))
810     {
811       dbus_message_unref (reply);
812       return FALSE;
813     }
814
815   return TRUE;
816 }