Home | History | Annotate | Download | only in dbus
      1 /* -*- mode: C; c-file-style: "gnu" -*- */
      2 /* dbus-server-debug-pipe.c In-proc debug server implementation
      3  *
      4  * Copyright (C) 2003  CodeFactory AB
      5  * Copyright (C) 2003, 2004  Red Hat, Inc.
      6  *
      7  * Licensed under the Academic Free License version 2.1
      8  *
      9  * This program is free software; you can redistribute it and/or modify
     10  * it under the terms of the GNU General Public License as published by
     11  * the Free Software Foundation; either version 2 of the License, or
     12  * (at your option) any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License
     20  * along with this program; if not, write to the Free Software
     21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     22  *
     23  */
     24 
     25 #include "dbus-internals.h"
     26 #include "dbus-server-debug-pipe.h"
     27 #include "dbus-transport-socket.h"
     28 #include "dbus-connection-internal.h"
     29 #include "dbus-hash.h"
     30 #include "dbus-string.h"
     31 #include "dbus-protocol.h"
     32 
     33 #ifdef DBUS_BUILD_TESTS
     34 
     35 /**
     36  * @defgroup DBusServerDebugPipe DBusServerDebugPipe
     37  * @ingroup  DBusInternals
     38  * @brief In-process pipe debug server used in unit tests.
     39  *
     40  * Types and functions related to DBusServerDebugPipe.
     41  * This is used for unit testing.
     42  *
     43  * @{
     44  */
     45 
     46 /**
     47  * Opaque object representing a debug server implementation.
     48  */
     49 typedef struct DBusServerDebugPipe DBusServerDebugPipe;
     50 
     51 /**
     52  * Implementation details of DBusServerDebugPipe. All members
     53  * are private.
     54  */
     55 struct DBusServerDebugPipe
     56 {
     57   DBusServer base;  /**< Parent class members. */
     58 
     59   char *name; /**< Server name. */
     60 
     61   dbus_bool_t disconnected; /**< TRUE if disconnect has been called */
     62 };
     63 
     64 /* FIXME not threadsafe (right now the test suite doesn't use threads anyhow ) */
     65 static DBusHashTable *server_pipe_hash;
     66 static int server_pipe_hash_refcount = 0;
     67 
     68 static dbus_bool_t
     69 pipe_hash_ref (void)
     70 {
     71   if (!server_pipe_hash)
     72     {
     73       _dbus_assert (server_pipe_hash_refcount == 0);
     74 
     75       server_pipe_hash = _dbus_hash_table_new (DBUS_HASH_STRING, NULL, NULL);
     76 
     77       if (!server_pipe_hash)
     78         return FALSE;
     79     }
     80 
     81   server_pipe_hash_refcount = 1;
     82 
     83   return TRUE;
     84 }
     85 
     86 static void
     87 pipe_hash_unref (void)
     88 {
     89   _dbus_assert (server_pipe_hash != NULL);
     90   _dbus_assert (server_pipe_hash_refcount > 0);
     91 
     92   server_pipe_hash_refcount -= 1;
     93   if (server_pipe_hash_refcount == 0)
     94     {
     95       _dbus_hash_table_unref (server_pipe_hash);
     96       server_pipe_hash = NULL;
     97     }
     98 }
     99 
    100 static void
    101 debug_finalize (DBusServer *server)
    102 {
    103   DBusServerDebugPipe *debug_server = (DBusServerDebugPipe*) server;
    104 
    105   pipe_hash_unref ();
    106 
    107   _dbus_server_finalize_base (server);
    108 
    109   dbus_free (debug_server->name);
    110   dbus_free (server);
    111 }
    112 
    113 static void
    114 debug_disconnect (DBusServer *server)
    115 {
    116   ((DBusServerDebugPipe*)server)->disconnected = TRUE;
    117 }
    118 
    119 static DBusServerVTable debug_vtable = {
    120   debug_finalize,
    121   debug_disconnect
    122 };
    123 
    124 /**
    125  * Creates a new debug server using an in-process pipe
    126  *
    127  * @param server_name the name of the server.
    128  * @param error address where an error can be returned.
    129  * @returns a new server, or #NULL on failure.
    130  */
    131 DBusServer*
    132 _dbus_server_debug_pipe_new (const char     *server_name,
    133                              DBusError      *error)
    134 {
    135   DBusServerDebugPipe *debug_server;
    136   DBusString address;
    137   DBusString name_str;
    138 
    139   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    140 
    141   if (!pipe_hash_ref ())
    142     return NULL;
    143 
    144   if (_dbus_hash_table_lookup_string (server_pipe_hash, server_name) != NULL)
    145     {
    146       dbus_set_error (error, DBUS_ERROR_ADDRESS_IN_USE, NULL);
    147       pipe_hash_unref ();
    148       return NULL;
    149     }
    150 
    151   debug_server = dbus_new0 (DBusServerDebugPipe, 1);
    152   if (debug_server == NULL)
    153     goto nomem_0;
    154 
    155   if (!_dbus_string_init (&address))
    156     goto nomem_1;
    157 
    158   _dbus_string_init_const (&name_str, server_name);
    159   if (!_dbus_string_append (&address, "debug-pipe:name=") ||
    160       !_dbus_address_append_escaped (&address, &name_str))
    161     goto nomem_2;
    162 
    163   debug_server->name = _dbus_strdup (server_name);
    164   if (debug_server->name == NULL)
    165     goto nomem_2;
    166 
    167   if (!_dbus_server_init_base (&debug_server->base,
    168 			       &debug_vtable, &address))
    169     goto nomem_3;
    170 
    171   if (!_dbus_hash_table_insert_string (server_pipe_hash,
    172 				       debug_server->name,
    173 				       debug_server))
    174     goto nomem_4;
    175 
    176   _dbus_string_free (&address);
    177 
    178   /* server keeps the pipe hash ref */
    179 
    180   return (DBusServer *)debug_server;
    181 
    182  nomem_4:
    183   _dbus_server_finalize_base (&debug_server->base);
    184  nomem_3:
    185   dbus_free (debug_server->name);
    186  nomem_2:
    187   _dbus_string_free (&address);
    188  nomem_1:
    189   dbus_free (debug_server);
    190  nomem_0:
    191   pipe_hash_unref ();
    192   dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    193   return NULL;
    194 }
    195 
    196 /**
    197  * Creates the client-side transport for
    198  * a debug-pipe connection connected to the
    199  * given debug-pipe server name.
    200  *
    201  * @param server_name name of server to connect to
    202  * @param error address where an error can be returned.
    203  * @returns #NULL on no memory or transport
    204  */
    205 DBusTransport*
    206 _dbus_transport_debug_pipe_new (const char     *server_name,
    207                                 DBusError      *error)
    208 {
    209   DBusTransport *client_transport;
    210   DBusTransport *server_transport;
    211   DBusConnection *connection;
    212   int client_fd, server_fd;
    213   DBusServer *server;
    214   DBusString address;
    215 
    216   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    217 
    218   if (server_pipe_hash == NULL)
    219     {
    220       dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL);
    221       return NULL;
    222     }
    223 
    224   server = _dbus_hash_table_lookup_string (server_pipe_hash,
    225                                            server_name);
    226   if (server == NULL ||
    227       ((DBusServerDebugPipe*)server)->disconnected)
    228     {
    229       dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL);
    230       return NULL;
    231     }
    232 
    233   if (!_dbus_string_init (&address))
    234     {
    235       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    236       return NULL;
    237     }
    238 
    239   if (!_dbus_string_append (&address, "debug-pipe:name=") ||
    240       !_dbus_string_append (&address, server_name))
    241     {
    242       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    243       _dbus_string_free (&address);
    244       return NULL;
    245     }
    246 
    247   if (!_dbus_full_duplex_pipe (&client_fd, &server_fd, FALSE,
    248                                NULL))
    249     {
    250       _dbus_verbose ("failed to create full duplex pipe\n");
    251       dbus_set_error (error, DBUS_ERROR_FAILED, "Could not create full-duplex pipe");
    252       _dbus_string_free (&address);
    253       return NULL;
    254     }
    255 
    256   _dbus_fd_set_close_on_exec (client_fd);
    257   _dbus_fd_set_close_on_exec (server_fd);
    258 
    259   client_transport = _dbus_transport_new_for_socket (client_fd,
    260                                                      NULL, &address);
    261   if (client_transport == NULL)
    262     {
    263       _dbus_close_socket (client_fd, NULL);
    264       _dbus_close_socket (server_fd, NULL);
    265       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    266       _dbus_string_free (&address);
    267       return NULL;
    268     }
    269 
    270   _dbus_string_free (&address);
    271 
    272   client_fd = -1;
    273 
    274   server_transport = _dbus_transport_new_for_socket (server_fd,
    275                                                      &server->guid_hex, NULL);
    276   if (server_transport == NULL)
    277     {
    278       _dbus_transport_unref (client_transport);
    279       _dbus_close_socket (server_fd, NULL);
    280       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    281       return NULL;
    282     }
    283 
    284   server_fd = -1;
    285 
    286   if (!_dbus_transport_set_auth_mechanisms (server_transport,
    287                                             (const char**) server->auth_mechanisms))
    288     {
    289       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    290       _dbus_transport_unref (server_transport);
    291       _dbus_transport_unref (client_transport);
    292       return NULL;
    293     }
    294 
    295   connection = _dbus_connection_new_for_transport (server_transport);
    296   _dbus_transport_unref (server_transport);
    297   server_transport = NULL;
    298 
    299   if (connection == NULL)
    300     {
    301       _dbus_transport_unref (client_transport);
    302       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
    303       return NULL;
    304     }
    305 
    306   /* See if someone wants to handle this new connection,
    307    * self-referencing for paranoia
    308    */
    309   if (server->new_connection_function)
    310     {
    311       dbus_server_ref (server);
    312       (* server->new_connection_function) (server, connection,
    313                                            server->new_connection_data);
    314       dbus_server_unref (server);
    315     }
    316 
    317   /* If no one grabbed a reference, the connection will die,
    318    * and the client transport will get an immediate disconnect
    319    */
    320   _dbus_connection_close_if_only_one_ref (connection);
    321   dbus_connection_unref (connection);
    322 
    323   return client_transport;
    324 }
    325 
    326 /**
    327  * Tries to interpret the address entry as a debug pipe entry.
    328  *
    329  * Sets error if the result is not OK.
    330  *
    331  * @param entry an address entry
    332  * @param server_p location to store a new DBusServer, or #NULL on failure.
    333  * @param error location to store rationale for failure on bad address
    334  * @returns the outcome
    335  *
    336  */
    337 DBusServerListenResult
    338 _dbus_server_listen_debug_pipe (DBusAddressEntry *entry,
    339                                 DBusServer      **server_p,
    340                                 DBusError        *error)
    341 {
    342   const char *method;
    343 
    344   *server_p = NULL;
    345 
    346   method = dbus_address_entry_get_method (entry);
    347 
    348   if (strcmp (method, "debug-pipe") == 0)
    349     {
    350       const char *name = dbus_address_entry_get_value (entry, "name");
    351 
    352       if (name == NULL)
    353         {
    354           _dbus_set_bad_address(error, "debug-pipe", "name",
    355                                 NULL);
    356           return DBUS_SERVER_LISTEN_BAD_ADDRESS;
    357         }
    358 
    359       *server_p = _dbus_server_debug_pipe_new (name, error);
    360 
    361       if (*server_p)
    362         {
    363           _DBUS_ASSERT_ERROR_IS_CLEAR(error);
    364           return DBUS_SERVER_LISTEN_OK;
    365         }
    366       else
    367         {
    368           _DBUS_ASSERT_ERROR_IS_SET(error);
    369           return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
    370         }
    371     }
    372   else
    373     {
    374       _DBUS_ASSERT_ERROR_IS_CLEAR(error);
    375       return DBUS_SERVER_LISTEN_NOT_HANDLED;
    376     }
    377 }
    378 
    379 /**
    380  * Opens a debug pipe transport, used in the test suite.
    381  *
    382  * @param entry the address entry to try opening as debug-pipe
    383  * @param transport_p return location for the opened transport
    384  * @param error error to be set
    385  * @returns result of the attempt
    386  */
    387 DBusTransportOpenResult
    388 _dbus_transport_open_debug_pipe (DBusAddressEntry  *entry,
    389                                  DBusTransport    **transport_p,
    390                                  DBusError         *error)
    391 {
    392   const char *method;
    393 
    394   method = dbus_address_entry_get_method (entry);
    395   _dbus_assert (method != NULL);
    396 
    397   if (strcmp (method, "debug-pipe") == 0)
    398     {
    399       const char *name = dbus_address_entry_get_value (entry, "name");
    400 
    401       if (name == NULL)
    402         {
    403           _dbus_set_bad_address (error, "debug-pipe", "name",
    404                                  NULL);
    405           return DBUS_TRANSPORT_OPEN_BAD_ADDRESS;
    406         }
    407 
    408       *transport_p = _dbus_transport_debug_pipe_new (name, error);
    409 
    410       if (*transport_p == NULL)
    411         {
    412           _DBUS_ASSERT_ERROR_IS_SET (error);
    413           return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT;
    414         }
    415       else
    416         {
    417           _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    418           return DBUS_TRANSPORT_OPEN_OK;
    419         }
    420     }
    421   else
    422     {
    423       _DBUS_ASSERT_ERROR_IS_CLEAR (error);
    424       return DBUS_TRANSPORT_OPEN_NOT_HANDLED;
    425     }
    426 }
    427 
    428 
    429 /** @} */
    430 
    431 #endif /* DBUS_BUILD_TESTS */
    432 
    433