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 
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   /* We don't need to send messages notifying of these
00436    * changes, since we're reverting something that was
00437    * cancelled (effectively never really happened)
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 /* this function is self-cancelling if you cancel the transaction */
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  /* Send service acquired message first, OOM will result
00493   * in cancelling the transaction
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; /* restore to position before this connection in owners list */
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   /* We don't need to send messages notifying of these
00557    * changes, since we're reverting something that was
00558    * cancelled (effectively never really happened)
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   /* Note that removing then restoring this changes the order in which
00572    * ServiceDeleted messages are sent on destruction of the
00573    * connection.  This should be OK as the only guarantee there is
00574    * that the base service is destroyed last, and we never even
00575    * tentatively remove the base service.
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 /* this function is self-cancelling if you cancel the transaction */
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   /* We send out notifications before we do any work we
00664    * might have to undo if the notification-sending failed
00665    */
00666   
00667   /* Send service lost message */
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       /* This will be our new owner */
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 }

Generated on Mon Sep 29 21:31:02 2003 for D-BUS by doxygen1.2.15