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 return (DBusServer *)debug_server; 182 183 nomem_4: 184 _dbus_server_finalize_base (&debug_server->base); 185 nomem_3: 186 dbus_free (debug_server->name); 187 nomem_2: 188 _dbus_string_free (&address); 189 nomem_1: 190 dbus_free (debug_server); 191 nomem_0: 192 pipe_hash_unref (); 193 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 194 return NULL; 195 } 196 197 /** 198 * Creates the client-side transport for 199 * a debug-pipe connection connected to the 200 * given debug-pipe server name. 201 * 202 * @param server_name name of server to connect to 203 * @param error address where an error can be returned. 204 * @returns #NULL on no memory or transport 205 */ 206 DBusTransport* 207 _dbus_transport_debug_pipe_new (const char *server_name, 208 DBusError *error) 209 { 210 DBusTransport *client_transport; 211 DBusTransport *server_transport; 212 DBusConnection *connection; 213 int client_fd, server_fd; 214 DBusServer *server; 215 DBusString address; 216 217 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 218 219 if (server_pipe_hash == NULL) 220 { 221 dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL); 222 return NULL; 223 } 224 225 server = _dbus_hash_table_lookup_string (server_pipe_hash, 226 server_name); 227 if (server == NULL || 228 ((DBusServerDebugPipe*)server)->disconnected) 229 { 230 dbus_set_error (error, DBUS_ERROR_NO_SERVER, NULL); 231 return NULL; 232 } 233 234 if (!_dbus_string_init (&address)) 235 { 236 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 237 return NULL; 238 } 239 240 if (!_dbus_string_append (&address, "debug-pipe:name=") || 241 !_dbus_string_append (&address, server_name)) 242 { 243 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 244 _dbus_string_free (&address); 245 return NULL; 246 } 247 248 if (!_dbus_full_duplex_pipe (&client_fd, &server_fd, FALSE, 249 NULL)) 250 { 251 _dbus_verbose ("failed to create full duplex pipe\n"); 252 dbus_set_error (error, DBUS_ERROR_FAILED, "Could not create full-duplex pipe"); 253 _dbus_string_free (&address); 254 return NULL; 255 } 256 257 client_transport = _dbus_transport_new_for_socket (client_fd, 258 NULL, &address); 259 if (client_transport == NULL) 260 { 261 _dbus_close_socket (client_fd, NULL); 262 _dbus_close_socket (server_fd, NULL); 263 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 264 _dbus_string_free (&address); 265 return NULL; 266 } 267 268 _dbus_string_free (&address); 269 270 client_fd = -1; 271 272 server_transport = _dbus_transport_new_for_socket (server_fd, 273 &server->guid_hex, NULL); 274 if (server_transport == NULL) 275 { 276 _dbus_transport_unref (client_transport); 277 _dbus_close_socket (server_fd, NULL); 278 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 279 return NULL; 280 } 281 282 server_fd = -1; 283 284 if (!_dbus_transport_set_auth_mechanisms (server_transport, 285 (const char**) server->auth_mechanisms)) 286 { 287 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 288 _dbus_transport_unref (server_transport); 289 _dbus_transport_unref (client_transport); 290 return NULL; 291 } 292 293 connection = _dbus_connection_new_for_transport (server_transport); 294 _dbus_transport_unref (server_transport); 295 server_transport = NULL; 296 297 if (connection == NULL) 298 { 299 _dbus_transport_unref (client_transport); 300 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 301 return NULL; 302 } 303 304 /* See if someone wants to handle this new connection, 305 * self-referencing for paranoia 306 */ 307 if (server->new_connection_function) 308 { 309 dbus_server_ref (server); 310 (* server->new_connection_function) (server, connection, 311 server->new_connection_data); 312 dbus_server_unref (server); 313 } 314 315 /* If no one grabbed a reference, the connection will die, 316 * and the client transport will get an immediate disconnect 317 */ 318 _dbus_connection_close_if_only_one_ref (connection); 319 dbus_connection_unref (connection); 320 321 return client_transport; 322 } 323 324 /** 325 * Tries to interpret the address entry as a debug pipe entry. 326 * 327 * Sets error if the result is not OK. 328 * 329 * @param entry an address entry 330 * @param server_p location to store a new DBusServer, or #NULL on failure. 331 * @param error location to store rationale for failure on bad address 332 * @returns the outcome 333 * 334 */ 335 DBusServerListenResult 336 _dbus_server_listen_debug_pipe (DBusAddressEntry *entry, 337 DBusServer **server_p, 338 DBusError *error) 339 { 340 const char *method; 341 342 *server_p = NULL; 343 344 method = dbus_address_entry_get_method (entry); 345 346 if (strcmp (method, "debug-pipe") == 0) 347 { 348 const char *name = dbus_address_entry_get_value (entry, "name"); 349 350 if (name == NULL) 351 { 352 _dbus_set_bad_address(error, "debug-pipe", "name", 353 NULL); 354 return DBUS_SERVER_LISTEN_BAD_ADDRESS; 355 } 356 357 *server_p = _dbus_server_debug_pipe_new (name, error); 358 359 if (*server_p) 360 { 361 _DBUS_ASSERT_ERROR_IS_CLEAR(error); 362 return DBUS_SERVER_LISTEN_OK; 363 } 364 else 365 { 366 _DBUS_ASSERT_ERROR_IS_SET(error); 367 return DBUS_SERVER_LISTEN_DID_NOT_CONNECT; 368 } 369 } 370 else 371 { 372 _DBUS_ASSERT_ERROR_IS_CLEAR(error); 373 return DBUS_SERVER_LISTEN_NOT_HANDLED; 374 } 375 } 376 377 /** 378 * Opens a debug pipe transport, used in the test suite. 379 * 380 * @param entry the address entry to try opening as debug-pipe 381 * @param transport_p return location for the opened transport 382 * @param error error to be set 383 * @returns result of the attempt 384 */ 385 DBusTransportOpenResult 386 _dbus_transport_open_debug_pipe (DBusAddressEntry *entry, 387 DBusTransport **transport_p, 388 DBusError *error) 389 { 390 const char *method; 391 392 method = dbus_address_entry_get_method (entry); 393 _dbus_assert (method != NULL); 394 395 if (strcmp (method, "debug-pipe") == 0) 396 { 397 const char *name = dbus_address_entry_get_value (entry, "name"); 398 399 if (name == NULL) 400 { 401 _dbus_set_bad_address (error, "debug-pipe", "name", 402 NULL); 403 return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; 404 } 405 406 *transport_p = _dbus_transport_debug_pipe_new (name, error); 407 408 if (*transport_p == NULL) 409 { 410 _DBUS_ASSERT_ERROR_IS_SET (error); 411 return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; 412 } 413 else 414 { 415 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 416 return DBUS_TRANSPORT_OPEN_OK; 417 } 418 } 419 else 420 { 421 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 422 return DBUS_TRANSPORT_OPEN_NOT_HANDLED; 423 } 424 } 425 426 427 /** @} */ 428 429 #endif /* DBUS_BUILD_TESTS */ 430 431