(linenum→info "unix/slp.c:2238")

dbus/1.0.2/bus/connection.c

    1: /* -*- mode: C; c-file-style: "gnu" -*- */
    2: /* connection.c  Client connections
    3:  *
    4:  * Copyright (C) 2003  Red Hat, Inc.
    5:  *
    6:  * Licensed under the Academic Free License version 2.1
    7:  * 
    8:  * This program is free software; you can redistribute it and/or modify
    9:  * it under the terms of the GNU General Public License as published by
   10:  * the Free Software Foundation; either version 2 of the License, or
   11:  * (at your option) any later version.
   12:  *
   13:  * This program is distributed in the hope that it will be useful,
   14:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16:  * GNU General Public License for more details.
   17:  * 
   18:  * You should have received a copy of the GNU General Public License
   19:  * along with this program; if not, write to the Free Software
   20:  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   21:  *
   22:  */
   23: #include "connection.h"
   24: #include "dispatch.h"
   25: #include "policy.h"
   26: #include "services.h"
   27: #include "utils.h"
   28: #include "signals.h"
   29: #include "expirelist.h"
   30: #include "selinux.h"
   31: #include <dbus/dbus-list.h>
   32: #include <dbus/dbus-hash.h>
   33: #include <dbus/dbus-timeout.h>
   34: 
   35: static void bus_connection_remove_transactions (DBusConnection *connection);
   36: 
   37: typedef struct
   38: {
   39:   BusExpireItem expire_item;
   40: 
   41:   DBusConnection *will_get_reply;
   42:   DBusConnection *will_send_reply;
   43: 
   44:   dbus_uint32_t reply_serial;
   45:   
   46: } BusPendingReply;
   47: 
   48: struct BusConnections
   49: {
   50:   int refcount;
   51:   DBusList *completed;  /**< List of all completed connections */
   52:   int n_completed;      /**< Length of completed list */
   53:   DBusList *incomplete; /**< List of all not-yet-active connections */
   54:   int n_incomplete;     /**< Length of incomplete list */
   55:   BusContext *context;
   56:   DBusHashTable *completed_by_user; /**< Number of completed connections for each UID */
   57:   DBusTimeout *expire_timeout; /**< Timeout for expiring incomplete connections. */
   58:   int stamp;                   /**< Incrementing number */
   59:   BusExpireList *pending_replies; /**< List of pending replies */
   60: };
   61: 
   62: static dbus_int32_t connection_data_slot = -1;
   63: 
   64: typedef struct
   65: {
   66:   BusConnections *connections;
   67:   DBusList *link_in_connection_list;
   68:   DBusConnection *connection;
   69:   DBusList *services_owned;
   70:   int n_services_owned;
   71:   DBusList *match_rules;
   72:   int n_match_rules;
   73:   char *name;
   74:   DBusList *transaction_messages; /**< Stuff we need to send as part of a transaction */
   75:   DBusMessage *oom_message;
   76:   DBusPreallocatedSend *oom_preallocated;
   77:   BusClientPolicy *policy;
   78: 
   79:   BusSELinuxID *selinux_id;
   80: 
   81:   long connection_tv_sec;  /**< Time when we connected (seconds component) */
   82:   long connection_tv_usec; /**< Time when we connected (microsec component) */
   83:   int stamp;               /**< connections->stamp last time we were traversed */
   84: } BusConnectionData;
   85: 
   86: static dbus_bool_t bus_pending_reply_expired (BusExpireList *list,
   87:                                               DBusList      *link,
   88:                                               void          *data);
   89: 
   90: static void bus_connection_drop_pending_replies (BusConnections  *connections,
   91:                                                  DBusConnection  *connection);
   92: 
   93: static dbus_bool_t expire_incomplete_timeout (void *data);
   94: 
   95: #define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
   96: 
   97: static DBusLoop*
   98: connection_get_loop (DBusConnection *connection)
   99: {
  100:   BusConnectionData *d;
  101: 
  102:   d = BUS_CONNECTION_DATA (connection);
  103: 
  104:   return bus_context_get_loop (d->connections->context);
  105: }
  106: 
  107: 
  108: static int
  109: get_connections_for_uid (BusConnections *connections,
  110:                          dbus_uid_t      uid)
  111: {
  112:   void *val;
  113:   int current_count;
  114: 
  115:   /* val is NULL is 0 when it isn't in the hash yet */
  116:   
  117:   val = _dbus_hash_table_lookup_ulong (connections->completed_by_user,
  118:                                        uid);
  119: 
  120:   current_count = _DBUS_POINTER_TO_INT (val);
  121: 
  122:   return current_count;
  123: }
  124: 
  125: static dbus_bool_t
  126: adjust_connections_for_uid (BusConnections *connections,
  127:                             dbus_uid_t      uid,
  128:                             int             adjustment)
  129: {
  130:   int current_count;
  131: 
  132:   current_count = get_connections_for_uid (connections, uid);
  133: 
  134:   _dbus_verbose ("Adjusting connection count for UID " DBUS_UID_FORMAT
  135:                  ": was %d adjustment %d making %d\n",
  136:                  uid, current_count, adjustment, current_count + adjustment);
  137:   
  138:   _dbus_assert (current_count >= 0);
  139:   
  140:   current_count += adjustment;
  141: 
  142:   _dbus_assert (current_count >= 0);
  143: 
  144:   if (current_count == 0)
  145:     {
  146:       _dbus_hash_table_remove_ulong (connections->completed_by_user, uid);
  147:       return TRUE;
  148:     }
  149:   else
  150:     {
  151:       dbus_bool_t retval;
  152:       
  153:       retval = _dbus_hash_table_insert_ulong (connections->completed_by_user,
  154:                                               uid, _DBUS_INT_TO_POINTER (current_count));
  155: 
  156:       /* only positive adjustment can fail as otherwise
  157:        * a hash entry should already exist
  158:        */
  159:       _dbus_assert (adjustment > 0 ||
  160:                     (adjustment <= 0 && retval));
  161: 
  162:       return retval;
  163:     }
  164: }
  165: 
  166: void
  167: bus_connection_disconnected (DBusConnection *connection)
  168: {
  169:   BusConnectionData *d;
  170:   BusService *service;
  171:   BusMatchmaker *matchmaker;
  172:   
  173:   d = BUS_CONNECTION_DATA (connection);
  174:   _dbus_assert (d != NULL);
  175: 
  176:   _dbus_verbose ("%s disconnected, dropping all service ownership and releasing\n",
  177:                  d->name ? d->name : "(inactive)");
  178: 
  179:   /* Delete our match rules */
  180:   if (d->n_match_rules > 0)
  181:     {
  182:       matchmaker = bus_context_get_matchmaker (d->connections->context);
  183:       bus_matchmaker_disconnected (matchmaker, connection);
  184:     }
  185:   
  186:   /* Drop any service ownership. Unfortunately, this requires
  187:    * memory allocation and there doesn't seem to be a good way to
  188:    * handle it other than sleeping; we can't "fail" the operation of
  189:    * disconnecting a client, and preallocating a broadcast "service is
  190:    * now gone" message for every client-service pair seems kind of
  191:    * involved.
  192:    */
  193:   while ((service = _dbus_list_get_last (&d->services_owned)))
  194:     {
  195:       BusTransaction *transaction;
  196:       DBusError error;
  197: 
  198:     retry:
  199:       
  200:       dbus_error_init (&error);
  201:         
  202:       while ((transaction = bus_transaction_new (d->connections->context)) == NULL)
  203:         _dbus_wait_for_memory ();
  204:         
  205:       if (!bus_service_remove_owner (service, connection,
  206:                                      transaction, &error))
  207:         {
  208:           _DBUS_ASSERT_ERROR_IS_SET (&error);
  209:           
  210:           if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
  211:             {
  212:               dbus_error_free (&error);
  213:               bus_transaction_cancel_and_free (transaction);
  214:               _dbus_wait_for_memory ();
  215:               goto retry;
  216:             }
  217:           else
  218:             {
  219:               _dbus_verbose ("Failed to remove service owner: %s %s\n",
  220:                              error.name, error.message);
  221:               _dbus_assert_not_reached ("Removing service owner failed for non-memory-related reason");
  222:             }
  223:         }
  224:         
  225:       bus_transaction_execute_and_free (transaction);
  226:     }
  227: 
  228:   bus_dispatch_remove_connection (connection);
  229:   
  230:   /* no more watching */
  231:   if (!dbus_connection_set_watch_functions (connection,
  232:                                             NULL, NULL, NULL,
  233:                                             connection,
  234:                                             NULL))
  235:     _dbus_assert_not_reached ("setting watch functions to NULL failed");
  236: 
  237:   if (!dbus_connection_set_timeout_functions (connection,
  238:                                               NULL, NULL, NULL,
  239:                                               connection,
  240:                                               NULL))
  241:     _dbus_assert_not_reached ("setting timeout functions to NULL failed");
  242:   
  243:   dbus_connection_set_unix_user_function (connection,
  244:                                           NULL, NULL, NULL);
  245: 
  246:   dbus_connection_set_dispatch_status_function (connection,
  247:                                                 NULL, NULL, NULL);
  248:   
  249:   bus_connection_remove_transactions (connection);
  250: 
  251:   if (d->link_in_connection_list != NULL)
  252:     {
  253:       if (d->name != NULL)
  254:         {
  255:           unsigned long uid;
  256:           
  257:           _dbus_list_remove_link (&d->connections->completed, d->link_in_connection_list);
  258:           d->link_in_connection_list = NULL;
  259:           d->connections->n_completed -= 1;
  260: 
  261:           if (dbus_connection_get_unix_user (connection, &uid))
  262:             {
  263:               if (!adjust_connections_for_uid (d->connections,
  264:                                                uid, -1))
  265:                 _dbus_assert_not_reached ("adjusting downward should never fail");
  266:             }
  267:         }
  268:       else
  269:         {
  270:           _dbus_list_remove_link (&d->connections->incomplete, d->link_in_connection_list);
  271:           d->link_in_connection_list = NULL;
  272:           d->connections->n_incomplete -= 1;
  273:         }
  274:       
  275:       _dbus_assert (d->connections->n_incomplete >= 0);
  276:       _dbus_assert (d->connections->n_completed >= 0);
  277:     }
  278: 
  279:   bus_connection_drop_pending_replies (d->connections, connection);
  280:   
  281:   /* frees "d" as side effect */
  282:   dbus_connection_set_data (connection,
  283:                             connection_data_slot,
  284:                             NULL, NULL);
  285:   
  286:   dbus_connection_unref (connection);
  287: }
  288: 
  289: static dbus_bool_t
  290: connection_watch_callback (DBusWatch     *watch,
  291:                            unsigned int   condition,
  292:                            void          *data)
  293: {
  294:  /* FIXME this can be done in dbus-mainloop.c
  295:   * if the code in activation.c for the babysitter
  296:   * watch handler is fixed.
  297:   */
  298:   
  299: #if 0
  300:   _dbus_verbose ("Calling handle_watch\n");
  301: #endif
  302:   return dbus_watch_handle (watch, condition);
  303: }
  304: 
  305: static dbus_bool_t
  306: add_connection_watch (DBusWatch      *watch,
  307:                       void           *data)
  308: {
  309:   DBusConnection *connection = data;
  310: 
  311:   return _dbus_loop_add_watch (connection_get_loop (connection),
  312:                                watch, connection_watch_callback, connection,
  313:                                NULL);
  314: }
  315: 
  316: static void
  317: remove_connection_watch (DBusWatch      *watch,
  318:                          void           *data)
  319: {
  320:   DBusConnection *connection = data;
  321:   
  322:   _dbus_loop_remove_watch (connection_get_loop (connection),
  323:                            watch, connection_watch_callback, connection);
  324: }
  325: 
  326: static void
  327: connection_timeout_callback (DBusTimeout   *timeout,
  328:                              void          *data)
  329: {
  330:   /* DBusConnection *connection = data; */
  331: 
  332:   /* can return FALSE on OOM but we just let it fire again later */
  333:   dbus_timeout_handle (timeout);
  334: }
  335: 
  336: static dbus_bool_t
  337: add_connection_timeout (DBusTimeout    *timeout,
  338:                         void           *data)
  339: {
  340:   DBusConnection *connection = data;
  341:   
  342:   return _dbus_loop_add_timeout (connection_get_loop (connection),
  343:                                  timeout, connection_timeout_callback, connection, NULL);
  344: }
  345: 
  346: static void
  347: remove_connection_timeout (DBusTimeout    *timeout,
  348:                            void           *data)
  349: {
  350:   DBusConnection *connection = data;
  351:   
  352:   _dbus_loop_remove_timeout (connection_get_loop (connection),
  353:                              timeout, connection_timeout_callback, connection);
  354: }
  355: 
  356: static void
  357: dispatch_status_function (DBusConnection    *connection,
  358:                           DBusDispatchStatus new_status,
  359:                           void              *data)
  360: {
  361:   DBusLoop *loop = data;
  362:   
  363:   if (new_status != DBUS_DISPATCH_COMPLETE)
  364:     {
  365:       while (!_dbus_loop_queue_dispatch (loop, connection))
  366:         _dbus_wait_for_memory ();
  367:     }
  368: }
  369: 
  370: static dbus_bool_t
  371: allow_user_function (DBusConnection *connection,
  372:                      unsigned long   uid,
  373:                      void           *data)
  374: {
  375:   BusConnectionData *d;
  376:     
  377:   d = BUS_CONNECTION_DATA (connection);
  378: 
  379:   _dbus_assert (d != NULL);
  380:   
  381:   return bus_context_allow_user (d->connections->context, uid);
  382: }
  383: 
  384: static void
  385: free_connection_data (void *data)
  386: {
  387:   BusConnectionData *d = data;
  388: 
  389:   /* services_owned should be NULL since we should be disconnected */
  390:   _dbus_assert (d->services_owned == NULL);
  391:   _dbus_assert (d->n_services_owned == 0);
  392:   /* similarly */
  393:   _dbus_assert (d->transaction_messages == NULL);
  394: 
  395:   if (d->oom_preallocated)
  396:     dbus_connection_free_preallocated_send (d->connection, d->oom_preallocated);
  397: 
  398:   if (d->oom_message)
  399:     dbus_message_unref (d->oom_message);
  400: 
  401:   if (d->policy)
  402:     bus_client_policy_unref (d->policy);
  403: 
  404:   if (d->selinux_id)
  405:     bus_selinux_id_unref (d->selinux_id);
  406:   
  407:   dbus_free (d->name);
  408:   
  409:   dbus_free (d);
  410: }
  411: 
  412: static void
  413: call_timeout_callback (DBusTimeout   *timeout,
  414:                        void          *data)
  415: {
  416:   /* can return FALSE on OOM but we just let it fire again later */
  417:   dbus_timeout_handle (timeout);
  418: }
  419: 
  420: BusConnections*
  421: bus_connections_new (BusContext *context)
  422: {
  423:   BusConnections *connections;
  424: 
  425:   if (!dbus_connection_allocate_data_slot (&connection_data_slot))
  426:     goto failed_0;
  427: 
  428:   connections = dbus_new0 (BusConnections, 1);
  429:   if (connections == NULL)
  430:     goto failed_1;
  431: 
  432:   connections->completed_by_user = _dbus_hash_table_new (DBUS_HASH_ULONG,
  433:                                                          NULL, NULL);
  434:   if (connections->completed_by_user == NULL)
  435:     goto failed_2;
  436: 
  437:   connections->expire_timeout = _dbus_timeout_new (100, /* irrelevant */
  438:                                                    expire_incomplete_timeout,
  439:                                                    connections, NULL);
  440:   if (connections->expire_timeout == NULL)
  441:     goto failed_3;
  442: 
  443:   _dbus_timeout_set_enabled (connections->expire_timeout, FALSE);
  444: 
  445:   connections->pending_replies = bus_expire_list_new (bus_context_get_loop (context),
  446:                                                       bus_context_get_reply_timeout (context),
  447:                                                       bus_pending_reply_expired,
  448:                                                       connections);
  449:   if (connections->pending_replies == NULL)
  450:     goto failed_4;
  451:   
  452:   if (!_dbus_loop_add_timeout (bus_context_get_loop (context),
  453:                                connections->expire_timeout,
  454:                                call_timeout_callback, NULL, NULL))
  455:     goto failed_5;
  456:   
  457:   connections->refcount = 1;
  458:   connections->context = context;
  459:   
  460:   return connections;
  461: 
  462:  failed_5:
  463:   bus_expire_list_free (connections->pending_replies);
  464:  failed_4:
  465:   _dbus_timeout_unref (connections->expire_timeout);
  466:  failed_3:
  467:   _dbus_hash_table_unref (connections->completed_by_user);
  468:  failed_2:
  469:   dbus_free (connections);
  470:  failed_1:
  471:   dbus_connection_free_data_slot (&connection_data_slot);
  472:  failed_0:
  473:   return NULL;
  474: }
  475: 
  476: BusConnections *
  477: bus_connections_ref (BusConnections *connections)
  478: {
  479:   _dbus_assert (connections->refcount > 0);
  480:   connections->refcount += 1;
  481: 
  482:   return connections;
  483: }
  484: 
  485: void
  486: bus_connections_unref (BusConnections *connections)
  487: {
  488:   _dbus_assert (connections->refcount > 0);
  489:   connections->refcount -= 1;
  490:   if (connections->refcount == 0)
  491:     {
  492:       /* drop all incomplete */
  493:       while (connections->incomplete != NULL)
  494:         {
  495:           DBusConnection *connection;
  496: 
  497:           connection = connections->incomplete->data;
  498: 
  499:           dbus_connection_ref (connection);
  500:           dbus_connection_close (connection);
  501:           bus_connection_disconnected (connection);
  502:           dbus_connection_unref (connection);
  503:         }
  504: 
  505:       _dbus_assert (connections->n_incomplete == 0);
  506:       
  507:       /* drop all real connections */
  508:       while (connections->completed != NULL)
  509:         {
  510:           DBusConnection *connection;
  511: 
  512:           connection = connections->completed->data;
  513: 
  514:           dbus_connection_ref (connection);
  515:           dbus_connection_close (connection);
  516:           bus_connection_disconnected (connection);
  517:           dbus_connection_unref (connection);
  518:         }
  519: 
  520:       _dbus_assert (connections->n_completed == 0);
  521: 
  522:       bus_expire_list_free (connections->pending_replies);
  523:       
  524:       _dbus_loop_remove_timeout (bus_context_get_loop (connections->context),
  525:                                  connections->expire_timeout,
  526:                                  call_timeout_callback, NULL);
  527:       
  528:       _dbus_timeout_unref (connections->expire_timeout);
  529:       
  530:       _dbus_hash_table_unref (connections->completed_by_user);
  531:       
  532:       dbus_free (connections);
  533: 
  534:       dbus_connection_free_data_slot (&connection_data_slot);
  535:     }
  536: }
  537: 
  538: dbus_bool_t
  539: bus_connections_setup_connection (BusConnections *connections,
  540:                                   DBusConnection *connection)
  541: {
  542:   BusConnectionData *d;
  543:   dbus_bool_t retval;
  544:   DBusError error;
  545:   
  546:   d = dbus_new0 (BusConnectionData, 1);
  547:   
  548:   if (d == NULL)
  549:     return FALSE;
  550: 
  551:   d->connections = connections;
  552:   d->connection = connection;
  553:   
  554:   _dbus_get_current_time (&d->connection_tv_sec,
  555:                           &d->connection_tv_usec);
  556:   
  557:   _dbus_assert (connection_data_slot >= 0);
  558:   
  559:   if (!dbus_connection_set_data (connection,
  560:                                  connection_data_slot,
  561:                                  d, free_connection_data))
  562:     {
  563:       dbus_free (d);
  564:       return FALSE;
  565:     }
  566: 
  567:   dbus_connection_set_route_peer_messages (connection, TRUE);
  568:   
  569:   retval = FALSE;
  570: 
  571:   dbus_error_init (&error);
  572:   d->selinux_id = bus_selinux_init_connection_id (connection,
  573:                                                   &error);
  574:   if (dbus_error_is_set (&error))
  575:     {
  576:       /* This is a bit bogus because we pretend all errors
  577:        * are OOM; this is done because we know that in bus.c
  578:        * an OOM error disconnects the connection, which is
  579:        * the same thing we want on any other error.
  580:        */
  581:       dbus_error_free (&error);
  582:       goto out;
  583:     }
  584:   
  585:   if (!dbus_connection_set_watch_functions (connection,
  586:                                             add_connection_watch,
  587:                                             remove_connection_watch,
  588:                                             NULL,
  589:                                             connection,
  590:                                             NULL))
  591:     goto out;
  592:   
  593:   if (!dbus_connection_set_timeout_functions (connection,
  594:                                               add_connection_timeout,
  595:                                               remove_connection_timeout,
  596:                                               NULL,
  597:                                               connection, NULL))
  598:     goto out;
  599:   
  600:   dbus_connection_set_unix_user_function (connection,
  601:                                           allow_user_function,
  602:                                           NULL, NULL);
  603: 
  604:   dbus_connection_set_dispatch_status_function (connection,
  605:                                                 dispatch_status_function,
  606:                                                 bus_context_get_loop (connections->context),
  607:                                                 NULL);
  608: 
  609:   d->link_in_connection_list = _dbus_list_alloc_link (connection);
  610:   if (d->link_in_connection_list == NULL)
  611:     goto out;
  612:   
  613:   /* Setup the connection with the dispatcher */
  614:   if (!bus_dispatch_add_connection (connection))
  615:     goto out;
  616: 
  617:   if (dbus_connection_get_dispatch_status (connection) != DBUS_DISPATCH_COMPLETE