1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2009 Tanu Kaskinen 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <dbus/dbus.h> 25 26#include <pulse/client-conf.h> 27#include <pulse/xmalloc.h> 28 29#include <pulsecore/core.h> 30#include <pulsecore/core-util.h> 31#include <pulsecore/dbus-shared.h> 32#include <pulsecore/macro.h> 33#include <pulsecore/protocol-dbus.h> 34 35#include "server-lookup.h" 36 37#define OBJECT_PATH "/org/pulseaudio/server_lookup1" 38#define INTERFACE "org.PulseAudio.ServerLookup1" 39 40struct pa_dbusobj_server_lookup { 41 pa_core *core; 42 pa_dbus_connection *conn; 43 bool path_registered; 44}; 45 46static const char introspection[] = 47 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE 48 "<node>" 49 " <!-- If you are looking for documentation make sure to check out\n" 50 " http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/ -->\n" 51 " <interface name=\"" INTERFACE "\">\n" 52 " <property name=\"Address\" type=\"s\" access=\"read\"/>\n" 53 " </interface>\n" 54 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n" 55 " <method name=\"Introspect\">\n" 56 " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" 57 " </method>\n" 58 " </interface>\n" 59 " <interface name=\"" DBUS_INTERFACE_PROPERTIES "\">\n" 60 " <method name=\"Get\">\n" 61 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n" 62 " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n" 63 " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n" 64 " </method>\n" 65 " <method name=\"Set\">\n" 66 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n" 67 " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n" 68 " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n" 69 " </method>\n" 70 " <method name=\"GetAll\">\n" 71 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n" 72 " <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n" 73 " </method>\n" 74 " </interface>\n" 75 "</node>\n"; 76 77static void unregister_cb(DBusConnection *conn, void *user_data) { 78 pa_dbusobj_server_lookup *sl = user_data; 79 80 pa_assert(sl); 81 pa_assert(sl->path_registered); 82 83 sl->path_registered = false; 84} 85 86static DBusHandlerResult handle_introspect(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { 87 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; 88 const char *i = introspection; 89 DBusMessage *reply = NULL; 90 91 pa_assert(conn); 92 pa_assert(msg); 93 94 if (!(reply = dbus_message_new_method_return(msg))) { 95 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 96 goto finish; 97 } 98 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &i, DBUS_TYPE_INVALID)) { 99 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 100 goto finish; 101 } 102 if (!dbus_connection_send(conn, reply, NULL)) { 103 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 104 goto finish; 105 } 106 107finish: 108 if (reply) 109 dbus_message_unref(reply); 110 111 return r; 112} 113 114enum get_address_result_t { 115 SUCCESS, 116 SERVER_FROM_TYPE_FAILED 117}; 118 119/* Caller frees the returned address. */ 120static enum get_address_result_t get_address(pa_server_type_t server_type, char **address) { 121 enum get_address_result_t r = SUCCESS; 122 pa_client_conf *conf = pa_client_conf_new(); 123 124 *address = NULL; 125 126 pa_client_conf_load(conf, false, false); 127 128 if (conf->default_dbus_server) 129 *address = pa_xstrdup(conf->default_dbus_server); 130 else if (!(*address = pa_get_dbus_address_from_server_type(server_type))) { 131 r = SERVER_FROM_TYPE_FAILED; 132 goto finish; 133 } 134 135finish: 136 pa_client_conf_free(conf); 137 return r; 138} 139 140static DBusHandlerResult handle_get_address(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { 141 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; 142 DBusMessage *reply = NULL; 143 char *address = NULL; 144 DBusMessageIter msg_iter; 145 DBusMessageIter variant_iter; 146 147 pa_assert(conn); 148 pa_assert(msg); 149 pa_assert(sl); 150 151 switch (get_address(sl->core->server_type, &address)) { 152 case SUCCESS: 153 if (!(reply = dbus_message_new_method_return(msg))) { 154 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 155 goto finish; 156 } 157 dbus_message_iter_init_append(reply, &msg_iter); 158 if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { 159 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 160 goto finish; 161 } 162 if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) { 163 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 164 goto finish; 165 } 166 if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) { 167 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 168 goto finish; 169 } 170 if (!dbus_connection_send(conn, reply, NULL)) { 171 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 172 goto finish; 173 } 174 r = DBUS_HANDLER_RESULT_HANDLED; 175 goto finish; 176 177 case SERVER_FROM_TYPE_FAILED: 178 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) { 179 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 180 goto finish; 181 } 182 if (!dbus_connection_send(conn, reply, NULL)) { 183 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 184 goto finish; 185 } 186 r = DBUS_HANDLER_RESULT_HANDLED; 187 goto finish; 188 189 default: 190 pa_assert_not_reached(); 191 } 192 193finish: 194 pa_xfree(address); 195 if (reply) 196 dbus_message_unref(reply); 197 198 return r; 199} 200 201static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { 202 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; 203 const char* interface; 204 const char* property; 205 DBusMessage *reply = NULL; 206 207 pa_assert(conn); 208 pa_assert(msg); 209 pa_assert(sl); 210 211 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { 212 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { 213 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 214 goto finish; 215 } 216 if (!dbus_connection_send(conn, reply, NULL)) { 217 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 218 goto finish; 219 } 220 r = DBUS_HANDLER_RESULT_HANDLED; 221 goto finish; 222 } 223 224 if (*interface && !pa_streq(interface, INTERFACE)) { 225 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 226 goto finish; 227 } 228 229 if (!pa_streq(property, "Address")) { 230 if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { 231 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 232 goto finish; 233 } 234 if (!dbus_connection_send(conn, reply, NULL)) { 235 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 236 goto finish; 237 } 238 r = DBUS_HANDLER_RESULT_HANDLED; 239 goto finish; 240 } 241 242 r = handle_get_address(conn, msg, sl); 243 244finish: 245 if (reply) 246 dbus_message_unref(reply); 247 248 return r; 249} 250 251static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { 252 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; 253 const char* interface; 254 const char* property; 255 DBusMessage *reply = NULL; 256 257 pa_assert(conn); 258 pa_assert(msg); 259 pa_assert(sl); 260 261 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) { 262 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { 263 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 264 goto finish; 265 } 266 if (!dbus_connection_send(conn, reply, NULL)) { 267 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 268 goto finish; 269 } 270 r = DBUS_HANDLER_RESULT_HANDLED; 271 goto finish; 272 } 273 274 if (*interface && !pa_streq(interface, INTERFACE)) { 275 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 276 goto finish; 277 } 278 279 if (!pa_streq(property, "Address")) { 280 if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) { 281 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 282 goto finish; 283 } 284 if (!dbus_connection_send(conn, reply, NULL)) { 285 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 286 goto finish; 287 } 288 r = DBUS_HANDLER_RESULT_HANDLED; 289 goto finish; 290 } 291 292 if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) { 293 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 294 goto finish; 295 } 296 if (!dbus_connection_send(conn, reply, NULL)) { 297 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 298 goto finish; 299 } 300 r = DBUS_HANDLER_RESULT_HANDLED; 301 302finish: 303 if (reply) 304 dbus_message_unref(reply); 305 306 return r; 307} 308 309static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_server_lookup *sl) { 310 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED; 311 DBusMessage *reply = NULL; 312 const char *property = "Address"; 313 char *interface = NULL; 314 char *address = NULL; 315 DBusMessageIter msg_iter; 316 DBusMessageIter dict_iter; 317 DBusMessageIter dict_entry_iter; 318 DBusMessageIter variant_iter; 319 320 pa_assert(conn); 321 pa_assert(msg); 322 pa_assert(sl); 323 324 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) { 325 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) { 326 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 327 goto finish; 328 } 329 if (!dbus_connection_send(conn, reply, NULL)) { 330 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 331 goto finish; 332 } 333 r = DBUS_HANDLER_RESULT_HANDLED; 334 goto finish; 335 } 336 337 switch (get_address(sl->core->server_type, &address)) { 338 case SUCCESS: 339 if (!(reply = dbus_message_new_method_return(msg))) { 340 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 341 goto finish; 342 } 343 dbus_message_iter_init_append(reply, &msg_iter); 344 if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) { 345 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 346 goto finish; 347 } 348 if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) { 349 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 350 goto finish; 351 } 352 if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &property)) { 353 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 354 goto finish; 355 } 356 if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) { 357 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 358 goto finish; 359 } 360 if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &address)) { 361 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 362 goto finish; 363 } 364 if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) { 365 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 366 goto finish; 367 } 368 if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) { 369 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 370 goto finish; 371 } 372 if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) { 373 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 374 goto finish; 375 } 376 if (!dbus_connection_send(conn, reply, NULL)) { 377 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 378 goto finish; 379 } 380 r = DBUS_HANDLER_RESULT_HANDLED; 381 goto finish; 382 383 case SERVER_FROM_TYPE_FAILED: 384 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_FAILED, "PulseAudio internal error: get_dbus_server_from_type() failed."))) { 385 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 386 goto finish; 387 } 388 if (!dbus_connection_send(conn, reply, NULL)) { 389 r = DBUS_HANDLER_RESULT_NEED_MEMORY; 390 goto finish; 391 } 392 r = DBUS_HANDLER_RESULT_HANDLED; 393 goto finish; 394 395 default: 396 pa_assert_not_reached(); 397 } 398 399finish: 400 pa_xfree(address); 401 if (reply) 402 dbus_message_unref(reply); 403 404 return r; 405} 406 407static DBusHandlerResult message_cb(DBusConnection *conn, DBusMessage *msg, void *user_data) { 408 pa_dbusobj_server_lookup *sl = user_data; 409 410 pa_assert(conn); 411 pa_assert(msg); 412 pa_assert(sl); 413 414 /* pa_log("Got message! type = %s path = %s iface = %s member = %s dest = %s", dbus_message_type_to_string(dbus_message_get_type(msg)), dbus_message_get_path(msg), dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_destination(msg)); */ 415 416 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) 417 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 418 419 if (dbus_message_is_method_call(msg, DBUS_INTERFACE_INTROSPECTABLE, "Introspect") || 420 (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Introspect"))) 421 return handle_introspect(conn, msg, sl); 422 423 if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Get") || 424 (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Get"))) 425 return handle_get(conn, msg, sl); 426 427 if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "Set") || 428 (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "Set"))) 429 return handle_set(conn, msg, sl); 430 431 if (dbus_message_is_method_call(msg, DBUS_INTERFACE_PROPERTIES, "GetAll") || 432 (!dbus_message_get_interface(msg) && dbus_message_has_member(msg, "GetAll"))) 433 return handle_get_all(conn, msg, sl); 434 435 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 436} 437 438static DBusObjectPathVTable vtable = { 439 .unregister_function = unregister_cb, 440 .message_function = message_cb, 441 .dbus_internal_pad1 = NULL, 442 .dbus_internal_pad2 = NULL, 443 .dbus_internal_pad3 = NULL, 444 .dbus_internal_pad4 = NULL 445}; 446 447pa_dbusobj_server_lookup *pa_dbusobj_server_lookup_new(pa_core *c) { 448 pa_dbusobj_server_lookup *sl; 449 DBusError error; 450 451 dbus_error_init(&error); 452 453 sl = pa_xnew(pa_dbusobj_server_lookup, 1); 454 sl->core = c; 455 sl->path_registered = false; 456 457 if (!(sl->conn = pa_dbus_bus_get(c, DBUS_BUS_SESSION, &error)) || dbus_error_is_set(&error)) { 458 pa_log_warn("Unable to contact D-Bus: %s: %s", error.name, error.message); 459 goto fail; 460 } 461 462 if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH, &vtable, sl)) { 463 pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH "."); 464 goto fail; 465 } 466 467 sl->path_registered = true; 468 469 return sl; 470 471fail: 472 dbus_error_free(&error); 473 474 pa_dbusobj_server_lookup_free(sl); 475 476 return NULL; 477} 478 479void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup *sl) { 480 pa_assert(sl); 481 482 if (sl->path_registered) { 483 pa_assert(sl->conn); 484 if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl->conn), OBJECT_PATH)) 485 pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH "."); 486 } 487 488 if (sl->conn) 489 pa_dbus_connection_unref(sl->conn); 490 491 pa_xfree(sl); 492} 493