services.c revision 43605a6f4e78a8c28afb4b1e924dff0301e0e95c
1/* -*- mode: C; c-file-style: "gnu" -*- */ 2/* services.c Service management 3 * 4 * Copyright (C) 2003 Red Hat, Inc. 5 * Copyright (C) 2003 CodeFactory AB 6 * 7 * Licensed under the Academic Free License version 2.1 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 * 23 */ 24#include <dbus/dbus-hash.h> 25#include <dbus/dbus-list.h> 26#include <dbus/dbus-mempool.h> 27 28#include "driver.h" 29#include "services.h" 30#include "connection.h" 31#include "utils.h" 32#include "activation.h" 33#include "policy.h" 34#include "bus.h" 35#include "selinux.h" 36 37struct BusService 38{ 39 int refcount; 40 41 BusRegistry *registry; 42 char *name; 43 DBusList *owners; 44 45 unsigned int prohibit_replacement : 1; 46}; 47 48struct BusRegistry 49{ 50 int refcount; 51 52 BusContext *context; 53 54 DBusHashTable *service_hash; 55 DBusMemPool *service_pool; 56 57 DBusHashTable *service_sid_table; 58}; 59 60BusRegistry* 61bus_registry_new (BusContext *context) 62{ 63 BusRegistry *registry; 64 65 registry = dbus_new0 (BusRegistry, 1); 66 if (registry == NULL) 67 return NULL; 68 69 registry->refcount = 1; 70 registry->context = context; 71 72 registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING, 73 NULL, NULL); 74 if (registry->service_hash == NULL) 75 goto failed; 76 77 registry->service_pool = _dbus_mem_pool_new (sizeof (BusService), 78 TRUE); 79 if (registry->service_pool == NULL) 80 goto failed; 81 82 registry->service_sid_table = NULL; 83 84 return registry; 85 86 failed: 87 bus_registry_unref (registry); 88 return NULL; 89} 90 91BusRegistry * 92bus_registry_ref (BusRegistry *registry) 93{ 94 _dbus_assert (registry->refcount > 0); 95 registry->refcount += 1; 96 97 return registry; 98} 99 100void 101bus_registry_unref (BusRegistry *registry) 102{ 103 _dbus_assert (registry->refcount > 0); 104 registry->refcount -= 1; 105 106 if (registry->refcount == 0) 107 { 108 if (registry->service_hash) 109 _dbus_hash_table_unref (registry->service_hash); 110 if (registry->service_pool) 111 _dbus_mem_pool_free (registry->service_pool); 112 if (registry->service_sid_table) 113 _dbus_hash_table_unref (registry->service_sid_table); 114 115 dbus_free (registry); 116 } 117} 118 119BusService* 120bus_registry_lookup (BusRegistry *registry, 121 const DBusString *service_name) 122{ 123 BusService *service; 124 125 service = _dbus_hash_table_lookup_string (registry->service_hash, 126 _dbus_string_get_const_data (service_name)); 127 128 return service; 129} 130 131BusService* 132bus_registry_ensure (BusRegistry *registry, 133 const DBusString *service_name, 134 DBusConnection *owner_if_created, 135 BusTransaction *transaction, 136 DBusError *error) 137{ 138 BusService *service; 139 140 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 141 142 _dbus_assert (owner_if_created != NULL); 143 _dbus_assert (transaction != NULL); 144 145 service = _dbus_hash_table_lookup_string (registry->service_hash, 146 _dbus_string_get_const_data (service_name)); 147 if (service != NULL) 148 return service; 149 150 service = _dbus_mem_pool_alloc (registry->service_pool); 151 if (service == NULL) 152 { 153 BUS_SET_OOM (error); 154 return NULL; 155 } 156 157 service->registry = registry; 158 service->refcount = 1; 159 160 if (!_dbus_string_copy_data (service_name, &service->name)) 161 { 162 _dbus_mem_pool_dealloc (registry->service_pool, service); 163 BUS_SET_OOM (error); 164 return NULL; 165 } 166 167 if (!bus_driver_send_service_created (service->name, transaction, error)) 168 { 169 bus_service_unref (service); 170 return NULL; 171 } 172 173 if (!bus_activation_service_created (bus_context_get_activation (registry->context), 174 service->name, transaction, error)) 175 { 176 bus_service_unref (service); 177 return NULL; 178 } 179 180 if (!bus_service_add_owner (service, owner_if_created, 181 transaction, error)) 182 { 183 bus_service_unref (service); 184 return NULL; 185 } 186 187 if (!_dbus_hash_table_insert_string (registry->service_hash, 188 service->name, 189 service)) 190 { 191 /* The add_owner gets reverted on transaction cancel */ 192 BUS_SET_OOM (error); 193 return NULL; 194 } 195 196 return service; 197} 198 199void 200bus_registry_foreach (BusRegistry *registry, 201 BusServiceForeachFunction function, 202 void *data) 203{ 204 DBusHashIter iter; 205 206 _dbus_hash_iter_init (registry->service_hash, &iter); 207 while (_dbus_hash_iter_next (&iter)) 208 { 209 BusService *service = _dbus_hash_iter_get_value (&iter); 210 211 (* function) (service, data); 212 } 213} 214 215dbus_bool_t 216bus_registry_list_services (BusRegistry *registry, 217 char ***listp, 218 int *array_len) 219{ 220 int i, j, len; 221 char **retval; 222 DBusHashIter iter; 223 224 len = _dbus_hash_table_get_n_entries (registry->service_hash); 225 retval = dbus_new (char *, len + 1); 226 227 if (retval == NULL) 228 return FALSE; 229 230 _dbus_hash_iter_init (registry->service_hash, &iter); 231 i = 0; 232 while (_dbus_hash_iter_next (&iter)) 233 { 234 BusService *service = _dbus_hash_iter_get_value (&iter); 235 236 retval[i] = _dbus_strdup (service->name); 237 if (retval[i] == NULL) 238 goto error; 239 240 i++; 241 } 242 243 retval[i] = NULL; 244 245 if (array_len) 246 *array_len = len; 247 248 *listp = retval; 249 return TRUE; 250 251 error: 252 for (j = 0; j < i; j++) 253 dbus_free (retval[i]); 254 dbus_free (retval); 255 256 return FALSE; 257} 258 259dbus_bool_t 260bus_registry_acquire_service (BusRegistry *registry, 261 DBusConnection *connection, 262 const DBusString *service_name, 263 dbus_uint32_t flags, 264 dbus_uint32_t *result, 265 BusTransaction *transaction, 266 DBusError *error) 267{ 268 dbus_bool_t retval; 269 DBusConnection *old_owner; 270 DBusConnection *current_owner; 271 BusClientPolicy *policy; 272 BusService *service; 273 BusActivation *activation; 274 BusSELinuxID *sid; 275 276 retval = FALSE; 277 278 if (_dbus_string_get_length (service_name) == 0) 279 { 280 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, 281 "Zero-length service name is not allowed"); 282 283 _dbus_verbose ("Attempt to acquire zero-length service name\n"); 284 285 goto out; 286 } 287 288 if (_dbus_string_get_byte (service_name, 0) == ':') 289 { 290 /* Not allowed; only base services can start with ':' */ 291 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, 292 "Cannot acquire a service starting with ':' such as \"%s\"", 293 _dbus_string_get_const_data (service_name)); 294 295 _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"", 296 _dbus_string_get_const_data (service_name)); 297 298 goto out; 299 } 300 301 policy = bus_connection_get_policy (connection); 302 _dbus_assert (policy != NULL); 303 304 /* Note that if sid is #NULL then the bus's own context gets used 305 * in bus_connection_selinux_allows_acquire_service() 306 */ 307 sid = bus_selinux_id_table_lookup (registry->service_sid_table, 308 service_name); 309 310 if (!bus_selinux_allows_acquire_service (connection, sid)) 311 { 312 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, 313 "Connection \"%s\" is not allowed to own the service \"%s\" due " 314 "to SELinux policy", 315 bus_connection_is_active (connection) ? 316 bus_connection_get_name (connection) : 317 "(inactive)", 318 service_name); 319 goto out; 320 } 321 322 if (!bus_client_policy_check_can_own (policy, connection, 323 service_name)) 324 { 325 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, 326 "Connection \"%s\" is not allowed to own the service \"%s\" due " 327 "to security policies in the configuration file", 328 bus_connection_is_active (connection) ? 329 bus_connection_get_name (connection) : 330 "(inactive)", 331 service_name); 332 goto out; 333 } 334 335 if (bus_connection_get_n_services_owned (connection) >= 336 bus_context_get_max_services_per_connection (registry->context)) 337 { 338 dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, 339 "Connection \"%s\" is not allowed to own more services " 340 "(increase limits in configuration file if required)", 341 bus_connection_is_active (connection) ? 342 bus_connection_get_name (connection) : 343 "(inactive)"); 344 goto out; 345 } 346 347 service = bus_registry_lookup (registry, service_name); 348 349 if (service != NULL) 350 old_owner = bus_service_get_primary_owner (service); 351 else 352 old_owner = NULL; 353 354 if (service == NULL) 355 { 356 service = bus_registry_ensure (registry, 357 service_name, connection, transaction, error); 358 if (service == NULL) 359 goto out; 360 } 361 362 current_owner = bus_service_get_primary_owner (service); 363 364 if (old_owner == NULL) 365 { 366 _dbus_assert (current_owner == connection); 367 368 bus_service_set_prohibit_replacement (service, 369 (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT)); 370 371 *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER; 372 } 373 else if (old_owner == connection) 374 *result = DBUS_SERVICE_REPLY_ALREADY_OWNER; 375 else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING))) 376 *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS; 377 else if (bus_service_get_prohibit_replacement (service)) 378 { 379 /* Queue the connection */ 380 if (!bus_service_add_owner (service, connection, 381 transaction, error)) 382 goto out; 383 384 *result = DBUS_SERVICE_REPLY_IN_QUEUE; 385 } 386 else 387 { 388 /* Replace the current owner */ 389 390 /* We enqueue the new owner and remove the first one because 391 * that will cause ServiceAcquired and ServiceLost messages to 392 * be sent. 393 */ 394 395 if (!bus_service_add_owner (service, connection, 396 transaction, error)) 397 goto out; 398 399 if (!bus_service_remove_owner (service, old_owner, 400 transaction, error)) 401 goto out; 402 403 _dbus_assert (connection == bus_service_get_primary_owner (service)); 404 *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER; 405 } 406 407 activation = bus_context_get_activation (registry->context); 408 retval = bus_activation_send_pending_auto_activation_messages (activation, 409 service, 410 transaction, 411 error); 412 413 out: 414 return retval; 415} 416 417void 418bus_registry_set_service_sid_table (BusRegistry *registry, 419 DBusHashTable *table) 420{ 421 _dbus_assert (registry->service_sid_table != table); 422 423 if (registry->service_sid_table) 424 _dbus_hash_table_unref (registry->service_sid_table); 425 426 registry->service_sid_table = table; 427 _dbus_hash_table_ref (table); 428} 429 430static void 431bus_service_unlink_owner (BusService *service, 432 DBusConnection *owner) 433{ 434 _dbus_list_remove_last (&service->owners, owner); 435 bus_connection_remove_owned_service (owner, service); 436} 437 438static void 439bus_service_unlink (BusService *service) 440{ 441 _dbus_assert (service->owners == NULL); 442 443 /* the service may not be in the hash, if 444 * the failure causing transaction cancel 445 * was in the right place, but that's OK 446 */ 447 _dbus_hash_table_remove_string (service->registry->service_hash, 448 service->name); 449 450 bus_service_unref (service); 451} 452 453static void 454bus_service_relink (BusService *service, 455 DBusPreallocatedHash *preallocated) 456{ 457 _dbus_assert (service->owners == NULL); 458 _dbus_assert (preallocated != NULL); 459 460 _dbus_hash_table_insert_string_preallocated (service->registry->service_hash, 461 preallocated, 462 service->name, 463 service); 464 465 bus_service_ref (service); 466} 467 468/** 469 * Data used to represent an ownership cancellation in 470 * a bus transaction. 471 */ 472typedef struct 473{ 474 DBusConnection *connection; /**< the connection */ 475 BusService *service; /**< service to cancel ownership of */ 476} OwnershipCancelData; 477 478static void 479cancel_ownership (void *data) 480{ 481 OwnershipCancelData *d = data; 482 483 /* We don't need to send messages notifying of these 484 * changes, since we're reverting something that was 485 * cancelled (effectively never really happened) 486 */ 487 bus_service_unlink_owner (d->service, d->connection); 488 489 if (d->service->owners == NULL) 490 bus_service_unlink (d->service); 491} 492 493static void 494free_ownership_cancel_data (void *data) 495{ 496 OwnershipCancelData *d = data; 497 498 dbus_connection_unref (d->connection); 499 bus_service_unref (d->service); 500 501 dbus_free (d); 502} 503 504static dbus_bool_t 505add_cancel_ownership_to_transaction (BusTransaction *transaction, 506 BusService *service, 507 DBusConnection *connection) 508{ 509 OwnershipCancelData *d; 510 511 d = dbus_new (OwnershipCancelData, 1); 512 if (d == NULL) 513 return FALSE; 514 515 d->service = service; 516 d->connection = connection; 517 518 if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d, 519 free_ownership_cancel_data)) 520 { 521 dbus_free (d); 522 return FALSE; 523 } 524 525 bus_service_ref (d->service); 526 dbus_connection_ref (d->connection); 527 528 return TRUE; 529} 530 531/* this function is self-cancelling if you cancel the transaction */ 532dbus_bool_t 533bus_service_add_owner (BusService *service, 534 DBusConnection *owner, 535 BusTransaction *transaction, 536 DBusError *error) 537{ 538 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 539 540 /* Send service acquired message first, OOM will result 541 * in cancelling the transaction 542 */ 543 if (service->owners == NULL) 544 { 545 if (!bus_driver_send_service_acquired (owner, service->name, transaction, error)) 546 return FALSE; 547 } 548 549 if (!_dbus_list_append (&service->owners, 550 owner)) 551 { 552 BUS_SET_OOM (error); 553 return FALSE; 554 } 555 556 if (!bus_connection_add_owned_service (owner, service)) 557 { 558 _dbus_list_remove_last (&service->owners, owner); 559 BUS_SET_OOM (error); 560 return FALSE; 561 } 562 563 if (!add_cancel_ownership_to_transaction (transaction, 564 service, 565 owner)) 566 { 567 bus_service_unlink_owner (service, owner); 568 BUS_SET_OOM (error); 569 return FALSE; 570 } 571 572 return TRUE; 573} 574 575typedef struct 576{ 577 DBusConnection *connection; 578 BusService *service; 579 DBusConnection *before_connection; /* restore to position before this connection in owners list */ 580 DBusList *connection_link; 581 DBusList *service_link; 582 DBusPreallocatedHash *hash_entry; 583} OwnershipRestoreData; 584 585static void 586restore_ownership (void *data) 587{ 588 OwnershipRestoreData *d = data; 589 DBusList *link; 590 591 _dbus_assert (d->service_link != NULL); 592 _dbus_assert (d->connection_link != NULL); 593 594 if (d->service->owners == NULL) 595 { 596 _dbus_assert (d->hash_entry != NULL); 597 bus_service_relink (d->service, d->hash_entry); 598 } 599 else 600 { 601 _dbus_assert (d->hash_entry == NULL); 602 } 603 604 /* We don't need to send messages notifying of these 605 * changes, since we're reverting something that was 606 * cancelled (effectively never really happened) 607 */ 608 link = _dbus_list_get_first_link (&d->service->owners); 609 while (link != NULL) 610 { 611 if (link->data == d->before_connection) 612 break; 613 614 link = _dbus_list_get_next_link (&d->service->owners, link); 615 } 616 617 _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link); 618 619 /* Note that removing then restoring this changes the order in which 620 * ServiceDeleted messages are sent on destruction of the 621 * connection. This should be OK as the only guarantee there is 622 * that the base service is destroyed last, and we never even 623 * tentatively remove the base service. 624 */ 625 bus_connection_add_owned_service_link (d->connection, d->service_link); 626 627 d->hash_entry = NULL; 628 d->service_link = NULL; 629 d->connection_link = NULL; 630} 631 632static void 633free_ownership_restore_data (void *data) 634{ 635 OwnershipRestoreData *d = data; 636 637 if (d->service_link) 638 _dbus_list_free_link (d->service_link); 639 if (d->connection_link) 640 _dbus_list_free_link (d->connection_link); 641 if (d->hash_entry) 642 _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash, 643 d->hash_entry); 644 645 dbus_connection_unref (d->connection); 646 bus_service_unref (d->service); 647 648 dbus_free (d); 649} 650 651static dbus_bool_t 652add_restore_ownership_to_transaction (BusTransaction *transaction, 653 BusService *service, 654 DBusConnection *connection) 655{ 656 OwnershipRestoreData *d; 657 DBusList *link; 658 659 d = dbus_new (OwnershipRestoreData, 1); 660 if (d == NULL) 661 return FALSE; 662 663 d->service = service; 664 d->connection = connection; 665 d->service_link = _dbus_list_alloc_link (service); 666 d->connection_link = _dbus_list_alloc_link (connection); 667 d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash); 668 669 bus_service_ref (d->service); 670 dbus_connection_ref (d->connection); 671 672 d->before_connection = NULL; 673 link = _dbus_list_get_first_link (&service->owners); 674 while (link != NULL) 675 { 676 if (link->data == connection) 677 { 678 link = _dbus_list_get_next_link (&service->owners, link); 679 680 if (link) 681 d->before_connection = link->data; 682 683 break; 684 } 685 686 link = _dbus_list_get_next_link (&service->owners, link); 687 } 688 689 if (d->service_link == NULL || 690 d->connection_link == NULL || 691 d->hash_entry == NULL || 692 !bus_transaction_add_cancel_hook (transaction, restore_ownership, d, 693 free_ownership_restore_data)) 694 { 695 free_ownership_restore_data (d); 696 return FALSE; 697 } 698 699 return TRUE; 700} 701 702/* this function is self-cancelling if you cancel the transaction */ 703dbus_bool_t 704bus_service_remove_owner (BusService *service, 705 DBusConnection *owner, 706 BusTransaction *transaction, 707 DBusError *error) 708{ 709 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 710 711 /* We send out notifications before we do any work we 712 * might have to undo if the notification-sending failed 713 */ 714 715 /* Send service lost message */ 716 if (bus_service_get_primary_owner (service) == owner) 717 { 718 if (!bus_driver_send_service_lost (owner, service->name, 719 transaction, error)) 720 return FALSE; 721 } 722 723 if (service->owners == NULL) 724 { 725 _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners"); 726 } 727 else if (_dbus_list_length_is_one (&service->owners)) 728 { 729 if (!bus_driver_send_service_deleted (service->name, 730 transaction, error)) 731 return FALSE; 732 } 733 else 734 { 735 DBusList *link; 736 link = _dbus_list_get_first_link (&service->owners); 737 _dbus_assert (link != NULL); 738 link = _dbus_list_get_next_link (&service->owners, link); 739 _dbus_assert (link != NULL); 740 741 /* This will be our new owner */ 742 if (!bus_driver_send_service_acquired (link->data, 743 service->name, 744 transaction, 745 error)) 746 return FALSE; 747 } 748 749 if (!add_restore_ownership_to_transaction (transaction, service, owner)) 750 { 751 BUS_SET_OOM (error); 752 return FALSE; 753 } 754 755 bus_service_unlink_owner (service, owner); 756 757 if (service->owners == NULL) 758 bus_service_unlink (service); 759 760 return TRUE; 761} 762 763BusService * 764bus_service_ref (BusService *service) 765{ 766 _dbus_assert (service->refcount > 0); 767 768 service->refcount += 1; 769 770 return service; 771} 772 773void 774bus_service_unref (BusService *service) 775{ 776 _dbus_assert (service->refcount > 0); 777 778 service->refcount -= 1; 779 780 if (service->refcount == 0) 781 { 782 _dbus_assert (service->owners == NULL); 783 784 dbus_free (service->name); 785 _dbus_mem_pool_dealloc (service->registry->service_pool, service); 786 } 787} 788 789DBusConnection* 790bus_service_get_primary_owner (BusService *service) 791{ 792 return _dbus_list_get_first (&service->owners); 793} 794 795const char* 796bus_service_get_name (BusService *service) 797{ 798 return service->name; 799} 800 801void 802bus_service_set_prohibit_replacement (BusService *service, 803 dbus_bool_t prohibit_replacement) 804{ 805 service->prohibit_replacement = prohibit_replacement != FALSE; 806} 807 808dbus_bool_t 809bus_service_get_prohibit_replacement (BusService *service) 810{ 811 return service->prohibit_replacement; 812} 813 814dbus_bool_t 815bus_service_has_owner (BusService *service, 816 DBusConnection *owner) 817{ 818 DBusList *link; 819 820 link = _dbus_list_get_first_link (&service->owners); 821 822 while (link != NULL) 823 { 824 if (link->data == owner) 825 return TRUE; 826 827 link = _dbus_list_get_next_link (&service->owners, link); 828 } 829 830 return FALSE; 831} 832