Main Page   Modules   Data Structures   File List   Data Fields   Related Pages  

connection.c

00001 /* -*- mode: C; c-file-style: "gnu" -*- */
00002 /* connection.c  Client connections
00003  *
00004  * Copyright (C) 2003  Red Hat, Inc.
00005  *
00006  * Licensed under the Academic Free License version 1.2
00007  * 
00008  * This program is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * This program is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  * 
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00021  *
00022  */
00023 #include "connection.h"
00024 #include "dispatch.h"
00025 #include "policy.h"
00026 #include "services.h"
00027 #include "utils.h"
00028 #include <dbus/dbus-list.h>
00029 #include <dbus/dbus-hash.h>
00030 #include <dbus/dbus-timeout.h>
00031 
00032 static void bus_connection_remove_transactions (DBusConnection *connection);
00033 
00034 struct BusConnections
00035 {
00036   int refcount;
00037   DBusList *completed;  
00038   int n_completed;      
00039   DBusList *incomplete; 
00040   int n_incomplete;     
00041   BusContext *context;
00042   DBusHashTable *completed_by_user; 
00043   DBusTimeout *expire_timeout; 
00044 };
00045 
00046 static dbus_int32_t connection_data_slot = -1;
00047 
00048 typedef struct
00049 {
00050   BusConnections *connections;
00051   DBusList *link_in_connection_list;
00052   DBusConnection *connection;
00053   DBusList *services_owned;
00054   int n_services_owned;
00055   char *name;
00056   DBusList *transaction_messages; 
00057   DBusMessage *oom_message;
00058   DBusPreallocatedSend *oom_preallocated;
00059   BusClientPolicy *policy;
00060 
00061   long connection_tv_sec;  
00062   long connection_tv_usec; 
00063 } BusConnectionData;
00064 
00065 static dbus_bool_t expire_incomplete_timeout (void *data);
00066 
00067 #define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
00068 
00069 static DBusLoop*
00070 connection_get_loop (DBusConnection *connection)
00071 {
00072   BusConnectionData *d;
00073 
00074   d = BUS_CONNECTION_DATA (connection);
00075 
00076   return bus_context_get_loop (d->connections->context);
00077 }
00078 
00079 
00080 static int
00081 get_connections_for_uid (BusConnections *connections,
00082                          dbus_uid_t      uid)
00083 {
00084   void *val;
00085   int current_count;
00086 
00087   /* val is NULL is 0 when it isn't in the hash yet */
00088   
00089   val = _dbus_hash_table_lookup_ulong (connections->completed_by_user,
00090                                        uid);
00091 
00092   current_count = _DBUS_POINTER_TO_INT (val);
00093 
00094   return current_count;
00095 }
00096 
00097 static dbus_bool_t
00098 adjust_connections_for_uid (BusConnections *connections,
00099                             dbus_uid_t      uid,
00100                             int             adjustment)
00101 {
00102   int current_count;
00103 
00104   current_count = get_connections_for_uid (connections, uid);
00105 
00106   _dbus_verbose ("Adjusting connection count for UID " DBUS_UID_FORMAT
00107                  ": was %d adjustment %d making %d\n",
00108                  uid, current_count, adjustment, current_count + adjustment);
00109   
00110   _dbus_assert (current_count >= 0);
00111   
00112   current_count += adjustment;
00113 
00114   _dbus_assert (current_count >= 0);
00115 
00116   if (current_count == 0)
00117     {
00118       _dbus_hash_table_remove_ulong (connections->completed_by_user, uid);
00119       return TRUE;
00120     }
00121   else
00122     {
00123       dbus_bool_t retval;
00124       
00125       retval = _dbus_hash_table_insert_ulong (connections->completed_by_user,
00126                                               uid, _DBUS_INT_TO_POINTER (current_count));
00127 
00128       /* only positive adjustment can fail as otherwise
00129        * a hash entry should already exist
00130        */
00131       _dbus_assert (adjustment > 0 ||
00132                     (adjustment <= 0 && retval));
00133 
00134       return retval;
00135     }
00136 }
00137 
00138 void
00139 bus_connection_disconnected (DBusConnection *connection)
00140 {
00141   BusConnectionData *d;
00142   BusService *service;
00143 
00144   d = BUS_CONNECTION_DATA (connection);
00145   _dbus_assert (d != NULL);
00146 
00147   _dbus_verbose ("%s disconnected, dropping all service ownership and releasing\n",
00148                  d->name ? d->name : "(inactive)");
00149   
00150   /* Drop any service ownership. FIXME Unfortunately, this requires
00151    * memory allocation and there doesn't seem to be a good way to
00152    * handle it other than sleeping; we can't "fail" the operation of
00153    * disconnecting a client, and preallocating a broadcast "service is
00154    * now gone" message for every client-service pair seems kind of
00155    * involved. Probably we need to do that though.
00156    */
00157   while ((service = _dbus_list_get_last (&d->services_owned)))
00158     {
00159       BusTransaction *transaction;
00160       DBusError error;
00161 
00162     retry:
00163       
00164       dbus_error_init (&error);
00165         
00166       transaction = NULL;
00167       while (transaction == NULL)
00168         {
00169           transaction = bus_transaction_new (d->connections->context);
00170           _dbus_wait_for_memory ();
00171         }
00172         
00173       if (!bus_service_remove_owner (service, connection,
00174                                      transaction, &error))
00175         {
00176           _DBUS_ASSERT_ERROR_IS_SET (&error);
00177           
00178           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
00179             {
00180               dbus_error_free (&error);
00181               bus_transaction_cancel_and_free (transaction);
00182               _dbus_wait_for_memory ();
00183               goto retry;
00184             }
00185           else
00186             {
00187               _dbus_verbose ("Failed to remove service owner: %s %s\n",
00188                              error.name, error.message);
00189               _dbus_assert_not_reached ("Removing service owner failed for non-memory-related reason");
00190             }
00191         }
00192         
00193       bus_transaction_execute_and_free (transaction);
00194     }
00195 
00196   bus_dispatch_remove_connection (connection);
00197   
00198   /* no more watching */
00199   if (!dbus_connection_set_watch_functions (connection,
00200                                             NULL, NULL, NULL,
00201                                             connection,
00202                                             NULL))
00203     _dbus_assert_not_reached ("setting watch functions to NULL failed");
00204 
00205   if (!dbus_connection_set_timeout_functions (connection,
00206                                               NULL, NULL, NULL,
00207                                               connection,
00208                                               NULL))
00209     _dbus_assert_not_reached ("setting timeout functions to NULL failed");
00210   
00211   dbus_connection_set_unix_user_function (connection,
00212                                           NULL, NULL, NULL);
00213 
00214   dbus_connection_set_dispatch_status_function (connection,
00215                                                 NULL, NULL, NULL);
00216   
00217   bus_connection_remove_transactions (connection);
00218 
00219   if (d->link_in_connection_list != NULL)
00220     {
00221       if (d->name != NULL)
00222         {
00223           unsigned long uid;
00224           
00225           _dbus_list_remove_link (&d->connections->completed, d->link_in_connection_list);
00226           d->link_in_connection_list = NULL;
00227           d->connections->n_completed -= 1;
00228 
00229           if (dbus_connection_get_unix_user (connection, &uid))
00230             {
00231               if (!adjust_connections_for_uid (d->connections,
00232                                                uid, -1))
00233                 _dbus_assert_not_reached ("adjusting downward should never fail");
00234             }
00235         }
00236       else
00237         {
00238           _dbus_list_remove_link (&d->connections->incomplete, d->link_in_connection_list);
00239           d->link_in_connection_list = NULL;
00240           d->connections->n_incomplete -= 1;
00241         }
00242       
00243       _dbus_assert (d->connections->n_incomplete >= 0);
00244       _dbus_assert (d->connections->n_completed >= 0);
00245     }
00246   
00247   /* frees "d" as side effect */
00248   dbus_connection_set_data (connection,
00249                             connection_data_slot,
00250                             NULL, NULL);
00251 
00252   dbus_connection_unref (connection);
00253 }
00254 
00255 static dbus_bool_t
00256 connection_watch_callback (DBusWatch     *watch,
00257                            unsigned int   condition,
00258                            void          *data)
00259 {
00260  /* FIXME this can be done in dbus-mainloop.c
00261   * if the code in activation.c for the babysitter
00262   * watch handler is fixed.
00263   */
00264   
00265 #if 0
00266   _dbus_verbose ("Calling handle_watch\n");
00267 #endif
00268   return dbus_watch_handle (watch, condition);
00269 }
00270 
00271 static dbus_bool_t
00272 add_connection_watch (DBusWatch      *watch,
00273                       void           *data)
00274 {
00275   DBusConnection *connection = data;
00276 
00277   return _dbus_loop_add_watch (connection_get_loop (connection),
00278                                watch, connection_watch_callback, connection,
00279                                NULL);
00280 }
00281 
00282 static void
00283 remove_connection_watch (DBusWatch      *watch,
00284                          void           *data)
00285 {
00286   DBusConnection *connection = data;
00287   
00288   _dbus_loop_remove_watch (connection_get_loop (connection),
00289                            watch, connection_watch_callback, connection);
00290 }
00291 
00292 static void
00293 connection_timeout_callback (DBusTimeout   *timeout,
00294                              void          *data)
00295 {
00296   /* DBusConnection *connection = data; */
00297 
00298   /* can return FALSE on OOM but we just let it fire again later */
00299   dbus_timeout_handle (timeout);
00300 }
00301 
00302 static dbus_bool_t
00303 add_connection_timeout (DBusTimeout    *timeout,
00304                         void           *data)
00305 {
00306   DBusConnection *connection = data;
00307   
00308   return _dbus_loop_add_timeout (connection_get_loop (connection),
00309                                  timeout, connection_timeout_callback, connection, NULL);
00310 }
00311 
00312 static void
00313 remove_connection_timeout (DBusTimeout    *timeout,
00314                            void           *data)
00315 {
00316   DBusConnection *connection = data;
00317   
00318   _dbus_loop_remove_timeout (connection_get_loop (connection),
00319                              timeout, connection_timeout_callback, connection);
00320 }
00321 
00322 static void
00323 dispatch_status_function (DBusConnection    *connection,
00324                           DBusDispatchStatus new_status,
00325                           void              *data)
00326 {
00327   DBusLoop *loop = data;
00328   
00329   if (new_status != DBUS_DISPATCH_COMPLETE)
00330     {
00331       while (!_dbus_loop_queue_dispatch (loop, connection))
00332         _dbus_wait_for_memory ();
00333     }
00334 }
00335 
00336 static dbus_bool_t
00337 allow_user_function (DBusConnection *connection,
00338                      unsigned long   uid,
00339                      void           *data)
00340 {
00341   BusConnectionData *d;
00342     
00343   d = BUS_CONNECTION_DATA (connection);
00344 
00345   _dbus_assert (d != NULL);
00346   
00347   return bus_context_allow_user (d->connections->context, uid);
00348 }
00349 
00350 static void
00351 free_connection_data (void *data)
00352 {
00353   BusConnectionData *d = data;
00354 
00355   /* services_owned should be NULL since we should be disconnected */
00356   _dbus_assert (d->services_owned == NULL);
00357   _dbus_assert (d->n_services_owned == 0);
00358   /* similarly */
00359   _dbus_assert (d->transaction_messages == NULL);
00360 
00361   if (d->oom_preallocated)
00362     dbus_connection_free_preallocated_send (d->connection, d->oom_preallocated);
00363 
00364   if (d->oom_message)
00365     dbus_message_unref (d->oom_message);
00366 
00367   if (d->policy)
00368     bus_client_policy_unref (d->policy);
00369   
00370   dbus_free (d->name);
00371   
00372   dbus_free (d);
00373 }
00374 
00375 static void
00376 call_timeout_callback (DBusTimeout   *timeout,
00377                        void          *data)
00378 {
00379   /* can return FALSE on OOM but we just let it fire again later */
00380   dbus_timeout_handle (timeout);
00381 }
00382 
00383 BusConnections*
00384 bus_connections_new (BusContext *context)
00385 {
00386   BusConnections *connections;
00387 
00388   if (!dbus_connection_allocate_data_slot (&connection_data_slot))
00389     goto failed_0;
00390 
00391   connections = dbus_new0 (BusConnections, 1);
00392   if (connections == NULL)
00393     goto failed_1;
00394 
00395   connections->completed_by_user = _dbus_hash_table_new (DBUS_HASH_ULONG,
00396                                                          NULL, NULL);
00397   if (connections->completed_by_user == NULL)
00398     goto failed_2;
00399 
00400   connections->expire_timeout = _dbus_timeout_new (100, /* irrelevant */
00401                                                    expire_incomplete_timeout,
00402                                                    connections, NULL);
00403   if (connections->expire_timeout == NULL)
00404     goto failed_3;
00405 
00406   _dbus_timeout_set_enabled (connections->expire_timeout, FALSE);
00407 
00408   if (!_dbus_loop_add_timeout (bus_context_get_loop (context),
00409                                connections->expire_timeout,
00410                                call_timeout_callback, NULL, NULL))
00411     goto failed_4;
00412   
00413   connections->refcount = 1;
00414   connections->context = context;
00415   
00416   return connections;
00417 
00418  failed_4:
00419   _dbus_timeout_unref (connections->expire_timeout);
00420  failed_3:
00421   _dbus_hash_table_unref (connections->completed_by_user);
00422  failed_2:
00423   dbus_free (connections);
00424  failed_1:
00425   dbus_connection_free_data_slot (&connection_data_slot);
00426  failed_0:
00427   return NULL;
00428 }
00429 
00430 void
00431 bus_connections_ref (BusConnections *connections)
00432 {
00433   _dbus_assert (connections->refcount > 0);
00434   connections->refcount += 1;
00435 }
00436 
00437 void
00438 bus_connections_unref (BusConnections *connections)
00439 {
00440   _dbus_assert (connections->refcount > 0);
00441   connections->refcount -= 1;
00442   if (connections->refcount == 0)
00443     {
00444       /* drop all incomplete */
00445       while (connections->incomplete != NULL)
00446         {
00447           DBusConnection *connection;
00448 
00449           connection = connections->incomplete->data;
00450 
00451           dbus_connection_ref (connection);
00452           dbus_connection_disconnect (connection);
00453           bus_connection_disconnected (connection);
00454           dbus_connection_unref (connection);
00455         }
00456 
00457       _dbus_assert (connections->n_incomplete == 0);
00458       
00459       /* drop all real connections */
00460       while (connections->completed != NULL)
00461         {
00462           DBusConnection *connection;
00463 
00464           connection = connections->completed->data;
00465 
00466           dbus_connection_ref (connection);
00467           dbus_connection_disconnect (connection);
00468           bus_connection_disconnected (connection);
00469           dbus_connection_unref (connection);          
00470         }
00471 
00472       _dbus_assert (connections->n_completed == 0);
00473 
00474       _dbus_loop_remove_timeout (bus_context_get_loop (connections->context),
00475                                  connections->expire_timeout,
00476                                  call_timeout_callback, NULL);
00477       
00478       _dbus_timeout_unref (connections->expire_timeout);
00479       
00480       _dbus_hash_table_unref (connections->completed_by_user);
00481       
00482       dbus_free (connections);
00483 
00484       dbus_connection_free_data_slot (&connection_data_slot);
00485     }
00486 }
00487 
00488 dbus_bool_t
00489 bus_connections_setup_connection (BusConnections *connections,
00490                                   DBusConnection *connection)
00491 {
00492   BusConnectionData *d;
00493   dbus_bool_t retval;
00494   
00495   d = dbus_new0 (BusConnectionData, 1);
00496   
00497   if (d == NULL)
00498     return FALSE;
00499 
00500   d->connections = connections;
00501   d->connection = connection;
00502   
00503   _dbus_get_current_time (&d->connection_tv_sec,
00504                           &d->connection_tv_usec);
00505   
00506   _dbus_assert (connection_data_slot >= 0);
00507   
00508   if (!dbus_connection_set_data (connection,
00509                                  connection_data_slot,
00510                                  d, free_connection_data))
00511     {
00512       dbus_free (d);
00513       return FALSE;
00514     }
00515 
00516   retval = FALSE;
00517   
00518   if (!dbus_connection_set_watch_functions (connection,
00519                                             add_connection_watch,
00520                                             remove_connection_watch,
00521                                             NULL,
00522                                             connection,
00523                                             NULL))
00524     goto out;
00525   
00526   if (!dbus_connection_set_timeout_functions (connection,
00527                                               add_connection_timeout,
00528                                               remove_connection_timeout,
00529                                               NULL,
00530                                               connection, NULL))
00531     goto out;
00532   
00533   dbus_connection_set_unix_user_function (connection,
00534                                           allow_user_function,
00535                                           NULL, NULL);
00536 
00537   dbus_connection_set_dispatch_status_function (connection,
00538                                                 dispatch_status_function,
00539                                                 bus_context_get_loop (connections->context),
00540                                                 NULL);
00541 
00542   d->link_in_connection_list = _dbus_list_alloc_link (connection);
00543   if (d->link_in_connection_list == NULL)
00544     goto out;
00545   
00546   /* Setup the connection with the dispatcher */
00547   if (!bus_dispatch_add_connection (connection))
00548     goto out;
00549 
00550   if (dbus_connection_get_dispatch_status (connection) != DBUS_DISPATCH_COMPLETE)
00551     {
00552       if (!_dbus_loop_queue_dispatch (bus_context_get_loop (connections->context), connection))
00553         {
00554           bus_dispatch_remove_connection (connection);
00555           goto out;
00556         }
00557     }
00558 
00559   _dbus_list_append_link (&connections->incomplete, d->link_in_connection_list);
00560   connections->n_incomplete += 1;
00561   
00562   dbus_connection_ref (connection);
00563 
00564   /* Note that we might disconnect ourselves here, but it only takes
00565    * effect on return to the main loop. We call this to free up
00566    * expired connections if possible, and to queue the timeout for our
00567    * own expiration.
00568    */
00569   bus_connections_expire_incomplete (connections);
00570   
00571   /* And we might also disconnect ourselves here, but again it
00572    * only takes effect on return to main loop.
00573    */
00574   if (connections->n_incomplete >
00575       bus_context_get_max_incomplete_connections (connections->context))
00576     {
00577       _dbus_verbose ("Number of incomplete connections exceeds max, dropping oldest one\n");
00578       
00579       _dbus_assert (connections->incomplete != NULL);
00580       /* Disconnect the oldest unauthenticated connection.  FIXME
00581        * would it be more secure to drop a *random* connection?  This
00582        * algorithm seems to mean that if someone can create new
00583        * connections quickly enough, they can keep anyone else from
00584        * completing authentication. But random may or may not really
00585        * help with that, a more elaborate solution might be required.
00586        */
00587       dbus_connection_disconnect (connections->incomplete->data);
00588     }
00589   
00590   retval = TRUE;
00591 
00592  out:
00593   if (!retval)
00594     {      
00595       if (!dbus_connection_set_watch_functions (connection,
00596                                                 NULL, NULL, NULL,
00597                                                 connection,
00598                                                 NULL))
00599         _dbus_assert_not_reached ("setting watch functions to NULL failed");
00600       
00601       if (!dbus_connection_set_timeout_functions (connection,
00602                                                   NULL, NULL, NULL,
00603                                                   connection,
00604                                                   NULL))
00605         _dbus_assert_not_reached ("setting timeout functions to NULL failed");
00606 
00607       dbus_connection_set_unix_user_function (connection,
00608                                               NULL, NULL, NULL);
00609 
00610       dbus_connection_set_dispatch_status_function (connection,
00611                                                     NULL, NULL, NULL);
00612 
00613       if (d->link_in_connection_list != NULL)
00614         {
00615           _dbus_assert (d->link_in_connection_list->next == NULL);
00616           _dbus_assert (d->link_in_connection_list->prev == NULL);
00617           _dbus_list_free_link (d->link_in_connection_list);
00618           d->link_in_connection_list = NULL;
00619         }
00620       
00621       if (!dbus_connection_set_data (connection,
00622                                      connection_data_slot,
00623                                      NULL, NULL))
00624         _dbus_assert_not_reached ("failed to set connection data to null");
00625 
00626       /* "d" has now been freed */
00627     }
00628   
00629   return retval;
00630 }
00631 
00632 void
00633 bus_connections_expire_incomplete (BusConnections *connections)
00634 {    
00635   int next_interval;
00636 
00637   next_interval = -1;
00638   
00639   if (connections->incomplete != NULL)
00640     {
00641       long tv_sec, tv_usec;
00642       DBusList *link;
00643       int auth_timeout;
00644       
00645       _dbus_get_current_time (&tv_sec, &tv_usec);
00646       auth_timeout = bus_context_get_auth_timeout (connections->context);
00647   
00648       link = _dbus_list_get_first_link (&connections->incomplete);
00649       while (link != NULL)
00650         {
00651           DBusList *next = _dbus_list_get_next_link (&connections->incomplete, link);
00652           DBusConnection *connection;
00653           BusConnectionData *d;
00654           double elapsed;
00655       
00656           connection = link->data;
00657       
00658           d = BUS_CONNECTION_DATA (connection);
00659       
00660           _dbus_assert (d != NULL);
00661       
00662           elapsed = ((double) tv_sec - (double) d->connection_tv_sec) * 1000.0 +
00663             ((double) tv_usec - (double) d->connection_tv_usec) / 1000.0;
00664 
00665           if (elapsed >= (double) auth_timeout)
00666             {
00667               _dbus_verbose ("Timing out authentication for connection %p\n", connection);
00668               dbus_connection_disconnect (connection);
00669             }
00670           else
00671             {
00672               /* We can end the loop, since the connections are in oldest-first order */
00673               next_interval = ((double)auth_timeout) - elapsed;
00674               _dbus_verbose ("Connection %p authentication expires in %d milliseconds\n",
00675                              connection, next_interval);
00676           
00677               break;
00678             }
00679       
00680           link = next;
00681         }
00682     }
00683   
00684   if (next_interval >= 0)
00685     {
00686       _dbus_timeout_set_interval (connections->expire_timeout,
00687                                   next_interval);
00688       _dbus_timeout_set_enabled (connections->expire_timeout, TRUE);
00689 
00690       _dbus_verbose ("Enabled incomplete connections timeout with interval %d, %d incomplete connections\n",
00691                      next_interval, connections->n_incomplete);
00692     }
00693   else if (dbus_timeout_get_enabled (connections->expire_timeout))
00694     {
00695       _dbus_timeout_set_enabled (connections->expire_timeout, FALSE);
00696 
00697       _dbus_verbose ("Disabled incomplete connections timeout, %d incomplete connections\n",
00698                      connections->n_incomplete);
00699     }
00700   else
00701     _dbus_verbose ("No need to disable incomplete connections timeout\n");
00702 }
00703 
00704 static dbus_bool_t
00705 expire_incomplete_timeout (void *data)
00706 {
00707   BusConnections *connections = data;
00708 
00709   _dbus_verbose ("Running %s\n", _DBUS_FUNCTION_NAME);
00710   
00711   /* note that this may remove the timeout */
00712   bus_connections_expire_incomplete (connections);
00713 
00714   return TRUE;
00715 }
00716 
00717 dbus_bool_t
00718 bus_connection_get_groups  (DBusConnection   *connection,
00719                             unsigned long   **groups,
00720                             int              *n_groups,
00721                             DBusError        *error)
00722 {
00723   BusConnectionData *d;
00724   unsigned long uid;
00725   DBusUserDatabase *user_database;
00726   
00727   d = BUS_CONNECTION_DATA (connection);
00728 
00729   _dbus_assert (d != NULL);
00730 
00731   user_database = bus_context_get_user_database (d->connections->context);
00732   
00733   *groups = NULL;
00734   *n_groups = 0;
00735 
00736   if (dbus_connection_get_unix_user (connection, &uid))
00737     {
00738       if (!_dbus_user_database_get_groups (user_database,
00739                                            uid, groups, n_groups,
00740                                            error))
00741         {
00742           _DBUS_ASSERT_ERROR_IS_SET (error);
00743           _dbus_verbose ("Did not get any groups for UID %lu\n",
00744                          uid);
00745           return FALSE;
00746         }
00747       else
00748         {
00749           _dbus_verbose ("Got %d groups for UID %lu\n",
00750                          *n_groups, uid);
00751           return TRUE;
00752         }
00753     }
00754   else
00755     return TRUE; /* successfully got 0 groups */
00756 }
00757 
00758 dbus_bool_t
00759 bus_connection_is_in_group (DBusConnection *connection,
00760                             unsigned long   gid)
00761 {
00762   int i;
00763   unsigned long *group_ids;
00764   int n_group_ids;
00765 
00766   if (!bus_connection_get_groups (connection, &group_ids, &n_group_ids,
00767                                   NULL))
00768     return FALSE;
00769 
00770   i = 0;
00771   while (i < n_group_ids)
00772     {
00773       if (group_ids[i] == gid)
00774         {
00775           dbus_free (group_ids);
00776           return TRUE;
00777         }
00778       ++i;
00779     }
00780 
00781   dbus_free (group_ids);
00782   return FALSE;
00783 }
00784 
00785 BusClientPolicy*
00786 bus_connection_get_policy (DBusConnection *connection)
00787 {
00788   BusConnectionData *d;
00789     
00790   d = BUS_CONNECTION_DATA (connection);
00791 
00792   _dbus_assert (d != NULL);
00793   _dbus_assert (d->policy != NULL);
00794   
00795   return d->policy;
00796 }
00797 
00798 static dbus_bool_t
00799 foreach_active (BusConnections               *connections,
00800                 BusConnectionForeachFunction  function,
00801                 void                         *data)
00802 {
00803   DBusList *link;
00804   
00805   link = _dbus_list_get_first_link (&connections->completed);
00806   while (link != NULL)
00807     {
00808       DBusConnection *connection = link->data;
00809       DBusList *next = _dbus_list_get_next_link (&connections->completed, link);
00810 
00811       if (!(* function) (connection, data))
00812         return FALSE;
00813       
00814       link = next;
00815     }
00816 
00817   return TRUE;
00818 }
00819 
00820 static dbus_bool_t
00821 foreach_inactive (BusConnections               *connections,
00822                   BusConnectionForeachFunction  function,
00823                   void                         *data)
00824 {
00825   DBusList *link;
00826   
00827   link = _dbus_list_get_first_link (&connections->incomplete);
00828   while (link != NULL)
00829     {
00830       DBusConnection *connection = link->data;
00831       DBusList *next = _dbus_list_get_next_link (&connections->incomplete, link);
00832 
00833       if (!(* function) (connection, data))
00834         return FALSE;
00835       
00836       link = next;
00837     }
00838 
00839   return TRUE;
00840 }
00841 
00851 void
00852 bus_connections_foreach_active (BusConnections               *connections,
00853                                 BusConnectionForeachFunction  function,
00854                                 void                         *data)
00855 {
00856   foreach_active (connections, function, data);
00857 }
00858 
00867 void
00868 bus_connections_foreach (BusConnections               *connections,
00869                          BusConnectionForeachFunction  function,
00870                          void                         *data)
00871 {
00872   if (!foreach_active (connections, function, data))
00873     return;
00874 
00875   foreach_inactive (connections, function, data);
00876 }
00877 
00878 BusContext*
00879 bus_connections_get_context (BusConnections *connections)
00880 {
00881   return connections->context;
00882 }
00883 
00884 BusContext*
00885 bus_connection_get_context (DBusConnection *connection)
00886 {
00887   BusConnectionData *d;
00888 
00889   d = BUS_CONNECTION_DATA (connection);
00890 
00891   _dbus_assert (d != NULL);
00892 
00893   return d->connections->context;
00894 }
00895 
00896 BusConnections*
00897 bus_connection_get_connections (DBusConnection *connection)
00898 {
00899   BusConnectionData *d;
00900     
00901   d = BUS_CONNECTION_DATA (connection);
00902 
00903   _dbus_assert (d != NULL);
00904 
00905   return d->connections;
00906 }
00907 
00908 BusRegistry*
00909 bus_connection_get_registry (DBusConnection *connection)
00910 {
00911   BusConnectionData *d;
00912 
00913   d = BUS_CONNECTION_DATA (connection);
00914 
00915   _dbus_assert (d != NULL);
00916 
00917   return bus_context_get_registry (d->connections->context);
00918 }
00919 
00920 BusActivation*
00921 bus_connection_get_activation (DBusConnection *connection)
00922 {
00923   BusConnectionData *d;
00924 
00925   d = BUS_CONNECTION_DATA (connection);
00926 
00927   _dbus_assert (d != NULL);
00928 
00929   return bus_context_get_activation (d->connections->context);
00930 }
00931 
00938 dbus_bool_t
00939 bus_connection_is_active (DBusConnection *connection)
00940 {
00941   BusConnectionData *d;
00942 
00943   d = BUS_CONNECTION_DATA (connection);
00944   
00945   return d != NULL && d->name != NULL;
00946 }
00947 
00948 dbus_bool_t
00949 bus_connection_preallocate_oom_error (DBusConnection *connection)
00950 {
00951   DBusMessage *message;
00952   DBusPreallocatedSend *preallocated;
00953   BusConnectionData *d;
00954 
00955   d = BUS_CONNECTION_DATA (connection);  
00956 
00957   _dbus_assert (d != NULL);
00958 
00959   if (d->oom_preallocated != NULL)
00960     return TRUE;
00961   
00962   preallocated = dbus_connection_preallocate_send (connection);
00963   if (preallocated == NULL)
00964     return FALSE;
00965 
00966   /* d->name may be NULL, but that is OK */
00967   message = dbus_message_new (DBUS_ERROR_NO_MEMORY,
00968                               d->name);
00969   if (message == NULL)
00970     {
00971       dbus_connection_free_preallocated_send (connection, preallocated);
00972       return FALSE;
00973     }
00974 
00975   dbus_message_set_is_error (message, TRUE);
00976 
00977   if (!dbus_message_set_sender (message,
00978                                 DBUS_SERVICE_DBUS))
00979     {
00980       dbus_connection_free_preallocated_send (connection, preallocated);
00981       dbus_message_unref (message);
00982       return FALSE;
00983     }
00984   
00985   /* set reply serial to placeholder value just so space is already allocated
00986    * for it.
00987    */
00988   if (!dbus_message_set_reply_serial (message, 14))
00989     {
00990       dbus_connection_free_preallocated_send (connection, preallocated);
00991       dbus_message_unref (message);
00992       return FALSE;
00993     }
00994 
00995   d->oom_message = message;
00996   d->oom_preallocated = preallocated;
00997   
00998   return TRUE;
00999 }
01000 
01001 void
01002 bus_connection_send_oom_error (DBusConnection *connection,
01003                                DBusMessage    *in_reply_to)
01004 {
01005   BusConnectionData *d;
01006 
01007   d = BUS_CONNECTION_DATA (connection);  
01008 
01009   _dbus_assert (d != NULL);  
01010   _dbus_assert (d->oom_message != NULL);
01011 
01012   /* should always succeed since we set it to a placeholder earlier */
01013   if (!dbus_message_set_reply_serial (d->oom_message,
01014                                       dbus_message_get_serial (in_reply_to)))
01015     _dbus_assert_not_reached ("Failed to set reply serial for preallocated oom message");
01016 
01017   _dbus_assert (dbus_message_get_sender (d->oom_message) != NULL);
01018   
01019   dbus_connection_send_preallocated (connection, d->oom_preallocated,
01020                                      d->oom_message, NULL);
01021 
01022   dbus_message_unref (d->oom_message);
01023   d->oom_message = NULL;
01024   d->oom_preallocated = NULL;
01025 }
01026 
01027 void
01028 bus_connection_add_owned_service_link (DBusConnection *connection,
01029                                        DBusList       *link)
01030 {
01031   BusConnectionData *d;
01032 
01033   d = BUS_CONNECTION_DATA (connection);
01034   _dbus_assert (d != NULL);
01035 
01036   _dbus_list_append_link (&d->services_owned, link);
01037 
01038   d->n_services_owned += 1;
01039 }
01040 
01041 dbus_bool_t
01042 bus_connection_add_owned_service (DBusConnection *connection,
01043                                   BusService     *service)
01044 {
01045   DBusList *link;
01046 
01047   link = _dbus_list_alloc_link (service);
01048 
01049   if (link == NULL)
01050     return FALSE;
01051 
01052   bus_connection_add_owned_service_link (connection, link);
01053 
01054   return TRUE;
01055 }
01056 
01057 void
01058 bus_connection_remove_owned_service (DBusConnection *connection,
01059                                      BusService     *service)
01060 {
01061   BusConnectionData *d;
01062 
01063   d = BUS_CONNECTION_DATA (connection);
01064   _dbus_assert (d != NULL);
01065 
01066   _dbus_list_remove_last (&d->services_owned, service);
01067 
01068   d->n_services_owned -= 1;
01069   _dbus_assert (d->n_services_owned >= 0);
01070 }
01071 
01072 int
01073 bus_connection_get_n_services_owned (DBusConnection *connection)
01074 {
01075   BusConnectionData *d;
01076 
01077   d = BUS_CONNECTION_DATA (connection);
01078   _dbus_assert (d != NULL);
01079   
01080   return d->n_services_owned;
01081 }
01082 
01083 dbus_bool_t
01084 bus_connection_complete (DBusConnection   *connection,
01085                          const DBusString *name,
01086                          DBusError        *error)
01087 {
01088   BusConnectionData *d;
01089   unsigned long uid;
01090   
01091   d = BUS_CONNECTION_DATA (connection);
01092   _dbus_assert (d != NULL);
01093   _dbus_assert (d->name == NULL);
01094   _dbus_assert (d->policy == NULL);
01095   
01096   if (!_dbus_string_copy_data (name, &d->name))
01097     {
01098       BUS_SET_OOM (error);
01099       return FALSE;
01100     }
01101 
01102   _dbus_assert (d->name != NULL);
01103   
01104   _dbus_verbose ("Name %s assigned to %p\n", d->name, connection);
01105 
01106   d->policy = bus_context_create_client_policy (d->connections->context,
01107                                                 connection,
01108                                                 error);
01109 
01110   /* we may have a NULL policy on OOM or error getting list of
01111    * groups for a user. In the latter case we don't handle it so
01112    * well currently, as it will just keep failing over and over.
01113    */
01114 
01115   if (d->policy == NULL)
01116     {
01117       _dbus_verbose ("Failed to create security policy for connection %p\n",
01118                      connection);
01119       _DBUS_ASSERT_ERROR_IS_SET (error);
01120       dbus_free (d->name);
01121       d->name = NULL;
01122       return FALSE;
01123     }
01124   
01125   if (dbus_connection_get_unix_user (connection, &uid))
01126     {
01127       if (!adjust_connections_for_uid (d->connections,
01128                                        uid, 1))
01129         {
01130           BUS_SET_OOM (error);
01131           dbus_free (d->name);
01132           d->name = NULL;
01133           return FALSE;
01134         }
01135     }
01136   
01137   /* Now the connection is active, move it between lists */
01138   _dbus_list_unlink (&d->connections->incomplete,
01139                      d->link_in_connection_list);
01140   d->connections->n_incomplete -= 1;
01141   _dbus_list_append_link (&d->connections->completed,
01142                           d->link_in_connection_list);
01143   d->connections->n_completed += 1;
01144 
01145   _dbus_assert (d->connections->n_incomplete >= 0);
01146   _dbus_assert (d->connections->n_completed > 0);
01147 
01148   /* See if we can remove the timeout */
01149   bus_connections_expire_incomplete (d->connections);
01150   
01151   return TRUE;
01152 }
01153 
01154 const char *
01155 bus_connection_get_name (DBusConnection *connection)
01156 {
01157   BusConnectionData *d;
01158   
01159   d = BUS_CONNECTION_DATA (connection);
01160   _dbus_assert (d != NULL);
01161   
01162   return d->name;
01163 }
01164 
01169 dbus_bool_t
01170 bus_connections_check_limits (BusConnections  *connections,
01171                               DBusConnection  *requesting_completion,
01172                               DBusError       *error)
01173 {
01174   BusConnectionData *d;
01175   unsigned long uid;
01176   
01177   d = BUS_CONNECTION_DATA (requesting_completion);
01178   _dbus_assert (d != NULL);
01179 
01180   _dbus_assert (d->name == NULL);
01181 
01182   if (connections->n_completed >=
01183       bus_context_get_max_completed_connections (connections->context))
01184     {
01185       dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
01186                       "The maximum number of active connections has been reached");
01187       return FALSE;
01188     }
01189   
01190   if (dbus_connection_get_unix_user (requesting_completion, &uid))
01191     {
01192       if (get_connections_for_uid (connections, uid) >=
01193           bus_context_get_max_connections_per_user (connections->context))
01194         {
01195           dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
01196                           "The maximum number of active connections for UID %lu has been reached",
01197                           uid);
01198           return FALSE;
01199         }
01200     }
01201   
01202   return TRUE;
01203 }
01204 
01205 
01206 /*
01207  * Transactions
01208  *
01209  * Note that this is fairly fragile; in particular, don't try to use
01210  * one transaction across any main loop iterations.
01211  */
01212 
01213 typedef struct
01214 {
01215   BusTransaction *transaction;
01216   DBusMessage    *message;
01217   DBusPreallocatedSend *preallocated;
01218 } MessageToSend;
01219 
01220 typedef struct
01221 {
01222   BusTransactionCancelFunction cancel_function;
01223   DBusFreeFunction free_data_function;
01224   void *data;
01225 } CancelHook;
01226 
01227 struct BusTransaction
01228 {
01229   DBusList *connections;
01230   BusContext *context;
01231   DBusList *cancel_hooks;
01232 };
01233 
01234 static void
01235 message_to_send_free (DBusConnection *connection,
01236                       MessageToSend  *to_send)
01237 {
01238   if (to_send->message)
01239     dbus_message_unref (to_send->message);
01240 
01241   if (to_send->preallocated)
01242     dbus_connection_free_preallocated_send (connection, to_send->preallocated);
01243 
01244   dbus_free (to_send);
01245 }
01246 
01247 static void
01248 cancel_hook_cancel (void *element,
01249                     void *data)
01250 {
01251   CancelHook *ch = element;
01252 
01253   _dbus_verbose ("Running transaction cancel hook\n");
01254   
01255   if (ch->cancel_function)
01256     (* ch->cancel_function) (ch->data);  
01257 }
01258 
01259 static void
01260 cancel_hook_free (void *element,
01261                   void *data)
01262 {
01263   CancelHook *ch = element;
01264 
01265   if (ch->free_data_function)
01266     (* ch->free_data_function) (ch->data);
01267 
01268   dbus_free (ch);
01269 }
01270 
01271 static void
01272 free_cancel_hooks (BusTransaction *transaction)
01273 {
01274   _dbus_list_foreach (&transaction->cancel_hooks,
01275                       cancel_hook_free, NULL);
01276   
01277   _dbus_list_clear (&transaction->cancel_hooks);
01278 }
01279 
01280 BusTransaction*
01281 bus_transaction_new (BusContext *context)
01282 {
01283   BusTransaction *transaction;
01284 
01285   transaction = dbus_new0 (BusTransaction, 1);
01286   if (transaction == NULL)
01287     return NULL;
01288 
01289   transaction->context = context;
01290   
01291   return transaction;
01292 }
01293 
01294 BusContext*
01295 bus_transaction_get_context (BusTransaction  *transaction)
01296 {
01297   return transaction->context;
01298 }
01299 
01300 BusConnections*
01301 bus_transaction_get_connections (BusTransaction  *transaction)
01302 {
01303   return bus_context_get_connections (transaction->context);
01304 }
01305 
01306 dbus_bool_t
01307 bus_transaction_send_from_driver (BusTransaction *transaction,
01308                                   DBusConnection *connection,
01309                                   DBusMessage    *message)
01310 {
01311   /* We have to set the sender to the driver, and have
01312    * to check security policy since it was not done in
01313    * dispatch.c
01314    */
01315   _dbus_verbose ("Sending %s from driver\n",
01316                  dbus_message_get_name (message));
01317   
01318   if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
01319     return FALSE;
01320 
01321   /* If security policy doesn't allow the message, we silently
01322    * eat it; the driver doesn't care about getting a reply.
01323    */
01324   if (!bus_context_check_security_policy (bus_transaction_get_context (transaction),
01325                                           NULL, connection, message, NULL))
01326     return TRUE;
01327 
01328   return bus_transaction_send (transaction, connection, message);
01329 }
01330 
01331 dbus_bool_t
01332 bus_transaction_send (BusTransaction *transaction,
01333                       DBusConnection *connection,
01334                       DBusMessage    *message)
01335 {
01336   MessageToSend *to_send;
01337   BusConnectionData *d;
01338   DBusList *link;
01339 
01340   _dbus_verbose ("  trying to add %s %s to transaction%s\n",
01341                  dbus_message_get_is_error (message) ? "error" :
01342                  dbus_message_get_reply_serial (message) != 0 ? "reply" :
01343                  "message",
01344                  dbus_message_get_name (message),
01345                  dbus_connection_get_is_connected (connection) ?
01346                  "" : " (disconnected)");
01347 
01348   _dbus_assert (dbus_message_get_sender (message) != NULL);
01349   
01350   if (!dbus_connection_get_is_connected (connection))
01351     return TRUE; /* silently ignore disconnected connections */
01352   
01353   d = BUS_CONNECTION_DATA (connection);
01354   _dbus_assert (d != NULL);
01355   
01356   to_send = dbus_new (MessageToSend, 1);
01357   if (to_send == NULL)
01358     {
01359       return FALSE;
01360     }
01361 
01362   to_send->preallocated = dbus_connection_preallocate_send (connection);
01363   if (to_send->preallocated == NULL)
01364     {
01365       dbus_free (to_send);
01366       return FALSE;
01367     }  
01368   
01369   dbus_message_ref (message);
01370   to_send->message = message;
01371   to_send->transaction = transaction;
01372 
01373   _dbus_verbose ("about to prepend message\n");
01374   
01375   if (!_dbus_list_prepend (&d->transaction_messages, to_send))
01376     {
01377       message_to_send_free (connection, to_send);
01378       return FALSE;
01379     }
01380 
01381   _dbus_verbose ("prepended message\n");
01382   
01383   /* See if we already had this connection in the list
01384    * for this transaction. If we have a pending message,
01385    * then we should already be in transaction->connections
01386    */
01387   link = _dbus_list_get_first_link (&d->transaction_messages);
01388   _dbus_assert (link->data == to_send);
01389   link = _dbus_list_get_next_link (&d->transaction_messages, link);
01390   while (link != NULL)
01391     {
01392       MessageToSend *m = link->data;
01393       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
01394       
01395       if (m->transaction == transaction)
01396         break;
01397         
01398       link = next;
01399     }
01400 
01401   if (link == NULL)
01402     {
01403       if (!_dbus_list_prepend (&transaction->connections, connection))
01404         {
01405           _dbus_list_remove (&d->transaction_messages, to_send);
01406           message_to_send_free (connection, to_send);
01407           return FALSE;
01408         }
01409     }
01410 
01411   return TRUE;
01412 }
01413 
01414 static void
01415 connection_cancel_transaction (DBusConnection *connection,
01416                                BusTransaction *transaction)
01417 {
01418   DBusList *link;
01419   BusConnectionData *d;
01420   
01421   d = BUS_CONNECTION_DATA (connection);
01422   _dbus_assert (d != NULL);
01423   
01424   link = _dbus_list_get_first_link (&d->transaction_messages);
01425   while (link != NULL)
01426     {
01427       MessageToSend *m = link->data;
01428       DBusList *next = _dbus_list_get_next_link (&d->transaction_messages, link);
01429       
01430       if (m->transaction == transaction)
01431         {
01432           _dbus_list_remove_link (&d->transaction_messages,
01433                                   link);
01434           
01435           message_to_send_free (connection, m);
01436         }
01437         
01438       link = next;
01439     }
01440 }
01441 
01442 void
01443 bus_transaction_cancel_and_free (BusTransaction *transaction)
01444 {
01445   DBusConnection *connection;
01446 
01447   _dbus_verbose ("TRANSACTION: cancelled\n");
01448   
01449   while ((connection = _dbus_list_pop_first (&transaction->connections)))
01450     connection_cancel_transaction (connection, transaction);
01451 
01452   _dbus_assert (transaction->connections == NULL);
01453 
01454   _dbus_list_foreach (&transaction->cancel_hooks,
01455                       cancel_hook_cancel, NULL);
01456 
01457   free_cancel_hooks (transaction);
01458   
01459   dbus_free (transaction);
01460 }
01461 
01462 static void
01463 connection_execute_transaction (DBusConnection *connection,
01464                                 BusTransaction *transaction)
01465 {
01466   DBusList *link;
01467   BusConnectionData *d;
01468   
01469   d = BUS_CONNECTION_DATA (connection);
01470   _dbus_assert (d != NULL);
01471 
01472   /* Send the queue in order (FIFO) */
01473   link = _dbus_list_get_last_link (&d->transaction_messages);
01474   while (link != NULL)
01475     {
01476       MessageToSend *m = link->data;
01477       DBusList *prev = _dbus_list_get_prev_link (&d->transaction_messages, link);
01478       
01479       if (m->transaction == transaction)
01480         {
01481           _dbus_list_remove_link (&d->transaction_messages,
01482                                   link);
01483 
01484           _dbus_assert (dbus_message_get_sender (m->message) != NULL);
01485           
01486           dbus_connection_send_preallocated (connection,
01487                                              m->preallocated,
01488                                              m->message,
01489                                              NULL);
01490 
01491           m->preallocated = NULL; /* so we don't double-free it */
01492           
01493           message_to_send_free (connection, m);
01494         }
01495         
01496       link = prev;
01497     }
01498 }
01499 
01500 void
01501 bus_transaction_execute_and_free (BusTransaction *transaction)
01502 {
01503   /* For each connection in transaction->connections
01504    * send the messages
01505    */
01506   DBusConnection *connection;
01507 
01508   _dbus_verbose ("TRANSACTION: executing\n");
01509   
01510   while ((connection = _dbus_list_pop_first (&transaction->connections)))
01511     connection_execute_transaction (connection, transaction);
01512 
01513   _dbus_assert (transaction->connections == NULL);
01514 
01515   free_cancel_hooks (transaction);
01516   
01517   dbus_free (transaction);
01518 }
01519 
01520 static void
01521 bus_connection_remove_transactions (DBusConnection *connection)
01522 {
01523   MessageToSend *to_send;
01524   BusConnectionData *d;
01525   
01526   d = BUS_CONNECTION_DATA (connection);
01527   _dbus_assert (d != NULL);
01528   
01529   while ((to_send = _dbus_list_get_first (&d->transaction_messages)))
01530     {
01531       /* only has an effect for the first MessageToSend listing this transaction */
01532       _dbus_list_remove (&to_send->transaction->connections,
01533                          connection);
01534 
01535       _dbus_list_remove (&d->transaction_messages, to_send);
01536       message_to_send_free (connection, to_send);
01537     }
01538 }
01539 
01543 dbus_bool_t
01544 bus_transaction_send_error_reply (BusTransaction  *transaction,
01545                                   DBusConnection  *connection,
01546                                   const DBusError *error,
01547                                   DBusMessage     *in_reply_to)
01548 {
01549   DBusMessage *reply;
01550   
01551   _dbus_assert (error != NULL);
01552   _DBUS_ASSERT_ERROR_IS_SET (error);
01553 
01554   _dbus_verbose ("Sending error reply %s \"%s\"\n",
01555                  error->name, error->message);
01556 
01557   reply = dbus_message_new_error_reply (in_reply_to,
01558                                         error->name,
01559                                         error->message);
01560   if (reply == NULL)
01561     return FALSE;
01562 
01563   if (!bus_transaction_send_from_driver (transaction, connection, reply))
01564     {
01565       dbus_message_unref (reply);
01566       return FALSE;
01567     }
01568 
01569   dbus_message_unref (reply);
01570   
01571   return TRUE;
01572 }
01573 
01574 dbus_bool_t
01575 bus_transaction_add_cancel_hook (BusTransaction               *transaction,
01576                                  BusTransactionCancelFunction  cancel_function,
01577                                  void                         *data,
01578                                  DBusFreeFunction              free_data_function)
01579 {
01580   CancelHook *ch;
01581 
01582   ch = dbus_new (CancelHook, 1);
01583   if (ch == NULL)
01584     return FALSE;
01585   
01586   ch->cancel_function = cancel_function;
01587   ch->data = data;
01588   ch->free_data_function = free_data_function;
01589 
01590   /* It's important that the hooks get run in reverse order that they
01591    * were added
01592    */
01593   if (!_dbus_list_prepend (&transaction->cancel_hooks, ch))
01594     {
01595       dbus_free (ch);
01596       return FALSE;
01597     }
01598 
01599   return TRUE;
01600 }

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