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