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
00424 typedef struct
00425 {
00426 DBusConnection *connection;
00427 BusService *service;
00428 } OwnershipCancelData;
00429
00430 static void
00431 cancel_ownership (void *data)
00432 {
00433 OwnershipCancelData *d = data;
00434
00435
00436
00437
00438
00439 bus_service_unlink_owner (d->service, d->connection);
00440
00441 if (d->service->owners == NULL)
00442 bus_service_unlink (d->service);
00443 }
00444
00445 static void
00446 free_ownership_cancel_data (void *data)
00447 {
00448 OwnershipCancelData *d = data;
00449
00450 dbus_connection_unref (d->connection);
00451 bus_service_unref (d->service);
00452
00453 dbus_free (d);
00454 }
00455
00456 static dbus_bool_t
00457 add_cancel_ownership_to_transaction (BusTransaction *transaction,
00458 BusService *service,
00459 DBusConnection *connection)
00460 {
00461 OwnershipCancelData *d;
00462
00463 d = dbus_new (OwnershipCancelData, 1);
00464 if (d == NULL)
00465 return FALSE;
00466
00467 d->service = service;
00468 d->connection = connection;
00469
00470 if (!bus_transaction_add_cancel_hook (transaction, cancel_ownership, d,
00471 free_ownership_cancel_data))
00472 {
00473 dbus_free (d);
00474 return FALSE;
00475 }
00476
00477 bus_service_ref (d->service);
00478 dbus_connection_ref (d->connection);
00479
00480 return TRUE;
00481 }
00482
00483
00484 dbus_bool_t
00485 bus_service_add_owner (BusService *service,
00486 DBusConnection *owner,
00487 BusTransaction *transaction,
00488 DBusError *error)
00489 {
00490 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00491
00492
00493
00494
00495 if (service->owners == NULL)
00496 {
00497 if (!bus_driver_send_service_acquired (owner, service->name, transaction, error))
00498 return FALSE;
00499 }
00500
00501 if (!_dbus_list_append (&service->owners,
00502 owner))
00503 {
00504 BUS_SET_OOM (error);
00505 return FALSE;
00506 }
00507
00508 if (!bus_connection_add_owned_service (owner, service))
00509 {
00510 _dbus_list_remove_last (&service->owners, owner);
00511 BUS_SET_OOM (error);
00512 return FALSE;
00513 }
00514
00515 if (!add_cancel_ownership_to_transaction (transaction,
00516 service,
00517 owner))
00518 {
00519 bus_service_unlink_owner (service, owner);
00520 BUS_SET_OOM (error);
00521 return FALSE;
00522 }
00523
00524 return TRUE;
00525 }
00526
00527 typedef struct
00528 {
00529 DBusConnection *connection;
00530 BusService *service;
00531 DBusConnection *before_connection;
00532 DBusList *connection_link;
00533 DBusList *service_link;
00534 DBusPreallocatedHash *hash_entry;
00535 } OwnershipRestoreData;
00536
00537 static void
00538 restore_ownership (void *data)
00539 {
00540 OwnershipRestoreData *d = data;
00541 DBusList *link;
00542
00543 _dbus_assert (d->service_link != NULL);
00544 _dbus_assert (d->connection_link != NULL);
00545
00546 if (d->service->owners == NULL)
00547 {
00548 _dbus_assert (d->hash_entry != NULL);
00549 bus_service_relink (d->service, d->hash_entry);
00550 }
00551 else
00552 {
00553 _dbus_assert (d->hash_entry == NULL);
00554 }
00555
00556
00557
00558
00559
00560 link = _dbus_list_get_first_link (&d->service->owners);
00561 while (link != NULL)
00562 {
00563 if (link->data == d->before_connection)
00564 break;
00565
00566 link = _dbus_list_get_next_link (&d->service->owners, link);
00567 }
00568
00569 _dbus_list_insert_before_link (&d->service->owners, link, d->connection_link);
00570
00571
00572
00573
00574
00575
00576
00577 bus_connection_add_owned_service_link (d->connection, d->service_link);
00578
00579 d->hash_entry = NULL;
00580 d->service_link = NULL;
00581 d->connection_link = NULL;
00582 }
00583
00584 static void
00585 free_ownership_restore_data (void *data)
00586 {
00587 OwnershipRestoreData *d = data;
00588
00589 if (d->service_link)
00590 _dbus_list_free_link (d->service_link);
00591 if (d->connection_link)
00592 _dbus_list_free_link (d->connection_link);
00593 if (d->hash_entry)
00594 _dbus_hash_table_free_preallocated_entry (d->service->registry->service_hash,
00595 d->hash_entry);
00596
00597 dbus_connection_unref (d->connection);
00598 bus_service_unref (d->service);
00599
00600 dbus_free (d);
00601 }
00602
00603 static dbus_bool_t
00604 add_restore_ownership_to_transaction (BusTransaction *transaction,
00605 BusService *service,
00606 DBusConnection *connection)
00607 {
00608 OwnershipRestoreData *d;
00609 DBusList *link;
00610
00611 d = dbus_new (OwnershipRestoreData, 1);
00612 if (d == NULL)
00613 return FALSE;
00614
00615 d->service = service;
00616 d->connection = connection;
00617 d->service_link = _dbus_list_alloc_link (service);
00618 d->connection_link = _dbus_list_alloc_link (connection);
00619 d->hash_entry = _dbus_hash_table_preallocate_entry (service->registry->service_hash);
00620
00621 bus_service_ref (d->service);
00622 dbus_connection_ref (d->connection);
00623
00624 d->before_connection = NULL;
00625 link = _dbus_list_get_first_link (&service->owners);
00626 while (link != NULL)
00627 {
00628 if (link->data == connection)
00629 {
00630 link = _dbus_list_get_next_link (&service->owners, link);
00631
00632 if (link)
00633 d->before_connection = link->data;
00634
00635 break;
00636 }
00637
00638 link = _dbus_list_get_next_link (&service->owners, link);
00639 }
00640
00641 if (d->service_link == NULL ||
00642 d->connection_link == NULL ||
00643 d->hash_entry == NULL ||
00644 !bus_transaction_add_cancel_hook (transaction, restore_ownership, d,
00645 free_ownership_restore_data))
00646 {
00647 free_ownership_restore_data (d);
00648 return FALSE;
00649 }
00650
00651 return TRUE;
00652 }
00653
00654
00655 dbus_bool_t
00656 bus_service_remove_owner (BusService *service,
00657 DBusConnection *owner,
00658 BusTransaction *transaction,
00659 DBusError *error)
00660 {
00661 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00662
00663
00664
00665
00666
00667
00668 if (bus_service_get_primary_owner (service) == owner)
00669 {
00670 if (!bus_driver_send_service_lost (owner, service->name,
00671 transaction, error))
00672 return FALSE;
00673 }
00674
00675 if (service->owners == NULL)
00676 {
00677 _dbus_assert_not_reached ("Tried to remove owner of a service that has no owners");
00678 }
00679 else if (_dbus_list_length_is_one (&service->owners))
00680 {
00681 if (!bus_driver_send_service_deleted (service->name,
00682 transaction, error))
00683 return FALSE;
00684 }
00685 else
00686 {
00687 DBusList *link;
00688 link = _dbus_list_get_first (&service->owners);
00689 _dbus_assert (link != NULL);
00690 link = _dbus_list_get_next_link (&service->owners, link);
00691
00692 _dbus_assert (link != NULL);
00693
00694
00695 if (!bus_driver_send_service_acquired (link->data,
00696 service->name,
00697 transaction,
00698 error))
00699 return FALSE;
00700 }
00701
00702 if (!add_restore_ownership_to_transaction (transaction, service, owner))
00703 {
00704 BUS_SET_OOM (error);
00705 return FALSE;
00706 }
00707
00708 bus_service_unlink_owner (service, owner);
00709
00710 if (service->owners == NULL)
00711 bus_service_unlink (service);
00712
00713 return TRUE;
00714 }
00715
00716 void
00717 bus_service_ref (BusService *service)
00718 {
00719 _dbus_assert (service->refcount > 0);
00720
00721 service->refcount += 1;
00722 }
00723
00724 void
00725 bus_service_unref (BusService *service)
00726 {
00727 _dbus_assert (service->refcount > 0);
00728
00729 service->refcount -= 1;
00730
00731 if (service->refcount == 0)
00732 {
00733 _dbus_assert (service->owners == NULL);
00734
00735 dbus_free (service->name);
00736 _dbus_mem_pool_dealloc (service->registry->service_pool, service);
00737 }
00738 }
00739
00740 DBusConnection*
00741 bus_service_get_primary_owner (BusService *service)
00742 {
00743 return _dbus_list_get_first (&service->owners);
00744 }
00745
00746 const char*
00747 bus_service_get_name (BusService *service)
00748 {
00749 return service->name;
00750 }
00751
00752 void
00753 bus_service_set_prohibit_replacement (BusService *service,
00754 dbus_bool_t prohibit_replacement)
00755 {
00756 service->prohibit_replacement = prohibit_replacement != FALSE;
00757 }
00758
00759 dbus_bool_t
00760 bus_service_get_prohibit_replacement (BusService *service)
00761 {
00762 return service->prohibit_replacement;
00763 }
00764
00765 dbus_bool_t
00766 bus_service_has_owner (BusService *service,
00767 DBusConnection *owner)
00768 {
00769 DBusList *link;
00770
00771 link = _dbus_list_get_first_link (&service->owners);
00772
00773 while (link != NULL)
00774 {
00775 if (link->data == owner)
00776 return TRUE;
00777
00778 link = _dbus_list_get_next_link (&service->owners, link);
00779 }
00780
00781 return FALSE;
00782 }