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