Main Page   Modules   Data Structures   File List   Data Fields   Related Pages  

activation.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* activation.c  Activation of services
00003  *
00004  * Copyright (C) 2003  CodeFactory AB
00005  * Copyright (C) 2003  Red Hat, Inc.
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 "activation.h"
00025 #include "desktop-file.h"
00026 #include "services.h"
00027 #include "utils.h"
00028 #include <dbus/dbus-internals.h>
00029 #include <dbus/dbus-hash.h>
00030 #include <dbus/dbus-list.h>
00031 #include <dbus/dbus-spawn.h>
00032 #include <dbus/dbus-timeout.h>
00033 #include <sys/types.h>
00034 #include <dirent.h>
00035 #include <errno.h>
00036 
00037 #define DBUS_SERVICE_SECTION "D-BUS Service"
00038 #define DBUS_SERVICE_NAME "Name"
00039 #define DBUS_SERVICE_EXEC "Exec"
00040 
00041 struct BusActivation
00042 {
00043   int refcount;
00044   DBusHashTable *entries;
00045   DBusHashTable *pending_activations;
00046   char *server_address;
00047   BusContext *context;
00048   int n_pending_activations; 
00052 };
00053 
00054 typedef struct
00055 {
00056   char *name;
00057   char *exec;
00058 } BusActivationEntry;
00059 
00060 typedef struct BusPendingActivationEntry BusPendingActivationEntry;
00061 
00062 struct BusPendingActivationEntry
00063 {
00064   DBusMessage *activation_message;
00065   DBusConnection *connection;
00066 };
00067 
00068 typedef struct
00069 {
00070   int refcount;
00071   BusActivation *activation;
00072   char *service_name;
00073   DBusList *entries;
00074   int n_entries;
00075   DBusBabysitter *babysitter;
00076   DBusTimeout *timeout;
00077   unsigned int timeout_added : 1;
00078 } BusPendingActivation;
00079 
00080 static void
00081 bus_pending_activation_entry_free (BusPendingActivationEntry *entry)
00082 {
00083   if (entry->activation_message)
00084     dbus_message_unref (entry->activation_message);
00085   
00086   if (entry->connection)
00087     dbus_connection_unref (entry->connection);
00088   
00089   dbus_free (entry);
00090 }
00091 
00092 static void
00093 handle_timeout_callback (DBusTimeout   *timeout,
00094                          void          *data)
00095 {
00096   BusPendingActivation *pending_activation = data;
00097 
00098   while (!dbus_timeout_handle (pending_activation->timeout))
00099     _dbus_wait_for_memory ();
00100 }
00101 
00102 static void
00103 bus_pending_activation_ref (BusPendingActivation *pending_activation)
00104 {
00105   _dbus_assert (pending_activation->refcount > 0);
00106   pending_activation->refcount += 1;
00107 }
00108 
00109 static void
00110 bus_pending_activation_unref (BusPendingActivation *pending_activation)
00111 {
00112   DBusList *link;
00113   
00114   if (pending_activation == NULL) /* hash table requires this */
00115     return;
00116 
00117   _dbus_assert (pending_activation->refcount > 0);
00118   pending_activation->refcount -= 1;
00119 
00120   if (pending_activation->refcount > 0)
00121     return;
00122   
00123   if (pending_activation->timeout_added)
00124     {
00125       _dbus_loop_remove_timeout (bus_context_get_loop (pending_activation->activation->context),
00126                                  pending_activation->timeout,
00127                                  handle_timeout_callback, pending_activation);
00128       pending_activation->timeout_added = FALSE;
00129     }
00130 
00131   if (pending_activation->timeout)
00132     _dbus_timeout_unref (pending_activation->timeout);
00133   
00134   if (pending_activation->babysitter)
00135     {
00136       if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
00137                                                  NULL, NULL, NULL,
00138                                                  pending_activation->babysitter,
00139                                                  NULL))
00140         _dbus_assert_not_reached ("setting watch functions to NULL failed");
00141       
00142       _dbus_babysitter_unref (pending_activation->babysitter);
00143     }
00144   
00145   dbus_free (pending_activation->service_name);
00146 
00147   link = _dbus_list_get_first_link (&pending_activation->entries);
00148 
00149   while (link != NULL)
00150     {
00151       BusPendingActivationEntry *entry = link->data;
00152 
00153       bus_pending_activation_entry_free (entry);
00154 
00155       link = _dbus_list_get_next_link (&pending_activation->entries, link);
00156     }
00157   _dbus_list_clear (&pending_activation->entries);
00158 
00159   pending_activation->activation->n_pending_activations -=
00160     pending_activation->n_entries;
00161 
00162   _dbus_assert (pending_activation->activation->n_pending_activations >= 0);
00163   
00164   dbus_free (pending_activation);
00165 }
00166 
00167 static void
00168 bus_activation_entry_free (BusActivationEntry *entry)
00169 {
00170   if (!entry)
00171     return;
00172   
00173   dbus_free (entry->name);
00174   dbus_free (entry->exec);
00175 
00176   dbus_free (entry);
00177 }
00178 
00179 static dbus_bool_t
00180 add_desktop_file_entry (BusActivation  *activation,
00181                         BusDesktopFile *desktop_file,
00182                         DBusError      *error)
00183 {
00184   char *name, *exec;
00185   BusActivationEntry *entry;
00186 
00187   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00188   
00189   name = NULL;
00190   exec = NULL;
00191   entry = NULL;
00192   
00193   if (!bus_desktop_file_get_string (desktop_file,
00194                                     DBUS_SERVICE_SECTION,
00195                                     DBUS_SERVICE_NAME,
00196                                     &name))
00197     {
00198       dbus_set_error (error, DBUS_ERROR_FAILED,
00199                       "No \""DBUS_SERVICE_NAME"\" key in .service file\n");
00200       goto failed;
00201     }
00202 
00203   if (!bus_desktop_file_get_string (desktop_file,
00204                                     DBUS_SERVICE_SECTION,
00205                                     DBUS_SERVICE_EXEC,
00206                                     &exec))
00207     {
00208       dbus_set_error (error, DBUS_ERROR_FAILED,
00209                       "No \""DBUS_SERVICE_EXEC"\" key in .service file\n");
00210       goto failed;
00211     }
00212 
00213   /* FIXME we need a better-defined algorithm for which service file to
00214    * pick than "whichever one is first in the directory listing"
00215    */
00216   if (_dbus_hash_table_lookup_string (activation->entries, name))
00217     {
00218       dbus_set_error (error, DBUS_ERROR_FAILED,
00219                       "Service %s already exists in activation entry list\n", name);
00220       goto failed;
00221     }
00222   
00223   entry = dbus_new0 (BusActivationEntry, 1);
00224   if (entry == NULL)
00225     {
00226       BUS_SET_OOM (error);
00227       goto failed;
00228     }
00229   
00230   entry->name = name;
00231   entry->exec = exec;
00232 
00233   if (!_dbus_hash_table_insert_string (activation->entries, entry->name, entry))
00234     {
00235       BUS_SET_OOM (error);
00236       goto failed;
00237     }
00238 
00239   _dbus_verbose ("Added \"%s\" to list of services\n", entry->name);
00240   
00241   return TRUE;
00242 
00243  failed:
00244   dbus_free (name);
00245   dbus_free (exec);
00246   dbus_free (entry);
00247   
00248   return FALSE;
00249 }
00250 
00251 /* warning: this doesn't fully "undo" itself on failure, i.e. doesn't strip
00252  * hash entries it already added.
00253  */
00254 static dbus_bool_t
00255 load_directory (BusActivation *activation,
00256                 const char    *directory,
00257                 DBusError     *error)
00258 {
00259   DBusDirIter *iter;
00260   DBusString dir, filename;
00261   DBusString full_path;
00262   BusDesktopFile *desktop_file;
00263   DBusError tmp_error;
00264   dbus_bool_t retval;
00265   
00266   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00267   
00268   _dbus_string_init_const (&dir, directory);
00269 
00270   iter = NULL;
00271   desktop_file = NULL;
00272   
00273   if (!_dbus_string_init (&filename))
00274     {
00275       BUS_SET_OOM (error);
00276       return FALSE;
00277     }
00278 
00279   if (!_dbus_string_init (&full_path))
00280     {
00281       BUS_SET_OOM (error);
00282       _dbus_string_free (&filename);
00283       return FALSE;
00284     }
00285 
00286   retval = FALSE;
00287   
00288   /* from this point it's safe to "goto out" */
00289   
00290   iter = _dbus_directory_open (&dir, error);
00291   if (iter == NULL)
00292     {
00293       _dbus_verbose ("Failed to open directory %s: %s\n",
00294                      directory, error ? error->message : "unknown");
00295       goto out;
00296     }
00297   
00298   /* Now read the files */
00299   dbus_error_init (&tmp_error);
00300   while (_dbus_directory_get_next_file (iter, &filename, &tmp_error))
00301     {
00302       _dbus_assert (!dbus_error_is_set (&tmp_error));
00303       
00304       _dbus_string_set_length (&full_path, 0);
00305       
00306       if (!_dbus_string_append (&full_path, directory) ||
00307           !_dbus_concat_dir_and_file (&full_path, &filename))
00308         {
00309           BUS_SET_OOM (error);
00310           goto out;
00311         }
00312       
00313       if (!_dbus_string_ends_with_c_str (&filename, ".service"))
00314         {
00315           _dbus_verbose ("Skipping non-.service file %s\n",
00316                          _dbus_string_get_const_data (&filename));
00317           continue;
00318         }
00319       
00320       desktop_file = bus_desktop_file_load (&full_path, &tmp_error);
00321 
00322       if (desktop_file == NULL)
00323         {
00324           _dbus_verbose ("Could not load %s: %s\n",
00325                          _dbus_string_get_const_data (&full_path),
00326                          tmp_error.message);
00327 
00328           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
00329             {
00330               dbus_move_error (&tmp_error, error);
00331               goto out;
00332             }
00333           
00334           dbus_error_free (&tmp_error);
00335           continue;
00336         }
00337 
00338       if (!add_desktop_file_entry (activation, desktop_file, &tmp_error))
00339         {
00340           bus_desktop_file_free (desktop_file);
00341           desktop_file = NULL;
00342           
00343           _dbus_verbose ("Could not add %s to activation entry list: %s\n",
00344                          _dbus_string_get_const_data (&full_path), tmp_error.message);
00345 
00346           if (dbus_error_has_name (&tmp_error, DBUS_ERROR_NO_MEMORY))
00347             {
00348               dbus_move_error (&tmp_error, error);
00349               goto out;
00350             }
00351 
00352           dbus_error_free (&tmp_error);
00353           continue;
00354         }
00355       else
00356         {
00357           bus_desktop_file_free (desktop_file);
00358           desktop_file = NULL;
00359           continue;
00360         }
00361     }
00362 
00363   if (dbus_error_is_set (&tmp_error))
00364     {
00365       dbus_move_error (&tmp_error, error);
00366       goto out;
00367     }
00368   
00369   retval = TRUE;
00370   
00371  out:
00372   if (!retval)
00373     _DBUS_ASSERT_ERROR_IS_SET (error);
00374   else
00375     _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00376   
00377   if (iter != NULL)
00378     _dbus_directory_close (iter);
00379   if (desktop_file)
00380     bus_desktop_file_free (desktop_file);
00381   _dbus_string_free (&filename);
00382   _dbus_string_free (&full_path);
00383   
00384   return retval;
00385 }
00386 
00387 BusActivation*
00388 bus_activation_new (BusContext        *context,
00389                     const DBusString  *address,
00390                     DBusList         **directories,
00391                     DBusError         *error)
00392 {
00393   BusActivation *activation;
00394   DBusList *link;
00395   
00396   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00397   
00398   activation = dbus_new0 (BusActivation, 1);
00399   if (activation == NULL)
00400     {
00401       BUS_SET_OOM (error);
00402       return NULL;
00403     }
00404   
00405   activation->refcount = 1;
00406   activation->context = context;
00407   activation->n_pending_activations = 0;
00408   
00409   if (!_dbus_string_copy_data (address, &activation->server_address))
00410     {
00411       BUS_SET_OOM (error);
00412       goto failed;
00413     }
00414   
00415   activation->entries = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
00416                                              (DBusFreeFunction)bus_activation_entry_free);
00417   if (activation->entries == NULL)
00418     {      
00419       BUS_SET_OOM (error);
00420       goto failed;
00421     }
00422 
00423   activation->pending_activations = _dbus_hash_table_new (DBUS_HASH_STRING, NULL,
00424                                                           (DBusFreeFunction)bus_pending_activation_unref);
00425 
00426   if (activation->pending_activations == NULL)
00427     {
00428       BUS_SET_OOM (error);
00429       goto failed;
00430     }
00431   
00432   /* Load service files */
00433   link = _dbus_list_get_first_link (directories);
00434   while (link != NULL)
00435     {
00436       if (!load_directory (activation, link->data, error))
00437         goto failed;
00438       link = _dbus_list_get_next_link (directories, link);
00439     }
00440 
00441   return activation;
00442   
00443  failed:
00444   bus_activation_unref (activation);  
00445   return NULL;
00446 }
00447 
00448 void
00449 bus_activation_ref (BusActivation *activation)
00450 {
00451   _dbus_assert (activation->refcount > 0);
00452   
00453   activation->refcount += 1;
00454 }
00455 
00456 void
00457 bus_activation_unref (BusActivation *activation)
00458 {
00459   _dbus_assert (activation->refcount > 0);
00460 
00461   activation->refcount -= 1;
00462 
00463   if (activation->refcount == 0)
00464     {
00465       dbus_free (activation->server_address);
00466       if (activation->entries)
00467         _dbus_hash_table_unref (activation->entries);
00468       if (activation->pending_activations)
00469         _dbus_hash_table_unref (activation->pending_activations);
00470       dbus_free (activation);
00471     }
00472 }
00473 
00474 static void
00475 child_setup (void *data)
00476 {
00477   BusActivation *activation = data;
00478   const char *type;
00479   
00480   /* If no memory, we simply have the child exit, so it won't try
00481    * to connect to the wrong thing.
00482    */
00483   if (!_dbus_setenv ("DBUS_ACTIVATION_ADDRESS", activation->server_address))
00484     _dbus_exit (1);
00485 
00486   type = bus_context_get_type (activation->context);
00487   if (type != NULL)
00488     {
00489       if (!_dbus_setenv ("DBUS_BUS_TYPE", type))
00490         _dbus_exit (1);
00491     }
00492 }
00493 
00494 typedef struct
00495 {
00496   BusPendingActivation *pending_activation;
00497   DBusPreallocatedHash *hash_entry;
00498 } RestorePendingData;
00499 
00500 static void
00501 restore_pending (void *data)
00502 {
00503   RestorePendingData *d = data;
00504 
00505   _dbus_assert (d->pending_activation != NULL);
00506   _dbus_assert (d->hash_entry != NULL);
00507 
00508   _dbus_verbose ("Restoring pending activation for service %s, has timeout = %d\n",
00509                  d->pending_activation->service_name,
00510                  d->pending_activation->timeout_added);
00511   
00512   _dbus_hash_table_insert_string_preallocated (d->pending_activation->activation->pending_activations,
00513                                                d->hash_entry,
00514                                                d->pending_activation->service_name, d->pending_activation);
00515 
00516   bus_pending_activation_ref (d->pending_activation);
00517   
00518   d->hash_entry = NULL;
00519 }
00520 
00521 static void
00522 free_pending_restore_data (void *data)
00523 {
00524   RestorePendingData *d = data;
00525 
00526   if (d->hash_entry)
00527     _dbus_hash_table_free_preallocated_entry (d->pending_activation->activation->pending_activations,
00528                                               d->hash_entry);
00529 
00530   bus_pending_activation_unref (d->pending_activation);
00531   
00532   dbus_free (d);
00533 }
00534 
00535 static dbus_bool_t
00536 add_restore_pending_to_transaction (BusTransaction       *transaction,
00537                                     BusPendingActivation *pending_activation)
00538 {
00539   RestorePendingData *d;
00540 
00541   d = dbus_new (RestorePendingData, 1);
00542   if (d == NULL)
00543     return FALSE;
00544   
00545   d->pending_activation = pending_activation;
00546   d->hash_entry = _dbus_hash_table_preallocate_entry (d->pending_activation->activation->pending_activations);
00547   
00548   bus_pending_activation_ref (d->pending_activation);
00549   
00550   if (d->hash_entry == NULL ||
00551       !bus_transaction_add_cancel_hook (transaction, restore_pending, d,
00552                                         free_pending_restore_data))
00553     {
00554       free_pending_restore_data (d);
00555       return FALSE;
00556     }
00557 
00558   _dbus_verbose ("Saved pending activation to be restored if the transaction fails\n");
00559   
00560   return TRUE;
00561 }
00562 
00563 dbus_bool_t
00564 bus_activation_service_created (BusActivation  *activation,
00565                                 const char     *service_name,
00566                                 BusTransaction *transaction,
00567                                 DBusError      *error)
00568 {
00569   BusPendingActivation *pending_activation;
00570   DBusMessage *message;
00571   DBusList *link;
00572 
00573   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00574   
00575   /* Check if it's a pending activation */
00576   pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
00577 
00578   if (!pending_activation)
00579     return TRUE;
00580 
00581   link = _dbus_list_get_first_link (&pending_activation->entries);
00582   while (link != NULL)
00583     {
00584       BusPendingActivationEntry *entry = link->data;
00585       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
00586       
00587       if (dbus_connection_get_is_connected (entry->connection))
00588         {
00589           message = dbus_message_new_reply (entry->activation_message);
00590           if (!message)
00591             {
00592               BUS_SET_OOM (error);
00593               goto error;
00594             }
00595 
00596           if (!dbus_message_append_args (message,
00597                                          DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ACTIVATED,
00598                                          DBUS_TYPE_INVALID))
00599             {
00600               dbus_message_unref (message);
00601               BUS_SET_OOM (error);
00602               goto error;
00603             }
00604           
00605           if (!bus_transaction_send_from_driver (transaction, entry->connection, message))
00606             {
00607               dbus_message_unref (message);
00608               BUS_SET_OOM (error);
00609               goto error;
00610             }
00611           
00612           dbus_message_unref (message);
00613         }
00614 
00615       link = next;
00616     }
00617 
00618   if (!add_restore_pending_to_transaction (transaction, pending_activation))
00619     {
00620       _dbus_verbose ("Could not add cancel hook to transaction to revert removing pending activation\n");
00621       BUS_SET_OOM (error);
00622       goto error;
00623     }
00624   
00625   _dbus_hash_table_remove_string (activation->pending_activations, service_name);
00626 
00627   return TRUE;
00628 
00629  error:
00630   return FALSE;
00631 }
00632 
00639 static dbus_bool_t
00640 try_send_activation_failure (BusPendingActivation *pending_activation,
00641                              const DBusError      *how)
00642 {
00643   BusActivation *activation;
00644   DBusList *link;
00645   BusTransaction *transaction;
00646   
00647   activation = pending_activation->activation;
00648 
00649   transaction = bus_transaction_new (activation->context);
00650   if (transaction == NULL)
00651     return FALSE;
00652   
00653   link = _dbus_list_get_first_link (&pending_activation->entries);
00654   while (link != NULL)
00655     {
00656       BusPendingActivationEntry *entry = link->data;
00657       DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
00658       
00659       if (dbus_connection_get_is_connected (entry->connection))
00660         {
00661           if (!bus_transaction_send_error_reply (transaction,
00662                                                  entry->connection,
00663                                                  how,
00664                                                  entry->activation_message))
00665             goto error;
00666         }
00667       
00668       link = next;
00669     }
00670 
00671   bus_transaction_execute_and_free (transaction);
00672   
00673   return TRUE;
00674 
00675  error:
00676   if (transaction)
00677     bus_transaction_cancel_and_free (transaction);
00678   return FALSE;
00679 }
00680 
00685 static void
00686 pending_activation_failed (BusPendingActivation *pending_activation,
00687                            const DBusError      *how)
00688 {
00689   /* FIXME use preallocated OOM messages instead of bus_wait_for_memory() */
00690   while (!try_send_activation_failure (pending_activation, how))
00691     _dbus_wait_for_memory ();
00692 
00693   /* Destroy this pending activation */
00694   _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
00695                                   pending_activation->service_name);
00696 }
00697 
00698 static dbus_bool_t
00699 babysitter_watch_callback (DBusWatch     *watch,
00700                            unsigned int   condition,
00701                            void          *data)
00702 {
00703   BusPendingActivation *pending_activation = data;
00704   dbus_bool_t retval;
00705   DBusBabysitter *babysitter;
00706 
00707   babysitter = pending_activation->babysitter;
00708   
00709   _dbus_babysitter_ref (babysitter);
00710   
00711   retval = dbus_watch_handle (watch, condition);
00712 
00713   /* FIXME this is broken in the same way that
00714    * connection watches used to be; there should be
00715    * a separate callback for status change, instead
00716    * of doing "if we handled a watch status might
00717    * have changed"
00718    *
00719    * Fixing this lets us move dbus_watch_handle
00720    * calls into dbus-mainloop.c
00721    */
00722   
00723   if (_dbus_babysitter_get_child_exited (babysitter))
00724     {
00725       DBusError error;
00726 
00727       dbus_error_init (&error);
00728       _dbus_babysitter_set_child_exit_error (babysitter, &error);
00729 
00730       /* Destroys the pending activation */
00731       pending_activation_failed (pending_activation, &error);
00732 
00733       dbus_error_free (&error);
00734     }
00735   
00736   _dbus_babysitter_unref (babysitter);
00737 
00738   return retval;
00739 }
00740 
00741 static dbus_bool_t
00742 add_babysitter_watch (DBusWatch      *watch,
00743                       void           *data)
00744 {
00745   BusPendingActivation *pending_activation = data;
00746 
00747   return _dbus_loop_add_watch (bus_context_get_loop (pending_activation->activation->context),
00748                                watch, babysitter_watch_callback, pending_activation,
00749                                NULL);
00750 }
00751 
00752 static void
00753 remove_babysitter_watch (DBusWatch      *watch,
00754                          void           *data)
00755 {
00756   BusPendingActivation *pending_activation = data;
00757   
00758   _dbus_loop_remove_watch (bus_context_get_loop (pending_activation->activation->context),
00759                            watch, babysitter_watch_callback, pending_activation);
00760 }
00761 
00762 static dbus_bool_t
00763 pending_activation_timed_out (void *data)
00764 {
00765   BusPendingActivation *pending_activation = data;
00766   DBusError error;
00767   
00768   /* Kill the spawned process, since it sucks
00769    * (not sure this is what we want to do, but
00770    * may as well try it for now)
00771    */
00772   _dbus_babysitter_kill_child (pending_activation->babysitter);
00773 
00774   dbus_error_init (&error);
00775 
00776   dbus_set_error (&error, DBUS_ERROR_TIMED_OUT,
00777                   "Activation of %s timed out",
00778                   pending_activation->service_name);
00779 
00780   pending_activation_failed (pending_activation, &error);
00781 
00782   dbus_error_free (&error);
00783 
00784   return TRUE;
00785 }
00786 
00787 static void
00788 cancel_pending (void *data)
00789 {
00790   BusPendingActivation *pending_activation = data;
00791 
00792   _dbus_verbose ("Canceling pending activation of %s\n",
00793                  pending_activation->service_name);
00794 
00795   if (pending_activation->babysitter)
00796     _dbus_babysitter_kill_child (pending_activation->babysitter);
00797   
00798   _dbus_hash_table_remove_string (pending_activation->activation->pending_activations,
00799                                   pending_activation->service_name);
00800 }
00801 
00802 static void
00803 free_pending_cancel_data (void *data)
00804 {
00805   BusPendingActivation *pending_activation = data;
00806   
00807   bus_pending_activation_unref (pending_activation);
00808 }
00809 
00810 static dbus_bool_t
00811 add_cancel_pending_to_transaction (BusTransaction       *transaction,
00812                                    BusPendingActivation *pending_activation)
00813 {  
00814   if (!bus_transaction_add_cancel_hook (transaction, cancel_pending,
00815                                         pending_activation,
00816                                         free_pending_cancel_data))
00817     return FALSE;
00818 
00819   bus_pending_activation_ref (pending_activation); 
00820   
00821   _dbus_verbose ("Saved pending activation to be canceled if the transaction fails\n");
00822   
00823   return TRUE;
00824 }
00825 
00826 dbus_bool_t
00827 bus_activation_activate_service (BusActivation  *activation,
00828                                  DBusConnection *connection,
00829                                  BusTransaction *transaction,
00830                                  DBusMessage    *activation_message,
00831                                  const char     *service_name,
00832                                  DBusError      *error)
00833 {
00834   BusActivationEntry *entry;
00835   BusPendingActivation *pending_activation;
00836   BusPendingActivationEntry *pending_activation_entry;
00837   DBusMessage *message;
00838   DBusString service_str;
00839   char *argv[2];
00840   dbus_bool_t retval;
00841 
00842   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00843 
00844   if (activation->n_pending_activations >=
00845       bus_context_get_max_pending_activations (activation->context))
00846     {
00847       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
00848                       "The maximum number of pending activations has been reached, activation of %s failed",
00849                       service_name);
00850       return FALSE;
00851     }
00852   
00853   entry = _dbus_hash_table_lookup_string (activation->entries, service_name);
00854 
00855   if (!entry)
00856     {
00857       dbus_set_error (error, DBUS_ERROR_ACTIVATE_SERVICE_NOT_FOUND,
00858                       "The service %s was not found in the activation entry list",
00859                       service_name);
00860       return FALSE;
00861     }
00862 
00863   /* Check if the service is active */
00864   _dbus_string_init_const (&service_str, service_name);
00865   if (bus_registry_lookup (bus_context_get_registry (activation->context), &service_str) != NULL)
00866     {
00867       _dbus_verbose ("Service \"%s\" is already active\n", service_name);
00868       
00869       message = dbus_message_new_reply (activation_message);
00870 
00871       if (!message)
00872         {
00873           _dbus_verbose ("No memory to create reply to activate message\n");
00874           BUS_SET_OOM (error);
00875           return FALSE;
00876         }
00877 
00878       if (!dbus_message_append_args (message,
00879                                      DBUS_TYPE_UINT32, DBUS_ACTIVATION_REPLY_ALREADY_ACTIVE, 
00880                                      DBUS_TYPE_INVALID))
00881         {
00882           _dbus_verbose ("No memory to set args of reply to activate message\n");
00883           BUS_SET_OOM (error);
00884           dbus_message_unref (message);
00885           return FALSE;
00886         }
00887 
00888       retval = bus_transaction_send_from_driver (transaction, connection, message);
00889       dbus_message_unref (message);
00890       if (!retval)
00891         {
00892           _dbus_verbose ("Failed to send reply\n");
00893           BUS_SET_OOM (error);
00894         }
00895 
00896       return retval;
00897     }
00898 
00899   pending_activation_entry = dbus_new0 (BusPendingActivationEntry, 1);
00900   if (!pending_activation_entry)
00901     {
00902       _dbus_verbose ("Failed to create pending activation entry\n");
00903       BUS_SET_OOM (error);
00904       return FALSE;
00905     }
00906 
00907   pending_activation_entry->activation_message = activation_message;
00908   dbus_message_ref (activation_message);
00909   pending_activation_entry->connection = connection;
00910   dbus_connection_ref (connection);
00911   
00912   /* Check if the service is being activated */
00913   pending_activation = _dbus_hash_table_lookup_string (activation->pending_activations, service_name);
00914   if (pending_activation)
00915     {
00916       if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
00917         {
00918           _dbus_verbose ("Failed to append a new entry to pending activation\n");
00919           
00920           BUS_SET_OOM (error);
00921           bus_pending_activation_entry_free (pending_activation_entry);
00922           return FALSE;
00923         }
00924 
00925       pending_activation->n_entries += 1;
00926       pending_activation->activation->n_pending_activations += 1;
00927     }
00928   else
00929     {
00930       pending_activation = dbus_new0 (BusPendingActivation, 1);
00931       if (!pending_activation)
00932         {
00933           _dbus_verbose ("Failed to create pending activation\n");
00934           
00935           BUS_SET_OOM (error);
00936           bus_pending_activation_entry_free (pending_activation_entry);   
00937           return FALSE;
00938         }
00939 
00940       pending_activation->activation = activation;
00941       pending_activation->refcount = 1;
00942       
00943       pending_activation->service_name = _dbus_strdup (service_name);
00944       if (!pending_activation->service_name)
00945         {
00946           _dbus_verbose ("Failed to copy service name for pending activation\n");
00947           
00948           BUS_SET_OOM (error);
00949           bus_pending_activation_unref (pending_activation);
00950           bus_pending_activation_entry_free (pending_activation_entry);   
00951           return FALSE;
00952         }
00953 
00954       pending_activation->timeout =
00955         _dbus_timeout_new (bus_context_get_activation_timeout (activation->context),
00956                            pending_activation_timed_out,
00957                            pending_activation,
00958                            NULL);
00959       if (!pending_activation->timeout)
00960         {
00961           _dbus_verbose ("Failed to create timeout for pending activation\n");
00962           
00963           BUS_SET_OOM (error);
00964           bus_pending_activation_unref (pending_activation);
00965           bus_pending_activation_entry_free (pending_activation_entry);   
00966           return FALSE;
00967         }
00968 
00969       if (!_dbus_loop_add_timeout (bus_context_get_loop (activation->context),
00970                                    pending_activation->timeout,
00971                                    handle_timeout_callback,
00972                                    pending_activation,
00973                                    NULL))
00974         {
00975           _dbus_verbose ("Failed to add timeout for pending activation\n");
00976           
00977           BUS_SET_OOM (error);
00978           bus_pending_activation_unref (pending_activation);
00979           bus_pending_activation_entry_free (pending_activation_entry);   
00980           return FALSE;
00981         }
00982 
00983       pending_activation->timeout_added = TRUE;
00984       
00985       if (!_dbus_list_append (&pending_activation->entries, pending_activation_entry))
00986         {
00987           _dbus_verbose ("Failed to add entry to just-created pending activation\n");
00988           
00989           BUS_SET_OOM (error);
00990           bus_pending_activation_unref (pending_activation);
00991           bus_pending_activation_entry_free (pending_activation_entry);   
00992           return FALSE;
00993         }
00994 
00995       pending_activation->n_entries += 1;
00996       pending_activation->activation->n_pending_activations += 1;
00997       
00998       if (!_dbus_hash_table_insert_string (activation->pending_activations,
00999                                            pending_activation->service_name,
01000                                            pending_activation))
01001         {
01002           _dbus_verbose ("Failed to put pending activation in hash table\n");
01003           
01004           BUS_SET_OOM (error);
01005           bus_pending_activation_unref (pending_activation);
01006           return FALSE;
01007         }
01008     }
01009 
01010   if (!add_cancel_pending_to_transaction (transaction, pending_activation))
01011     {
01012       _dbus_verbose ("Failed to add pending activation cancel hook to transaction\n");
01013       BUS_SET_OOM (error);
01014       _dbus_hash_table_remove_string (activation->pending_activations,
01015                                       pending_activation->service_name);
01016       return FALSE;
01017     }
01018   
01019   /* FIXME we need to support a full command line, not just a single
01020    * argv[0]
01021    */
01022   
01023   /* Now try to spawn the process */
01024   argv[0] = entry->exec;
01025   argv[1] = NULL;
01026 
01027   if (!_dbus_spawn_async_with_babysitter (&pending_activation->babysitter, argv,
01028                                           child_setup, activation, 
01029                                           error))
01030     {
01031       _dbus_verbose ("Failed to spawn child\n");
01032       _DBUS_ASSERT_ERROR_IS_SET (error);
01033       return FALSE;
01034     }
01035 
01036   _dbus_assert (pending_activation->babysitter != NULL);
01037   
01038   if (!_dbus_babysitter_set_watch_functions (pending_activation->babysitter,
01039                                              add_babysitter_watch,
01040                                              remove_babysitter_watch,
01041                                              NULL,
01042                                              pending_activation,
01043                                              NULL))
01044     {
01045       BUS_SET_OOM (error);
01046       _dbus_verbose ("Failed to set babysitter watch functions\n");
01047       return FALSE;
01048     }
01049   
01050   return TRUE;
01051 }

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