connection.c revision 4c95a9782c65f88e2904c44abeb734a1b00f6353
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
30static void bus_connection_remove_transactions (DBusConnection *connection);
31
32struct BusConnections
33{
34  int refcount;
35  DBusList *list; /**< List of all the connections */
36  BusContext *context;
37};
38
39static int connection_data_slot = -1;
40
41typedef 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
54void
55bus_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
135static void
136connection_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
151static dbus_bool_t
152add_connection_watch (DBusWatch      *watch,
153                      DBusConnection *connection)
154{
155  return bus_loop_add_watch (watch, connection_watch_callback, connection,
156                             NULL);
157}
158
159static void
160remove_connection_watch (DBusWatch      *watch,
161                         DBusConnection *connection)
162{
163  bus_loop_remove_watch (watch, connection_watch_callback, connection);
164}
165
166static void
167connection_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
181static dbus_bool_t
182add_connection_timeout (DBusTimeout    *timeout,
183                        DBusConnection *connection)
184{
185  return bus_loop_add_timeout (timeout, connection_timeout_callback, connection, NULL);
186}
187
188static void
189remove_connection_timeout (DBusTimeout    *timeout,
190                           DBusConnection *connection)
191{
192  bus_loop_remove_timeout (timeout, connection_timeout_callback, connection);
193}
194
195static void
196free_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
215BusConnections*
216bus_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
238void
239bus_connections_ref (BusConnections *connections)
240{
241  _dbus_assert (connections->refcount > 0);
242  connections->refcount += 1;
243}
244
245void
246bus_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
261dbus_bool_t
262bus_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 */
331void
332bus_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
351BusContext*
352bus_connections_get_context (BusConnections *connections)
353{
354  return connections->context;
355}
356
357BusContext*
358bus_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
369BusConnections*
370bus_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
381BusRegistry*
382bus_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
393BusActivation*
394bus_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 */
411dbus_bool_t
412bus_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
421dbus_bool_t
422bus_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
463void
464bus_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
487dbus_bool_t
488bus_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
503void
504bus_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
515dbus_bool_t
516bus_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
536const char *
537bus_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
547typedef struct
548{
549  BusTransaction *transaction;
550  DBusMessage    *message;
551  DBusPreallocatedSend *preallocated;
552} MessageToSend;
553
554struct BusTransaction
555{
556  DBusList *connections;
557  BusContext *context;
558};
559
560static void
561message_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
573BusTransaction*
574bus_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
587BusContext*
588bus_transaction_get_context (BusTransaction  *transaction)
589{
590  return transaction->context;
591}
592
593BusConnections*
594bus_transaction_get_connections (BusTransaction  *transaction)
595{
596  return bus_context_get_connections (transaction->context);
597}
598
599dbus_bool_t
600bus_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
668static void
669connection_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
696void
697bus_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
709static void
710connection_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
745void
746bus_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
761static void
762bus_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 */
784dbus_bool_t
785bus_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}
809