Main Page   Modules   Data Structures   File List   Data Fields   Related Pages  

services.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* services.c  Service management
00003  *
00004  * Copyright (C) 2003  Red Hat, Inc.
00005  * Copyright (C) 2003  CodeFactory AB
00006  *
00007  * Licensed under the Academic Free License version 1.2
00008  * 
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  * 
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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       /* The add_owner gets reverted on transaction cancel */
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       /* Not allowed; only base services can start with ':' */
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       /* Queue the connection */
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       /* Replace the current owner */
00358 
00359       /* We enqueue the new owner and remove the first one because
00360        * that will cause ServiceAcquired and ServiceLost messages to
00361        * be sent.
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   /* the service may not be in the hash, if
00396    * the failure causing transaction cancel
00397    * was in the right place, but that's OK
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   /* We don't need to send messages notifying of these
00432    * changes, since we're reverting something that was
00433    * cancelled (effectively never really happened)
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 /* this function is self-cancelling if you cancel the transaction */
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  /* Send service acquired message first, OOM will result
00489   * in cancelling the transaction
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; /* restore to position before this connection in owners list */
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   /* We don't need to send messages notifying of these
00553    * changes, since we're reverting something that was
00554    * cancelled (effectively never really happened)
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   /* Note that removing then restoring this changes the order in which
00568    * ServiceDeleted messages are sent on destruction of the
00569    * connection.  This should be OK as the only guarantee there is
00570    * that the base service is destroyed last, and we never even
00571    * tentatively remove the base service.
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 /* this function is self-cancelling if you cancel the transaction */
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   /* We send out notifications before we do any work we
00660    * might have to undo if the notification-sending failed
00661    */
00662   
00663   /* Send service lost message */
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       /* This will be our new owner */
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 }

Generated on Wed Oct 22 14:05:06 2003 for D-BUS by doxygen1.3-rc3