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