00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <dbus/dbus-hash.h>
00025 #include <dbus/dbus-list.h>
00026 #include <dbus/dbus-mempool.h>
00027
00028 #include "driver.h"
00029 #include "services.h"
00030 #include "connection.h"
00031 #include "utils.h"
00032 #include "activation.h"
00033 #include "policy.h"
00034
00035 struct BusService
00036 {
00037 int refcount;
00038
00039 BusRegistry *registry;
00040 char *name;
00041 DBusList *owners;
00042
00043 unsigned int prohibit_replacement : 1;
00044 };
00045
00046 struct BusRegistry
00047 {
00048 int refcount;
00049
00050 BusContext *context;
00051
00052 DBusHashTable *service_hash;
00053 DBusMemPool *service_pool;
00054 };
00055
00056 BusRegistry*
00057 bus_registry_new (BusContext *context)
00058 {
00059 BusRegistry *registry;
00060
00061 registry = dbus_new0 (BusRegistry, 1);
00062 if (registry == NULL)
00063 return NULL;
00064
00065 registry->refcount = 1;
00066 registry->context = context;
00067
00068 registry->service_hash = _dbus_hash_table_new (DBUS_HASH_STRING,
00069 NULL, NULL);
00070 if (registry->service_hash == NULL)
00071 goto failed;
00072
00073 registry->service_pool = _dbus_mem_pool_new (sizeof (BusService),
00074 TRUE);
00075 if (registry->service_pool == NULL)
00076 goto failed;
00077
00078 return registry;
00079
00080 failed:
00081 bus_registry_unref (registry);
00082 return NULL;
00083 }
00084
00085 void
00086 bus_registry_ref (BusRegistry *registry)
00087 {
00088 _dbus_assert (registry->refcount > 0);
00089 registry->refcount += 1;
00090 }
00091
00092 void
00093 bus_registry_unref (BusRegistry *registry)
00094 {
00095 _dbus_assert (registry->refcount > 0);
00096 registry->refcount -= 1;
00097
00098 if (registry->refcount == 0)
00099 {
00100 if (registry->service_hash)
00101 _dbus_hash_table_unref (registry->service_hash);
00102 if (registry->service_pool)
00103 _dbus_mem_pool_free (registry->service_pool);
00104
00105 dbus_free (registry);
00106 }
00107 }
00108
00109 BusService*
00110 bus_registry_lookup (BusRegistry *registry,
00111 const DBusString *service_name)
00112 {
00113 BusService *service;
00114
00115 service = _dbus_hash_table_lookup_string (registry->service_hash,
00116 _dbus_string_get_const_data (service_name));
00117
00118 return service;
00119 }
00120
00121 BusService*
00122 bus_registry_ensure (BusRegistry *registry,
00123 const DBusString *service_name,
00124 DBusConnection *owner_if_created,
00125 BusTransaction *transaction,
00126 DBusError *error)
00127 {
00128 BusService *service;
00129
00130 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00131
00132 _dbus_assert (owner_if_created != NULL);
00133 _dbus_assert (transaction != NULL);
00134
00135 service = _dbus_hash_table_lookup_string (registry->service_hash,
00136 _dbus_string_get_const_data (service_name));
00137 if (service != NULL)
00138 return service;
00139
00140 service = _dbus_mem_pool_alloc (registry->service_pool);
00141 if (service == NULL)
00142 {
00143 BUS_SET_OOM (error);
00144 return NULL;
00145 }
00146
00147 service->registry = registry;
00148 service->refcount = 1;
00149
00150 if (!_dbus_string_copy_data (service_name, &service->name))
00151 {
00152 _dbus_mem_pool_dealloc (registry->service_pool, service);
00153 BUS_SET_OOM (error);
00154 return NULL;
00155 }
00156
00157 if (!bus_driver_send_service_created (service->name, transaction, error))
00158 {
00159 bus_service_unref (service);
00160 return NULL;
00161 }
00162
00163 if (!bus_activation_service_created (bus_context_get_activation (registry->context),
00164 service->name, transaction, error))
00165 {
00166 bus_service_unref (service);
00167 return NULL;
00168 }
00169
00170 if (!bus_service_add_owner (service, owner_if_created,
00171 transaction, error))
00172 {
00173 bus_service_unref (service);
00174 return NULL;
00175 }
00176
00177 if (!_dbus_hash_table_insert_string (registry->service_hash,
00178 service->name,
00179 service))
00180 {
00181
00182 BUS_SET_OOM (error);
00183 return NULL;
00184 }
00185
00186 return service;
00187 }
00188
00189 void
00190 bus_registry_foreach (BusRegistry *registry,
00191 BusServiceForeachFunction function,
00192 void *data)
00193 {
00194 DBusHashIter iter;
00195
00196 _dbus_hash_iter_init (registry->service_hash, &iter);
00197 while (_dbus_hash_iter_next (&iter))
00198 {
00199 BusService *service = _dbus_hash_iter_get_value (&iter);
00200
00201 (* function) (service, data);
00202 }
00203 }
00204
00205 dbus_bool_t
00206 bus_registry_list_services (BusRegistry *registry,
00207 char ***listp,
00208 int *array_len)
00209 {
00210 int i, j, len;
00211 char **retval;
00212 DBusHashIter iter;
00213
00214 len = _dbus_hash_table_get_n_entries (registry->service_hash);
00215 retval = dbus_new (char *, len + 1);
00216
00217 if (retval == NULL)
00218 return FALSE;
00219
00220 _dbus_hash_iter_init (registry->service_hash, &iter);
00221 i = 0;
00222 while (_dbus_hash_iter_next (&iter))
00223 {
00224 BusService *service = _dbus_hash_iter_get_value (&iter);
00225
00226 retval[i] = _dbus_strdup (service->name);
00227 if (retval[i] == NULL)
00228 goto error;
00229
00230 i++;
00231 }
00232
00233 retval[i] = NULL;
00234
00235 if (array_len)
00236 *array_len = len;
00237
00238 *listp = retval;
00239 return TRUE;
00240
00241 error:
00242 for (j = 0; j < i; j++)
00243 dbus_free (retval[i]);
00244 dbus_free (retval);
00245
00246 return FALSE;
00247 }
00248
00249 dbus_bool_t
00250 bus_registry_acquire_service (BusRegistry *registry,
00251 DBusConnection *connection,
00252 const DBusString *service_name,
00253 dbus_uint32_t flags,
00254 dbus_uint32_t *result,
00255 BusTransaction *transaction,
00256 DBusError *error)
00257 {
00258 dbus_bool_t retval;
00259 DBusConnection *old_owner;
00260 DBusConnection *current_owner;
00261 BusClientPolicy *policy;
00262 BusService *service;
00263
00264 retval = FALSE;
00265
00266 if (_dbus_string_get_length (service_name) == 0)
00267 {
00268 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
00269 "Zero-length service name is not allowed");
00270
00271 _dbus_verbose ("Attempt to acquire zero-length service name\n");
00272
00273 goto out;
00274 }
00275
00276 if (_dbus_string_get_byte (service_name, 0) == ':')
00277 {
00278
00279 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
00280 "Cannot acquire a service starting with ':' such as \"%s\"",
00281 _dbus_string_get_const_data (service_name));
00282
00283 _dbus_verbose ("Attempt to acquire invalid base service name \"%s\"",
00284 _dbus_string_get_const_data (service_name));
00285
00286 goto out;
00287 }
00288
00289 policy = bus_connection_get_policy (connection);
00290 _dbus_assert (policy != NULL);
00291
00292 if (!bus_client_policy_check_can_own (policy, connection,
00293 service_name))
00294 {
00295 dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
00296 "Connection \"%s\" is not allowed to own the service \"%s\" due "
00297 "to security policies in the configuration file",
00298 bus_connection_is_active (connection) ?
00299 bus_connection_get_name (connection) :
00300 "(inactive)");
00301 goto out;
00302 }
00303
00304 if (bus_connection_get_n_services_owned (connection) >=
00305 bus_context_get_max_services_per_connection (registry->context))
00306 {
00307 dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
00308 "Connection \"%s\" is not allowed to own more services "
00309 "(increase limits in configuration file if required)",
00310 bus_connection_is_active (connection) ?
00311 bus_connection_get_name (connection) :
00312 "(inactive)");
00313 goto out;
00314 }
00315
00316 service = bus_registry_lookup (registry, service_name);
00317
00318 if (service != NULL)
00319 old_owner = bus_service_get_primary_owner (service);
00320 else
00321 old_owner = NULL;
00322
00323 if (service == NULL)
00324 {
00325 service = bus_registry_ensure (registry,
00326 service_name, connection, transaction, error);
00327 if (service == NULL)
00328 goto out;
00329 }
00330
00331 current_owner = bus_service_get_primary_owner (service);
00332
00333 if (old_owner == NULL)
00334 {
00335 _dbus_assert (current_owner == connection);
00336
00337 bus_service_set_prohibit_replacement (service,
00338 (flags & DBUS_SERVICE_FLAG_PROHIBIT_REPLACEMENT));
00339
00340 *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
00341 }
00342 else if (old_owner == connection)
00343 *result = DBUS_SERVICE_REPLY_ALREADY_OWNER;
00344 else if (!((flags & DBUS_SERVICE_FLAG_REPLACE_EXISTING)))
00345 *result = DBUS_SERVICE_REPLY_SERVICE_EXISTS;
00346 else if (bus_service_get_prohibit_replacement (service))
00347 {
00348
00349 if (!bus_service_add_owner (service, connection,
00350 transaction, error))
00351 goto out;
00352
00353 *result = DBUS_SERVICE_REPLY_IN_QUEUE;
00354 }
00355 else
00356 {
00357
00358
00359
00360
00361
00362
00363
00364 if (!bus_service_add_owner (service, connection,
00365 transaction, error))
00366 goto out;
00367
00368 if (!bus_service_remove_owner (service, old_owner,
00369 transaction, error))
00370 goto out;
00371
00372 _dbus_assert (connection == bus_service_get_primary_owner (service));
00373 *result = DBUS_SERVICE_REPLY_PRIMARY_OWNER;
00374 }
00375
00376 retval = TRUE;
00377
00378 out:
00379 return retval;
00380 }
00381
00382 static void
00383 bus_service_unlink_owner (BusService *service,
00384 DBusConnection *owner)
00385 {
00386 _dbus_list_remove_last (&service->owners, owner);
00387 bus_connection_remove_owned_service (owner, service);
00388 }
00389
00390 static void
00391 bus_service_unlink (BusService *service)
00392 {
00393 _dbus_assert (service->owners == NULL);
00394
00395
00396
00397
00398
00399 _dbus_hash_table_remove_string (service->registry->service_hash,
00400 service->name);
00401
00402 bus_service_unref (service);
00403 }
00404
00405 static void
00406 bus_service_relink (BusService *service,
00407 DBusPreallocatedHash *preallocated)
00408 {
00409 _dbus_assert (service->owners == NULL);
00410 _dbus_assert (preallocated != NULL);
00411
00412 _dbus_hash_table_insert_string_preallocated (service->registry->service_hash,
00413 preallocated,
00414 service->name,
00415 service);
00416
00417 bus_service_ref (service);
00418 }
00419
00420 typedef struct
00421 {
00422 DBusConnection *connection;
00423 BusService *service;
00424 } OwnershipCancelData;
00425
00426 static void
00427 cancel_ownership (void *data)
00428 {
00429 OwnershipCancelData *d = data;
00430
00431
00432
00433
00434
00435 bus_service_unlink_owner (d->service, d->connection);
00436
00437 if (d->service->owners == NULL)
00438 bus_service_unlink (d->service);
00439 }
00440
00441 static void
00442 free_ownership_cancel_data (void *data)
00443 {
00444 OwnershipCancelData *d = data;
00445
00446 dbus_connection_unref (d->connection);
00447 bus_service_unref (d->service);
00448
00449 dbus_free (d);
00450 }
00451
00452 static dbus_bool_t
00453 add_cancel_ownership_to_transaction (BusTransaction *transaction,
00454 BusService *service,
00455 DBusConnection *connection)
00456 {
00457 OwnershipCancelData *d;
00458
00459 d = dbus_new (OwnershipCancelData, 1);
00460 if (d == NULL)
00461 return FALSE;
00462
00463 d->service = service;
00464 d->connection = connection;
00465
00466 if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
00467 free_ownership_cancel_data))
00468 {
00469 dbus_free (d);
00470 return FALSE;
00471 }
00472
00473 bus_service_ref (d->service);
00474 dbus_connection_ref (d->connection);
00475
00476 return TRUE;
00477 }
00478
00479
00480 dbus_bool_t
00481 bus_service_add_owner (BusService *service,
00482 DBusConnection *owner,
00483 BusTransaction *transaction,
00484 DBusError *error)
00485 {
00486 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00487
00488
00489
00490
00491 if (service->owners == NULL)
00492 {
00493 if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
00494 return FALSE;
00495 }
00496
00497 if (!_dbus_list_append (&service->owners,
00498 owner))
00499 {
00500 BUS_SET_OOM (error);
00501 return FALSE;
00502 }
00503
00504 if (!bus_connection_add_owned_service (owner, service))
00505 {
00506 _dbus_list_remove_last (&service->owners, owner);
00507 BUS_SET_OOM (error);
00508 return FALSE;
00509 }
00510
00511 if (!add_cancel_ownership_to_transaction (transaction,
00512 service,
00513 owner))
00514 {
00515 bus_service_unlink_owner (service, owner);
00516 BUS_SET_OOM (error);
00517 return FALSE;
00518 }
00519
00520 return TRUE;
00521 }
00522
00523 typedef struct
00524 {
00525 DBusConnection *connection;
00526 BusService *service;
00527 DBusConnection *before_connection;
00528 DBusList *connection_link;
00529 DBusList *service_link;
00530 DBusPreallocatedHash *hash_entry;
00531 } OwnershipRestoreData;
00532
00533 static void
00534 restore_ownership (void *data)
00535 {
00536 OwnershipRestoreData *d = data;
00537 DBusList *link;
00538
00539 _dbus_assert (d->service_link != NULL);
00540 _dbus_assert (d->connection_link != NULL);
00541
00542 if (d->service->owners == NULL)
00543 {
00544 _dbus_assert (d->hash_entry != NULL);
00545 bus_service_relink (d->service, d->hash_entry);
00546 }
00547 else
00548 {
00549 _dbus_assert (d->hash_entry == NULL);
00550 }
00551
00552
00553
00554
00555
00556 link = _dbus_list_get_first_link (&d->service->owners);
00557 while (link != NULL)
00558 {
00559 if (link->data == d->before_connection)
00560 break;
00561
00562 link = _dbus_list_get_next_link (&d->service->owners, link);
00563 }
00564
00565 _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
00566
00567
00568
00569
00570
00571
00572
00573 bus_connection_add_owned_service_link (d->connection, d->service_link);
00574
00575 d->hash_entry = NULL;
00576 d->service_link = NULL;
00577 d->connection_link = NULL;
00578 }
00579
00580 static void
00581 free_ownership_restore_data (void *data)
00582 {
00583 OwnershipRestoreData *d = data;
00584
00585 if (d->service_link)
00586 _dbus_list_free_link (d->service_link);
00587 if (d->connection_link)
00588 _dbus_list_free_link (d->connection_link);
00589 if (d->hash_entry)
00590 _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
00591 d->hash_entry);
00592
00593 dbus_connection_unref (d->connection);
00594 bus_service_unref (d->service);
00595
00596 dbus_free (d);
00597 }
00598
00599 static dbus_bool_t
00600 add_restore_ownership_to_transaction (BusTransaction *transaction,
00601 BusService *service,
00602 DBusConnection *connection)
00603 {
00604 OwnershipRestoreData *d;
00605 DBusList *link;
00606
00607 d = dbus_new (OwnershipRestoreData, 1);
00608 if (d == NULL)
00609 return FALSE;
00610
00611 d->service = service;
00612 d->connection = connection;
00613 d->service_link = _dbus_list_alloc_link (service);
00614 d->connection_link = _dbus_list_alloc_link (connection);
00615 d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
00616
00617 bus_service_ref (d->service);
00618 dbus_connection_ref (d->connection);
00619
00620 d->before_connection = NULL;
00621 link = _dbus_list_get_first_link (&service->owners);
00622 while (link != NULL)
00623 {
00624 if (link->data == connection)
00625 {
00626 link = _dbus_list_get_next_link (&service->owners, link);
00627
00628 if (link)
00629 d->before_connection = link->data;
00630
00631 break;
00632 }
00633
00634 link = _dbus_list_get_next_link (&service->owners, link);
00635 }
00636
00637 if (d->service_link == NULL ||
00638 d->connection_link == NULL ||
00639 d->hash_entry == NULL ||
00640 !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
00641 free_ownership_restore_data))
00642 {
00643 free_ownership_restore_data (d);
00644 return FALSE;
00645 }
00646
00647 return TRUE;
00648 }
00649
00650
00651 dbus_bool_t
00652 bus_service_remove_owner (BusService *service,
00653 DBusConnection *owner,
00654 BusTransaction *transaction,
00655 DBusError *error)
00656 {
00657 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00658
00659
00660
00661
00662
00663
00664 if (bus_service_get_primary_owner (service) == owner)
00665 {
00666 if (!bus_driver_send_service_lost (owner, service->name,
00667 transaction, error))
00668 return FALSE;
00669 }
00670
00671 if (service->owners == NULL)
00672 {
00673 _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
00674 }
00675 else if (_dbus_list_length_is_one (&service->owners))
00676 {
00677 if (!bus_driver_send_service_deleted (service->name,
00678 transaction, error))
00679 return FALSE;
00680 }
00681 else
00682 {
00683 DBusList *link;
00684 link = _dbus_list_get_first (&service->owners);
00685 _dbus_assert (link != NULL);
00686 link = _dbus_list_get_next_link (&service->owners, link);
00687
00688 _dbus_assert (link != NULL);
00689
00690
00691 if (!bus_driver_send_service_acquired (link->data,
00692 service->name,
00693 transaction,
00694 error))
00695 return FALSE;
00696 }
00697
00698 if (!add_restore_ownership_to_transaction (transaction, service, owner))
00699 {
00700 BUS_SET_OOM (error);
00701 return FALSE;
00702 }
00703
00704 bus_service_unlink_owner (service, owner);
00705
00706 if (service->owners == NULL)
00707 bus_service_unlink (service);
00708
00709 return TRUE;
00710 }
00711
00712 void
00713 bus_service_ref (BusService *service)
00714 {
00715 _dbus_assert (service->refcount > 0);
00716
00717 service->refcount += 1;
00718 }
00719
00720 void
00721 bus_service_unref (BusService *service)
00722 {
00723 _dbus_assert (service->refcount > 0);
00724
00725 service->refcount -= 1;
00726
00727 if (service->refcount == 0)
00728 {
00729 _dbus_assert (service->owners == NULL);
00730
00731 dbus_free (service->name);
00732 _dbus_mem_pool_dealloc (service->registry->service_pool, service);
00733 }
00734 }
00735
00736 DBusConnection*
00737 bus_service_get_primary_owner (BusService *service)
00738 {
00739 return _dbus_list_get_first (&service->owners);
00740 }
00741
00742 const char*
00743 bus_service_get_name (BusService *service)
00744 {
00745 return service->name;
00746 }
00747
00748 void
00749 bus_service_set_prohibit_replacement (BusService *service,
00750 dbus_bool_t prohibit_replacement)
00751 {
00752 service->prohibit_replacement = prohibit_replacement != FALSE;
00753 }
00754
00755 dbus_bool_t
00756 bus_service_get_prohibit_replacement (BusService *service)
00757 {
00758 return service->prohibit_replacement;
00759 }
00760
00761 dbus_bool_t
00762 bus_service_has_owner (BusService *service,
00763 DBusConnection *owner)
00764 {
00765 DBusList *link;
00766
00767 link = _dbus_list_get_first_link (&service->owners);
00768
00769 while (link != NULL)
00770 {
00771 if (link->data == owner)
00772 return TRUE;
00773
00774 link = _dbus_list_get_next_link (&service->owners, link);
00775 }
00776
00777 return FALSE;
00778 }