2003-03-14 Havoc Pennington <hp@redhat.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   if (!dbus_connection_set_watch_functions (connection,
112                                             NULL, NULL,
113                                             connection,
114                                             NULL))
115     _dbus_assert_not_reached ("setting watch functions to NULL failed");
116
117   if (!dbus_connection_set_timeout_functions (connection,
118                                               NULL, NULL,
119                                               connection,
120                                               NULL))
121     _dbus_assert_not_reached ("setting timeout functions to NULL failed");
122   
123   bus_connection_remove_transactions (connection);
124
125   _dbus_list_remove (&d->connections->list, connection);
126
127   /* frees "d" as side effect */
128   dbus_connection_set_data (connection,
129                             connection_data_slot,
130                             NULL, NULL);
131
132   dbus_connection_unref (connection);
133 }
134
135 static void
136 connection_watch_callback (DBusWatch     *watch,
137                            unsigned int   condition,
138                            void          *data)
139 {
140   DBusConnection *connection = data;
141
142   dbus_connection_ref (connection);
143   
144   dbus_connection_handle_watch (connection, watch, condition);
145
146   while (dbus_connection_dispatch_message (connection))
147     ;
148   dbus_connection_unref (connection);
149 }
150
151 static dbus_bool_t
152 add_connection_watch (DBusWatch      *watch,
153                       DBusConnection *connection)
154 {
155   return bus_loop_add_watch (watch, connection_watch_callback, connection,
156                              NULL);
157 }
158
159 static void
160 remove_connection_watch (DBusWatch      *watch,
161                          DBusConnection *connection)
162 {
163   bus_loop_remove_watch (watch, connection_watch_callback, connection);
164 }
165
166 static void
167 connection_timeout_callback (DBusTimeout   *timeout,
168                              void          *data)
169 {
170   DBusConnection *connection = data;
171
172   dbus_connection_ref (connection);
173   
174   dbus_timeout_handle (timeout);
175
176   while (dbus_connection_dispatch_message (connection))
177     ;
178   dbus_connection_unref (connection);
179 }
180
181 static dbus_bool_t
182 add_connection_timeout (DBusTimeout    *timeout,
183                         DBusConnection *connection)
184 {
185   return bus_loop_add_timeout (timeout, connection_timeout_callback, connection, NULL);
186 }
187
188 static void
189 remove_connection_timeout (DBusTimeout    *timeout,
190                            DBusConnection *connection)
191 {
192   bus_loop_remove_timeout (timeout, connection_timeout_callback, connection);
193 }
194
195 static void
196 free_connection_data (void *data)
197 {
198   BusConnectionData *d = data;
199
200   /* services_owned should be NULL since we should be disconnected */
201   _dbus_assert (d->services_owned == NULL);
202   /* similarly */
203   _dbus_assert (d->transaction_messages == NULL);
204
205   if (d->oom_preallocated)
206     dbus_connection_free_preallocated_send (d->connection, d->oom_preallocated);
207   if (d->oom_message)
208     dbus_message_unref (d->oom_message);
209   
210   dbus_free (d->name);
211   
212   dbus_free (d);
213 }
214
215 BusConnections*
216 bus_connections_new (BusContext *context)
217 {
218   BusConnections *connections;
219
220   if (connection_data_slot < 0)
221     {
222       connection_data_slot = dbus_connection_allocate_data_slot ();
223       
224       if (connection_data_slot < 0)
225         return NULL;
226     }
227
228   connections = dbus_new0 (BusConnections, 1);
229   if (connections == NULL)
230     return NULL;
231   
232   connections->refcount = 1;
233   connections->context = context;
234   
235   return connections;
236 }
237
238 void
239 bus_connections_ref (BusConnections *connections)
240 {
241   _dbus_assert (connections->refcount > 0);
242   connections->refcount += 1;
243 }
244
245 void
246 bus_connections_unref (BusConnections *connections)
247 {
248   _dbus_assert (connections->refcount > 0);
249   connections->refcount -= 1;
250   if (connections->refcount == 0)
251     {
252       /* FIXME free each connection... */
253       _dbus_assert_not_reached ("shutting down connections not implemented");
254       
255       _dbus_list_clear (&connections->list);
256       
257       dbus_free (connections);      
258     }
259 }
260
261 dbus_bool_t
262 bus_connections_setup_connection (BusConnections *connections,
263                                   DBusConnection *connection)
264 {
265   BusConnectionData *d;
266
267   d = dbus_new0 (BusConnectionData, 1);
268   
269   if (d == NULL)
270     return FALSE;
271
272   d->connections = connections;
273   d->connection = connection;
274   
275   if (!dbus_connection_set_data (connection,
276                                  connection_data_slot,
277                                  d, free_connection_data))
278     {
279       dbus_free (d);
280       return FALSE;
281     }
282   
283   if (!_dbus_list_append (&connections->list, connection))
284     {
285       /* this will free our data when connection gets finalized */
286       dbus_connection_disconnect (connection);
287       return FALSE;
288     }
289   
290   if (!dbus_connection_set_watch_functions (connection,
291                                             (DBusAddWatchFunction) add_connection_watch,
292                                             (DBusRemoveWatchFunction) remove_connection_watch,
293                                             connection,
294                                             NULL))
295     {
296       dbus_connection_disconnect (connection);
297       return FALSE;
298     }
299   
300   if (!dbus_connection_set_timeout_functions (connection,
301                                               (DBusAddTimeoutFunction) add_connection_timeout,
302                                               (DBusRemoveTimeoutFunction) remove_connection_timeout,
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   /* set reply serial to placeholder value just so space is already allocated
448    * for it.
449    */
450   if (!dbus_message_set_reply_serial (message, 14))
451     {
452       dbus_connection_free_preallocated_send (connection, preallocated);
453       dbus_message_unref (message);
454       return FALSE;
455     }
456
457   d->oom_message = message;
458   d->oom_preallocated = preallocated;
459   
460   return TRUE;
461 }
462
463 void
464 bus_connection_send_oom_error (DBusConnection *connection,
465                                DBusMessage    *in_reply_to)
466 {
467   BusConnectionData *d;
468
469   d = BUS_CONNECTION_DATA (connection);  
470
471   _dbus_assert (d != NULL);  
472   _dbus_assert (d->oom_message != NULL);
473
474   /* should always succeed since we set it to a placeholder earlier */
475   if (!dbus_message_set_reply_serial (d->oom_message,
476                                       dbus_message_get_serial (in_reply_to)))
477     _dbus_assert_not_reached ("Failed to set reply serial for preallocated oom message");
478
479   dbus_connection_send_preallocated (connection, d->oom_preallocated,
480                                      d->oom_message, NULL);
481
482   dbus_message_unref (d->oom_message);
483   d->oom_message = NULL;
484   d->oom_preallocated = NULL;
485 }
486
487 dbus_bool_t
488 bus_connection_add_owned_service (DBusConnection *connection,
489                                   BusService     *service)
490 {
491   BusConnectionData *d;
492
493   d = BUS_CONNECTION_DATA (connection);
494   _dbus_assert (d != NULL);
495
496   if (!_dbus_list_append (&d->services_owned,
497                           service))
498     return FALSE;
499
500   return TRUE;
501 }
502
503 void
504 bus_connection_remove_owned_service (DBusConnection *connection,
505                                      BusService     *service)
506 {
507   BusConnectionData *d;
508
509   d = BUS_CONNECTION_DATA (connection);
510   _dbus_assert (d != NULL);
511
512   _dbus_list_remove_last (&d->services_owned, service);
513 }
514
515 dbus_bool_t
516 bus_connection_set_name (DBusConnection   *connection,
517                          const DBusString *name)
518 {
519   const char *c_name;
520   BusConnectionData *d;
521   
522   d = BUS_CONNECTION_DATA (connection);
523   _dbus_assert (d != NULL);
524   _dbus_assert (d->name == NULL);
525
526   _dbus_string_get_const_data (name, &c_name);
527
528   d->name = _dbus_strdup (c_name);
529
530   if (d->name == NULL)
531     return FALSE;
532
533   return TRUE;
534 }
535
536 const char *
537 bus_connection_get_name (DBusConnection *connection)
538 {
539   BusConnectionData *d;
540   
541   d = BUS_CONNECTION_DATA (connection);
542   _dbus_assert (d != NULL);
543   
544   return d->name;
545 }
546
547 typedef struct
548 {
549   BusTransaction *transaction;
550   DBusMessage    *message;
551   DBusPreallocatedSend *preallocated;
552 } MessageToSend;
553
554 struct BusTransaction
555 {
556   DBusList *connections;
557   BusContext *context;
558 };
559
560 static void
561 message_to_send_free (DBusConnection *connection,
562                       MessageToSend  *to_send)
563 {
564   if (to_send->message)
565     dbus_message_unref (to_send->message);
566
567   if (to_send->preallocated)
568     dbus_connection_free_preallocated_send (connection, to_send->preallocated);
569
570   dbus_free (to_send);
571 }
572
573 BusTransaction*
574 bus_transaction_new (BusContext *context)
575 {
576   BusTransaction *transaction;
577
578   transaction = dbus_new0 (BusTransaction, 1);
579   if (transaction == NULL)
580     return NULL;
581
582   transaction->context = context;
583   
584   return transaction;
585 }
586
587 BusContext*
588 bus_transaction_get_context (BusTransaction  *transaction)
589 {
590   return transaction->context;
591 }
592
593 BusConnections*
594 bus_transaction_get_connections (BusTransaction  *transaction)
595 {
596   return bus_context_get_connections (transaction->context);
597 }
598
599 dbus_bool_t
600 bus_transaction_send_message (BusTransaction *transaction,
601                               DBusConnection *connection,
602                               DBusMessage    *message)
603 {
604   MessageToSend *to_send;
605   BusConnectionData *d;
606   DBusList *link;
607
608   if (!dbus_connection_get_is_connected (connection))
609     return TRUE; /* silently ignore disconnected connections */
610   
611   d = BUS_CONNECTION_DATA (connection);
612   _dbus_assert (d != NULL);
613   
614   to_send = dbus_new (MessageToSend, 1);
615   if (to_send == NULL)
616     {
617       return FALSE;
618     }
619
620   to_send->preallocated = dbus_connection_preallocate_send (connection);
621   if (to_send->preallocated == NULL)
622     {
623       dbus_free (to_send);
624       return FALSE;
625     }  
626   
627   dbus_message_ref (message);
628   to_send->message = message;
629   to_send->transaction = transaction;
630
631   if (!_dbus_list_prepend (&d->transaction_messages, to_send))
632     {
633       message_to_send_free (connection, to_send);
634       return FALSE;
635     }
636   
637   /* See if we already had this connection in the list
638    * for this transaction. If we have a pending message,
639    * then we should already be in transaction->connections
640    */
641   link = _dbus_list_get_first_link (&d->transaction_messages);
642   _dbus_assert (link->data == to_send);
643   link = _dbus_list_get_next_link (&d->transaction_messages, link);
644   while (link != NULL)
645     {
646       MessageToSend *m = link->data;
647       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
648       
649       if (m->transaction == transaction)
650         break;
651         
652       link = next;
653     }
654
655   if (link == NULL)
656     {
657       if (!_dbus_list_prepend (&transaction->connections, connection))
658         {
659           _dbus_list_remove (&d->transaction_messages, to_send);
660           message_to_send_free (connection, to_send);
661           return FALSE;
662         }
663     }
664
665   return TRUE;
666 }
667
668 static void
669 connection_cancel_transaction (DBusConnection *connection,
670                                BusTransaction *transaction)
671 {
672   DBusList *link;
673   BusConnectionData *d;
674   
675   d = BUS_CONNECTION_DATA (connection);
676   _dbus_assert (d != NULL);
677   
678   link = _dbus_list_get_first_link (&d->transaction_messages);
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         {
686           _dbus_list_remove_link (&d->transaction_messages,
687                                   link);
688           
689           message_to_send_free (connection, m);
690         }
691         
692       link = next;
693     }
694 }
695
696 void
697 bus_transaction_cancel_and_free (BusTransaction *transaction)
698 {
699   DBusConnection *connection;
700   
701   while ((connection = _dbus_list_pop_first (&transaction->connections)))
702     connection_cancel_transaction (connection, transaction);
703
704   _dbus_assert (transaction->connections == NULL);
705
706   dbus_free (transaction);
707 }
708
709 static void
710 connection_execute_transaction (DBusConnection *connection,
711                                 BusTransaction *transaction)
712 {
713   DBusList *link;
714   BusConnectionData *d;
715   
716   d = BUS_CONNECTION_DATA (connection);
717   _dbus_assert (d != NULL);
718
719   /* Send the queue in order (FIFO) */
720   link = _dbus_list_get_last_link (&d->transaction_messages);
721   while (link != NULL)
722     {
723       MessageToSend *m = link->data;
724       DBusList *prev = _dbus_list_get_prev_link (&d->transaction_messages, link);
725       
726       if (m->transaction == transaction)
727         {
728           _dbus_list_remove_link (&d->transaction_messages,
729                                   link);
730
731           dbus_connection_send_preallocated (connection,
732                                              m->preallocated,
733                                              m->message,
734                                              NULL);
735
736           m->preallocated = NULL; /* so we don't double-free it */
737           
738           message_to_send_free (connection, m);
739         }
740         
741       link = prev;
742     }
743 }
744
745 void
746 bus_transaction_execute_and_free (BusTransaction *transaction)
747 {
748   /* For each connection in transaction->connections
749    * send the messages
750    */
751   DBusConnection *connection;
752   
753   while ((connection = _dbus_list_pop_first (&transaction->connections)))
754     connection_execute_transaction (connection, transaction);
755
756   _dbus_assert (transaction->connections == NULL);
757
758   dbus_free (transaction);
759 }
760
761 static void
762 bus_connection_remove_transactions (DBusConnection *connection)
763 {
764   MessageToSend *to_send;
765   BusConnectionData *d;
766   
767   d = BUS_CONNECTION_DATA (connection);
768   _dbus_assert (d != NULL);
769   
770   while ((to_send = _dbus_list_get_first (&d->transaction_messages)))
771     {
772       /* only has an effect for the first MessageToSend listing this transaction */
773       _dbus_list_remove (&to_send->transaction->connections,
774                          connection);
775
776       _dbus_list_remove (&d->transaction_messages, to_send);
777       message_to_send_free (connection, to_send);
778     }
779 }
780
781 /**
782  * Converts the DBusError to a message reply
783  */
784 dbus_bool_t
785 bus_transaction_send_error_reply (BusTransaction  *transaction,
786                                   DBusConnection  *connection,
787                                   const DBusError *error,
788                                   DBusMessage     *in_reply_to)
789 {
790   DBusMessage *reply;
791
792   _dbus_assert (error != NULL);
793   _DBUS_ASSERT_ERROR_IS_SET (error);
794   
795   reply = dbus_message_new_error_reply (in_reply_to,
796                                         error->name,
797                                         error->message);
798   if (reply == NULL)
799     return FALSE;
800
801   if (!bus_transaction_send_message (transaction, connection, reply))
802     {
803       dbus_message_unref (reply);
804       return FALSE;
805     }
806
807   return TRUE;
808 }