1/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2/* dbus-transport-unix.c UNIX socket subclasses of DBusTransport 3 * 4 * Copyright (C) 2002, 2003, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24#include <config.h> 25 26#include <stdio.h> 27 28#include "dbus-internals.h" 29#include "dbus-connection-internal.h" 30#include "dbus-transport-unix.h" 31#include "dbus-transport-socket.h" 32#include "dbus-transport-protected.h" 33#include "dbus-watch.h" 34#include "dbus-sysdeps-unix.h" 35#include "dbus-test.h" 36 37/** 38 * @defgroup DBusTransportUnix DBusTransport implementations for UNIX 39 * @ingroup DBusInternals 40 * @brief Implementation details of DBusTransport on UNIX 41 * 42 * @{ 43 */ 44 45/** 46 * Creates a new transport for the given Unix domain socket 47 * path. This creates a client-side of a transport. 48 * 49 * @todo once we add a way to escape paths in a dbus 50 * address, this function needs to do escaping. 51 * 52 * @param path the path to the domain socket. 53 * @param abstract #TRUE to use abstract socket namespace 54 * @param error address where an error can be returned. 55 * @returns a new transport, or #NULL on failure. 56 */ 57DBusTransport* 58_dbus_transport_new_for_domain_socket (const char *path, 59 dbus_bool_t abstract, 60 DBusError *error) 61{ 62 int fd; 63 DBusTransport *transport; 64 DBusString address; 65 66 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 67 68 if (!_dbus_string_init (&address)) 69 { 70 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 71 return NULL; 72 } 73 74 fd = -1; 75 76 if ((abstract && 77 !_dbus_string_append (&address, "unix:abstract=")) || 78 (!abstract && 79 !_dbus_string_append (&address, "unix:path=")) || 80 !_dbus_string_append (&address, path)) 81 { 82 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 83 goto failed_0; 84 } 85 86 fd = _dbus_connect_unix_socket (path, abstract, error); 87 if (fd < 0) 88 { 89 _DBUS_ASSERT_ERROR_IS_SET (error); 90 goto failed_0; 91 } 92 93 _dbus_verbose ("Successfully connected to unix socket %s\n", 94 path); 95 96 transport = _dbus_transport_new_for_socket (fd, NULL, &address); 97 if (transport == NULL) 98 { 99 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 100 goto failed_1; 101 } 102 103 _dbus_string_free (&address); 104 105 return transport; 106 107 failed_1: 108 _dbus_close_socket (fd, NULL); 109 failed_0: 110 _dbus_string_free (&address); 111 return NULL; 112} 113 114/** 115 * Creates a new transport for the given binary and arguments. This 116 * creates a client-side of a transport. The process will be forked 117 * off and executed with stdin/stdout connected to a local AF_UNIX 118 * socket. 119 * 120 * @param path the path to the domain socket. 121 * @param argv Parameters list 122 * @param error address where an error can be returned. 123 * @returns a new transport, or #NULL on failure. 124 */ 125static DBusTransport* 126_dbus_transport_new_for_exec (const char *path, 127 char *const argv[], 128 DBusError *error) 129{ 130 int fd; 131 DBusTransport *transport; 132 DBusString address; 133 unsigned i; 134 char *escaped; 135 136 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 137 138 if (!_dbus_string_init (&address)) 139 { 140 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 141 return NULL; 142 } 143 144 fd = -1; 145 146 escaped = dbus_address_escape_value (path); 147 if (!escaped) 148 { 149 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 150 goto failed; 151 } 152 153 if (!_dbus_string_append (&address, "unixexec:path=") || 154 !_dbus_string_append (&address, escaped)) 155 { 156 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 157 dbus_free (escaped); 158 goto failed; 159 } 160 161 dbus_free (escaped); 162 163 if (argv) 164 { 165 for (i = 0; argv[i]; i++) 166 { 167 dbus_bool_t success; 168 169 escaped = dbus_address_escape_value (argv[i]); 170 if (!escaped) 171 { 172 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 173 goto failed; 174 } 175 176 success = _dbus_string_append_printf (&address, ",argv%u=%s", i, escaped); 177 dbus_free (escaped); 178 179 if (!success) 180 { 181 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 182 goto failed; 183 } 184 } 185 } 186 187 fd = _dbus_connect_exec (path, argv, error); 188 if (fd < 0) 189 { 190 _DBUS_ASSERT_ERROR_IS_SET (error); 191 goto failed; 192 } 193 194 _dbus_verbose ("Successfully connected to process %s\n", 195 path); 196 197 transport = _dbus_transport_new_for_socket (fd, NULL, &address); 198 if (transport == NULL) 199 { 200 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 201 goto failed; 202 } 203 204 _dbus_string_free (&address); 205 206 return transport; 207 208 failed: 209 if (fd >= 0) 210 _dbus_close_socket (fd, NULL); 211 212 _dbus_string_free (&address); 213 return NULL; 214} 215 216/** 217 * Opens platform specific transport types. 218 * 219 * @param entry the address entry to try opening 220 * @param transport_p return location for the opened transport 221 * @param error error to be set 222 * @returns result of the attempt 223 */ 224DBusTransportOpenResult 225_dbus_transport_open_platform_specific (DBusAddressEntry *entry, 226 DBusTransport **transport_p, 227 DBusError *error) 228{ 229 const char *method; 230 231 method = dbus_address_entry_get_method (entry); 232 _dbus_assert (method != NULL); 233 234 if (strcmp (method, "unix") == 0) 235 { 236 const char *path = dbus_address_entry_get_value (entry, "path"); 237 const char *tmpdir = dbus_address_entry_get_value (entry, "tmpdir"); 238 const char *abstract = dbus_address_entry_get_value (entry, "abstract"); 239 240 if (tmpdir != NULL) 241 { 242 _dbus_set_bad_address (error, NULL, NULL, 243 "cannot use the \"tmpdir\" option for an address to connect to, only in an address to listen on"); 244 return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; 245 } 246 247 if (path == NULL && abstract == NULL) 248 { 249 _dbus_set_bad_address (error, "unix", 250 "path or abstract", 251 NULL); 252 return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; 253 } 254 255 if (path != NULL && abstract != NULL) 256 { 257 _dbus_set_bad_address (error, NULL, NULL, 258 "can't specify both \"path\" and \"abstract\" options in an address"); 259 return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; 260 } 261 262 if (path) 263 *transport_p = _dbus_transport_new_for_domain_socket (path, FALSE, 264 error); 265 else 266 *transport_p = _dbus_transport_new_for_domain_socket (abstract, TRUE, 267 error); 268 if (*transport_p == NULL) 269 { 270 _DBUS_ASSERT_ERROR_IS_SET (error); 271 return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; 272 } 273 else 274 { 275 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 276 return DBUS_TRANSPORT_OPEN_OK; 277 } 278 } 279 else if (strcmp (method, "unixexec") == 0) 280 { 281 const char *path; 282 unsigned i; 283 char **argv; 284 285 path = dbus_address_entry_get_value (entry, "path"); 286 if (path == NULL) 287 { 288 _dbus_set_bad_address (error, NULL, NULL, 289 "No process path specified"); 290 return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; 291 } 292 293 /* First count argv arguments */ 294 for (i = 1; ; i++) 295 { 296 char t[4+20+1]; /* "argv" plus space for a formatted base 10 64bit integer, plus NUL */ 297 298 snprintf (t, sizeof(t), "argv%u", i); 299 300 if (!dbus_address_entry_get_value (entry, t)) 301 break; 302 } 303 304 /* Allocate string array */ 305 argv = dbus_new0 (char*, i+1); 306 if (!argv) 307 { 308 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 309 return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; 310 } 311 312 /* Fill in string array */ 313 for (i = 0; ; i++) 314 { 315 char t[4+20+1]; 316 const char *p; 317 318 snprintf (t, sizeof(t), "argv%u", i); 319 320 p = dbus_address_entry_get_value (entry, t); 321 if (!p) 322 { 323 if (i == 0) 324 /* If argv0 isn't specified, fill in the path instead */ 325 p = path; 326 else 327 break; 328 } 329 330 argv[i] = _dbus_strdup (p); 331 if (!argv[i]) 332 { 333 dbus_free_string_array (argv); 334 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 335 return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; 336 } 337 } 338 339 *transport_p = _dbus_transport_new_for_exec (path, argv, error); 340 dbus_free_string_array (argv); 341 342 if (*transport_p == NULL) 343 { 344 _DBUS_ASSERT_ERROR_IS_SET (error); 345 return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; 346 } 347 else 348 { 349 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 350 return DBUS_TRANSPORT_OPEN_OK; 351 } 352 } 353#ifdef DBUS_ENABLE_LAUNCHD 354 else if (strcmp (method, "launchd") == 0) 355 { 356 DBusError tmp_error = DBUS_ERROR_INIT; 357 const char *launchd_env_var = dbus_address_entry_get_value (entry, "env"); 358 const char *launchd_socket; 359 DBusString socket_path; 360 dbus_bool_t valid_socket; 361 362 if (!_dbus_string_init (&socket_path)) 363 { 364 _DBUS_SET_OOM (error); 365 return FALSE; 366 } 367 368 if (launchd_env_var == NULL) 369 { 370 _dbus_set_bad_address (error, "launchd", "env", NULL); 371 return DBUS_TRANSPORT_OPEN_BAD_ADDRESS; 372 } 373 374 valid_socket = _dbus_lookup_launchd_socket (&socket_path, launchd_env_var, error); 375 376 if (dbus_error_is_set(error)) 377 { 378 _dbus_string_free(&socket_path); 379 return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; 380 } 381 382 if (!valid_socket) 383 { 384 dbus_set_error(&tmp_error, DBUS_ERROR_BAD_ADDRESS, 385 "launchd's env var %s does not exist", launchd_env_var); 386 dbus_error_free(error); 387 dbus_move_error(&tmp_error, error); 388 return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; 389 } 390 391 launchd_socket = _dbus_string_get_const_data(&socket_path); 392 *transport_p = _dbus_transport_new_for_domain_socket (launchd_socket, FALSE, error); 393 394 if (*transport_p == NULL) 395 { 396 _DBUS_ASSERT_ERROR_IS_SET (error); 397 return DBUS_TRANSPORT_OPEN_DID_NOT_CONNECT; 398 } 399 else 400 { 401 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 402 return DBUS_TRANSPORT_OPEN_OK; 403 } 404 } 405#endif 406 else 407 { 408 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 409 return DBUS_TRANSPORT_OPEN_NOT_HANDLED; 410 } 411} 412 413/** @} */ 414 415#ifdef DBUS_BUILD_TESTS 416 417dbus_bool_t 418_dbus_transport_unix_test (void) 419{ 420 DBusConnection *c; 421 DBusError error; 422 dbus_bool_t ret; 423 const char *address; 424 425 dbus_error_init (&error); 426 427 c = dbus_connection_open ("unixexec:argv0=false,argv1=foobar,path=/bin/false", &error); 428 _dbus_assert (c != NULL); 429 _dbus_assert (!dbus_error_is_set (&error)); 430 431 address = _dbus_connection_get_address (c); 432 _dbus_assert (address != NULL); 433 434 /* Let's see if the address got parsed, reordered and formatted correctly */ 435 ret = strcmp (address, "unixexec:path=/bin/false,argv0=false,argv1=foobar") == 0; 436 437 dbus_connection_unref (c); 438 439 return ret; 440} 441 442#endif 443