• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
40 struct pa_dbusobj_server_lookup {
41     pa_core *core;
42     pa_dbus_connection *conn;
43     bool path_registered;
44 };
45 
46 static 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 
unregister_cb(DBusConnection * conn,void * user_data)77 static 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 
handle_introspect(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)86 static 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 
107 finish:
108     if (reply)
109         dbus_message_unref(reply);
110 
111     return r;
112 }
113 
114 enum get_address_result_t {
115     SUCCESS,
116     SERVER_FROM_TYPE_FAILED
117 };
118 
119 /* Caller frees the returned address. */
get_address(pa_server_type_t server_type,char ** address)120 static 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 
135 finish:
136     pa_client_conf_free(conf);
137     return r;
138 }
139 
handle_get_address(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)140 static 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 
193 finish:
194     pa_xfree(address);
195     if (reply)
196         dbus_message_unref(reply);
197 
198     return r;
199 }
200 
handle_get(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)201 static 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 
244 finish:
245     if (reply)
246         dbus_message_unref(reply);
247 
248     return r;
249 }
250 
handle_set(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)251 static 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 
302 finish:
303     if (reply)
304         dbus_message_unref(reply);
305 
306     return r;
307 }
308 
handle_get_all(DBusConnection * conn,DBusMessage * msg,pa_dbusobj_server_lookup * sl)309 static 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 
399 finish:
400     pa_xfree(address);
401     if (reply)
402         dbus_message_unref(reply);
403 
404     return r;
405 }
406 
message_cb(DBusConnection * conn,DBusMessage * msg,void * user_data)407 static 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 
438 static 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 
pa_dbusobj_server_lookup_new(pa_core * c)447 pa_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 
471 fail:
472     dbus_error_free(&error);
473 
474     pa_dbusobj_server_lookup_free(sl);
475 
476     return NULL;
477 }
478 
pa_dbusobj_server_lookup_free(pa_dbusobj_server_lookup * sl)479 void 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