2 This file is part of PulseAudio.
4 Copyright 2009 Tanu Kaskinen
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.
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.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <dbus/dbus.h>
28 #include <pulse/client-conf.h>
29 #include <pulse/xmalloc.h>
31 #include <pulsecore/core.h>
32 #include <pulsecore/core-util.h>
33 #include <pulsecore/dbus-shared.h>
34 #include <pulsecore/macro.h>
35 #include <pulsecore/protocol-dbus.h>
37 #include "server-lookup.h"
39 #define OBJECT_PATH "/org/pulseaudio/server_lookup1"
40 #define INTERFACE "org.PulseAudio.ServerLookup1"
42 struct pa_dbusobj_server_lookup
{
44 pa_dbus_connection
*conn
;
45 pa_bool_t path_registered
;
48 static const char introspection
[] =
49 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
51 " <!-- If you are looking for documentation make sure to check out\n"
52 " http://pulseaudio.org/wiki/DBusInterface -->\n"
53 " <interface name=\"" INTERFACE
"\">\n"
54 " <property name=\"Address\" type=\"s\" access=\"read\"/>\n"
56 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE
"\">\n"
57 " <method name=\"Introspect\">\n"
58 " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
61 " <interface name=\"" DBUS_INTERFACE_PROPERTIES
"\">\n"
62 " <method name=\"Get\">\n"
63 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
64 " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
65 " <arg name=\"value\" type=\"v\" direction=\"out\"/>\n"
67 " <method name=\"Set\">\n"
68 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
69 " <arg name=\"property_name\" type=\"s\" direction=\"in\"/>\n"
70 " <arg name=\"value\" type=\"v\" direction=\"in\"/>\n"
72 " <method name=\"GetAll\">\n"
73 " <arg name=\"interface_name\" type=\"s\" direction=\"in\"/>\n"
74 " <arg name=\"props\" type=\"a{sv}\" direction=\"out\"/>\n"
79 static void unregister_cb(DBusConnection
*conn
, void *user_data
) {
80 pa_dbusobj_server_lookup
*sl
= user_data
;
83 pa_assert(sl
->path_registered
);
85 sl
->path_registered
= FALSE
;
88 static DBusHandlerResult
handle_introspect(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_server_lookup
*sl
) {
89 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
90 const char *i
= introspection
;
91 DBusMessage
*reply
= NULL
;
96 if (!(reply
= dbus_message_new_method_return(msg
))) {
97 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
100 if (!dbus_message_append_args(reply
, DBUS_TYPE_STRING
, &i
, DBUS_TYPE_INVALID
)) {
101 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
104 if (!dbus_connection_send(conn
, reply
, NULL
)) {
105 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
111 dbus_message_unref(reply
);
116 enum get_address_result_t
{
118 FAILED_TO_LOAD_CLIENT_CONF
,
119 SERVER_FROM_TYPE_FAILED
122 /* Caller frees the returned address. */
123 static enum get_address_result_t
get_address(pa_server_type_t server_type
, char **address
) {
124 enum get_address_result_t r
= SUCCESS
;
125 pa_client_conf
*conf
= pa_client_conf_new();
129 if (pa_client_conf_load(conf
, NULL
) < 0) {
130 r
= FAILED_TO_LOAD_CLIENT_CONF
;
134 if (conf
->default_dbus_server
)
135 *address
= pa_xstrdup(conf
->default_dbus_server
);
136 else if (!(*address
= pa_get_dbus_address_from_server_type(server_type
))) {
137 r
= SERVER_FROM_TYPE_FAILED
;
142 pa_client_conf_free(conf
);
146 static DBusHandlerResult
handle_get_address(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_server_lookup
*sl
) {
147 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
148 DBusMessage
*reply
= NULL
;
149 char *address
= NULL
;
150 DBusMessageIter msg_iter
;
151 DBusMessageIter variant_iter
;
157 switch (get_address(sl
->core
->server_type
, &address
)) {
159 if (!(reply
= dbus_message_new_method_return(msg
))) {
160 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
163 dbus_message_iter_init_append(reply
, &msg_iter
);
164 if (!dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_VARIANT
, "s", &variant_iter
)) {
165 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
168 if (!dbus_message_iter_append_basic(&variant_iter
, DBUS_TYPE_STRING
, &address
)) {
169 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
172 if (!dbus_message_iter_close_container(&msg_iter
, &variant_iter
)) {
173 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
176 if (!dbus_connection_send(conn
, reply
, NULL
)) {
177 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
180 r
= DBUS_HANDLER_RESULT_HANDLED
;
183 case FAILED_TO_LOAD_CLIENT_CONF
:
184 if (!(reply
= dbus_message_new_error(msg
, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) {
185 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
188 if (!dbus_connection_send(conn
, reply
, NULL
)) {
189 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
192 r
= DBUS_HANDLER_RESULT_HANDLED
;
195 case SERVER_FROM_TYPE_FAILED
:
196 if (!(reply
= dbus_message_new_error(msg
, DBUS_ERROR_FAILED
, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
197 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
200 if (!dbus_connection_send(conn
, reply
, NULL
)) {
201 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
204 r
= DBUS_HANDLER_RESULT_HANDLED
;
208 pa_assert_not_reached();
214 dbus_message_unref(reply
);
219 static DBusHandlerResult
handle_get(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_server_lookup
*sl
) {
220 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
221 const char* interface
;
222 const char* property
;
223 DBusMessage
*reply
= NULL
;
229 if (!dbus_message_get_args(msg
, NULL
, DBUS_TYPE_STRING
, &interface
, DBUS_TYPE_STRING
, &property
, DBUS_TYPE_INVALID
)) {
230 if (!(reply
= dbus_message_new_error(msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid arguments"))) {
231 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
234 if (!dbus_connection_send(conn
, reply
, NULL
)) {
235 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
238 r
= DBUS_HANDLER_RESULT_HANDLED
;
242 if (*interface
&& !pa_streq(interface
, INTERFACE
)) {
243 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
247 if (!pa_streq(property
, "Address")) {
248 if (!(reply
= dbus_message_new_error_printf(msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s: No such property", property
))) {
249 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
252 if (!dbus_connection_send(conn
, reply
, NULL
)) {
253 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
256 r
= DBUS_HANDLER_RESULT_HANDLED
;
260 r
= handle_get_address(conn
, msg
, sl
);
264 dbus_message_unref(reply
);
269 static DBusHandlerResult
handle_set(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_server_lookup
*sl
) {
270 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
271 const char* interface
;
272 const char* property
;
273 DBusMessage
*reply
= NULL
;
279 if (!dbus_message_get_args(msg
, NULL
, DBUS_TYPE_STRING
, &interface
, DBUS_TYPE_STRING
, &property
, DBUS_TYPE_INVALID
)) {
280 if (!(reply
= dbus_message_new_error(msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid arguments"))) {
281 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
284 if (!dbus_connection_send(conn
, reply
, NULL
)) {
285 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
288 r
= DBUS_HANDLER_RESULT_HANDLED
;
292 if (*interface
&& !pa_streq(interface
, INTERFACE
)) {
293 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
297 if (!pa_streq(property
, "Address")) {
298 if (!(reply
= dbus_message_new_error_printf(msg
, PA_DBUS_ERROR_NO_SUCH_PROPERTY
, "%s: No such property", property
))) {
299 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
302 if (!dbus_connection_send(conn
, reply
, NULL
)) {
303 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
306 r
= DBUS_HANDLER_RESULT_HANDLED
;
310 if (!(reply
= dbus_message_new_error_printf(msg
, DBUS_ERROR_ACCESS_DENIED
, "%s: Property not settable", property
))) {
311 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
314 if (!dbus_connection_send(conn
, reply
, NULL
)) {
315 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
318 r
= DBUS_HANDLER_RESULT_HANDLED
;
322 dbus_message_unref(reply
);
327 static DBusHandlerResult
handle_get_all(DBusConnection
*conn
, DBusMessage
*msg
, pa_dbusobj_server_lookup
*sl
) {
328 DBusHandlerResult r
= DBUS_HANDLER_RESULT_HANDLED
;
329 DBusMessage
*reply
= NULL
;
330 const char *property
= "Address";
331 char *interface
= NULL
;
332 char *address
= NULL
;
333 DBusMessageIter msg_iter
;
334 DBusMessageIter dict_iter
;
335 DBusMessageIter dict_entry_iter
;
336 DBusMessageIter variant_iter
;
342 if (!dbus_message_get_args(msg
, NULL
, DBUS_TYPE_STRING
, &interface
, DBUS_TYPE_INVALID
)) {
343 if (!(reply
= dbus_message_new_error(msg
, DBUS_ERROR_INVALID_ARGS
, "Invalid arguments"))) {
344 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
347 if (!dbus_connection_send(conn
, reply
, NULL
)) {
348 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
351 r
= DBUS_HANDLER_RESULT_HANDLED
;
355 switch (get_address(sl
->core
->server_type
, &address
)) {
357 if (!(reply
= dbus_message_new_method_return(msg
))) {
358 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
361 dbus_message_iter_init_append(reply
, &msg_iter
);
362 if (!dbus_message_iter_open_container(&msg_iter
, DBUS_TYPE_ARRAY
, "{sv}", &dict_iter
)) {
363 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
366 if (!dbus_message_iter_open_container(&dict_iter
, DBUS_TYPE_DICT_ENTRY
, NULL
, &dict_entry_iter
)) {
367 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
370 if (!dbus_message_iter_append_basic(&dict_entry_iter
, DBUS_TYPE_STRING
, &property
)) {
371 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
374 if (!dbus_message_iter_open_container(&dict_entry_iter
, DBUS_TYPE_VARIANT
, "s", &variant_iter
)) {
375 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
378 if (!dbus_message_iter_append_basic(&variant_iter
, DBUS_TYPE_STRING
, &address
)) {
379 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
382 if (!dbus_message_iter_close_container(&dict_entry_iter
, &variant_iter
)) {
383 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
386 if (!dbus_message_iter_close_container(&dict_iter
, &dict_entry_iter
)) {
387 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
390 if (!dbus_message_iter_close_container(&msg_iter
, &dict_iter
)) {
391 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
394 if (!dbus_connection_send(conn
, reply
, NULL
)) {
395 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
398 r
= DBUS_HANDLER_RESULT_HANDLED
;
401 case FAILED_TO_LOAD_CLIENT_CONF
:
402 if (!(reply
= dbus_message_new_error(msg
, "org.pulseaudio.ClientConfLoadError", "Failed to load client.conf."))) {
403 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
406 if (!dbus_connection_send(conn
, reply
, NULL
)) {
407 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
410 r
= DBUS_HANDLER_RESULT_HANDLED
;
413 case SERVER_FROM_TYPE_FAILED
:
414 if (!(reply
= dbus_message_new_error(msg
, DBUS_ERROR_FAILED
, "PulseAudio internal error: get_dbus_server_from_type() failed."))) {
415 r
= DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
418 if (!dbus_connection_send(conn
, reply
, NULL
)) {
419 r
= DBUS_HANDLER_RESULT_NEED_MEMORY
;
422 r
= DBUS_HANDLER_RESULT_HANDLED
;
426 pa_assert_not_reached();
432 dbus_message_unref(reply
);
437 static DBusHandlerResult
message_cb(DBusConnection
*conn
, DBusMessage
*msg
, void *user_data
) {
438 pa_dbusobj_server_lookup
*sl
= user_data
;
444 /* 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)); */
446 if (dbus_message_get_type(msg
) != DBUS_MESSAGE_TYPE_METHOD_CALL
)
447 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
449 if (dbus_message_is_method_call(msg
, DBUS_INTERFACE_INTROSPECTABLE
, "Introspect") ||
450 (!dbus_message_get_interface(msg
) && dbus_message_has_member(msg
, "Introspect")))
451 return handle_introspect(conn
, msg
, sl
);
453 if (dbus_message_is_method_call(msg
, DBUS_INTERFACE_PROPERTIES
, "Get") ||
454 (!dbus_message_get_interface(msg
) && dbus_message_has_member(msg
, "Get")))
455 return handle_get(conn
, msg
, sl
);
457 if (dbus_message_is_method_call(msg
, DBUS_INTERFACE_PROPERTIES
, "Set") ||
458 (!dbus_message_get_interface(msg
) && dbus_message_has_member(msg
, "Set")))
459 return handle_set(conn
, msg
, sl
);
461 if (dbus_message_is_method_call(msg
, DBUS_INTERFACE_PROPERTIES
, "GetAll") ||
462 (!dbus_message_get_interface(msg
) && dbus_message_has_member(msg
, "GetAll")))
463 return handle_get_all(conn
, msg
, sl
);
465 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
468 static DBusObjectPathVTable vtable
= {
469 .unregister_function
= unregister_cb
,
470 .message_function
= message_cb
,
471 .dbus_internal_pad1
= NULL
,
472 .dbus_internal_pad2
= NULL
,
473 .dbus_internal_pad3
= NULL
,
474 .dbus_internal_pad4
= NULL
477 pa_dbusobj_server_lookup
*pa_dbusobj_server_lookup_new(pa_core
*c
) {
478 pa_dbusobj_server_lookup
*sl
;
481 dbus_error_init(&error
);
483 sl
= pa_xnew(pa_dbusobj_server_lookup
, 1);
485 sl
->path_registered
= FALSE
;
487 if (!(sl
->conn
= pa_dbus_bus_get(c
, DBUS_BUS_SESSION
, &error
)) || dbus_error_is_set(&error
)) {
488 pa_log("Unable to contact D-Bus: %s: %s", error
.name
, error
.message
);
492 if (!dbus_connection_register_object_path(pa_dbus_connection_get(sl
->conn
), OBJECT_PATH
, &vtable
, sl
)) {
493 pa_log("dbus_connection_register_object_path() failed for " OBJECT_PATH
".");
497 sl
->path_registered
= TRUE
;
502 dbus_error_free(&error
);
504 pa_dbusobj_server_lookup_free(sl
);
509 void pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup
*sl
) {
512 if (sl
->path_registered
) {
514 if (!dbus_connection_unregister_object_path(pa_dbus_connection_get(sl
->conn
), OBJECT_PATH
))
515 pa_log_debug("dbus_connection_unregister_object_path() failed for " OBJECT_PATH
".");
519 pa_dbus_connection_unref(sl
->conn
);