]> code.delx.au - pulseaudio/blob - src/pulsecore/dbus-objs/core.c
dbus: Implement the Name property of the core object.
[pulseaudio] / src / pulsecore / dbus-objs / core.c
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, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <dbus/dbus.h>
27
28 #include <pulse/xmalloc.h>
29
30 #include <pulsecore/core-util.h>
31 #include <pulsecore/dbus-common.h>
32 #include <pulsecore/macro.h>
33
34 #include "core.h"
35
36 #define OBJECT_PATH "/org/pulseaudio1"
37 #define INTERFACE_CORE "org.PulseAudio.Core1"
38
39 struct pa_dbusobj_core {
40 pa_core *core;
41 };
42
43 static const char *introspection_snippet =
44 " <interface name=\"" INTERFACE_CORE "\">\n"
45 " <method name=\"GetCardByName\">\n"
46 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
47 " <arg name=\"Card\" type=\"o\" direction=\"out\"/>\n"
48 " </method>\n"
49 " <method name=\"GetSinkByName\">\n"
50 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
51 " <arg name=\"Sink\" type=\"o\" direction=\"out\"/>\n"
52 " </method>\n"
53 " <method name=\"GetSourceByName\">\n"
54 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
55 " <arg name=\"Source\" type=\"o\" direction=\"out\"/>\n"
56 " </method>\n"
57 " <method name=\"GetSampleByName\">\n"
58 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
59 " <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
60 " </method>\n"
61 " <method name=\"UploadSample\">\n"
62 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
63 " <arg name=\"SampleFormat\" type=\"y\" direction=\"in\"/>\n"
64 " <arg name=\"SampleRate\" type=\"u\" direction=\"in\"/>\n"
65 " <arg name=\"Channels\" type=\"ay\" direction=\"in\"/>\n"
66 " <arg name=\"DefaultVolume\" type=\"au\" direction=\"in\"/>\n"
67 " <arg name=\"Proplist\" type=\"a{say}\" direction=\"in\"/>\n"
68 " <arg name=\"Data\" type=\"ay\" direction=\"in\"/>\n"
69 " <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
70 " </method>\n"
71 " <method name=\"LoadSampleFromFile\">\n"
72 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
73 " <arg name=\"Filepath\" type=\"s\" direction=\"in\"/>\n"
74 " <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
75 " </method>\n"
76 " <method name=\"AddLazySample\">\n"
77 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
78 " <arg name=\"Filepath\" type=\"s\" direction=\"in\"/>\n"
79 " <arg name=\"Sample\" type=\"o\" direction=\"out\"/>\n"
80 " </method>\n"
81 " <method name=\"AddLazySamplesFromDirectory\">\n"
82 " <arg name=\"Dirpath\" type=\"s\" direction=\"in\"/>\n"
83 " </method>\n"
84 " <method name=\"LoadModule\">\n"
85 " <arg name=\"Name\" type=\"s\" direction=\"in\"/>\n"
86 " <arg name=\"Arguments\" type=\"a{ss}\" direction=\"in\"/>\n"
87 " <arg name=\"Module\" type=\"o\" direction=\"out\"/>\n"
88 " </method>\n"
89 " <method name=\"Exit\"/>\n"
90 " <signal name=\"NewCard\">\n"
91 " <arg name=\"Card\" type=\"o\"/>\n"
92 " </signal>\n"
93 " <signal name=\"CardRemoved\">\n"
94 " <arg name=\"Card\" type=\"o\"/>\n"
95 " </signal>\n"
96 " <signal name=\"NewSink\">\n"
97 " <arg name=\"Sink\" type=\"o\"/>\n"
98 " </signal>\n"
99 " <signal name=\"SinkRemoved\">\n"
100 " <arg name=\"Sink\" type=\"o\"/>\n"
101 " </signal>\n"
102 " <signal name=\"FallbackSinkUpdated\">\n"
103 " <arg name=\"Sink\" type=\"o\"/>\n"
104 " </signal>\n"
105 " <signal name=\"NewSource\">\n"
106 " <arg name=\"Source\" type=\"o\"/>\n"
107 " </signal>\n"
108 " <signal name=\"SourceRemoved\">\n"
109 " <arg name=\"Source\" type=\"o\"/>\n"
110 " </signal>\n"
111 " <signal name=\"FallbackSourceUpdated\">\n"
112 " <arg name=\"Source\" type=\"o\"/>\n"
113 " </signal>\n"
114 " <signal name=\"NewPlaybackStream\">\n"
115 " <arg name=\"PlaybackStream\" type=\"o\"/>\n"
116 " </signal>\n"
117 " <signal name=\"PlaybackStreamRemoved\">\n"
118 " <arg name=\"PlaybackStream\" type=\"o\"/>\n"
119 " </signal>\n"
120 " <signal name=\"NewRecordStream\">\n"
121 " <arg name=\"RecordStream\" type=\"o\"/>\n"
122 " </signal>\n"
123 " <signal name=\"RecordStreamRemoved\">\n"
124 " <arg name=\"RecordStream\" type=\"o\"/>\n"
125 " </signal>\n"
126 " <signal name=\"NewSample\">\n"
127 " <arg name=\"Sample\" type=\"o\"/>\n"
128 " </signal>\n"
129 " <signal name=\"SampleRemoved\">\n"
130 " <arg name=\"Sample\" type=\"o\"/>\n"
131 " </signal>\n"
132 " <signal name=\"NewModule\">\n"
133 " <arg name=\"Module\" type=\"o\"/>\n"
134 " </signal>\n"
135 " <signal name=\"ModuleRemoved\">\n"
136 " <arg name=\"Module\" type=\"o\"/>\n"
137 " </signal>\n"
138 " <signal name=\"NewClient\">\n"
139 " <arg name=\"Client\" type=\"o\"/>\n"
140 " </signal>\n"
141 " <signal name=\"ClientRemoved\">\n"
142 " <arg name=\"Client\" type=\"o\"/>\n"
143 " </signal>\n"
144 " <property name=\"InterfaceRevision\" type=\"u\" access=\"read\"/>\n"
145 " <property name=\"Name\" type=\"s\" access=\"read\"/>\n"
146 " <property name=\"Version\" type=\"s\" access=\"read\"/>\n"
147 " <property name=\"Username\" type=\"s\" access=\"read\"/>\n"
148 " <property name=\"Hostname\" type=\"s\" access=\"read\"/>\n"
149 " <property name=\"DefaultChannels\" type=\"ay\" access=\"readwrite\"/>\n"
150 " <property name=\"DefaultSampleFormat\" type=\"y\" access=\"readwrite\"/>\n"
151 " <property name=\"DefaultSampleRate\" type=\"u\" access=\"readwrite\"/>\n"
152 " <property name=\"Sinks\" type=\"ao\" access=\"read\"/>\n"
153 " <property name=\"FallbackSink\" type=\"s\" access=\"readwrite\"/>\n"
154 " <property name=\"Sources\" type=\"ao\" access=\"read\"/>\n"
155 " <property name=\"FallbackSource\" type=\"o\" access=\"readwrite\"/>\n"
156 " <property name=\"PlaybackStreams\" type=\"ao\" access=\"read\"/>\n"
157 " <property name=\"RecordStreams\" type=\"ao\" access=\"read\"/>\n"
158 " <property name=\"Samples\" type=\"ao\" access=\"read\"/>\n"
159 " <property name=\"Modules\" type=\"ao\" access=\"read\"/>\n"
160 " <property name=\"Clients\" type=\"ao\" access=\"read\"/>\n"
161 " <property name=\"Extensions\" type=\"as\" access=\"read\"/>\n"
162 " </interface>\n";
163
164 /* If you need to modify this list, note that handle_get_all() uses hard-coded
165 * indexes to point to these strings, so make sure the indexes don't go wrong
166 * there. */
167 static const char *properties[] = {
168 "InterfaceRevision",
169 "Name",
170 "Version",
171 "Username",
172 "Hostname",
173 "DefaultChannels",
174 "DefaultSampleFormat",
175 "DefaultSampleRate",
176 "Sinks",
177 "FallbackSink",
178 "Sources",
179 "FallbackSource",
180 "PlaybackStreams",
181 "RecordStreams",
182 "Samples",
183 "Modules",
184 "Clients",
185 "Extensions",
186 NULL
187 };
188
189 static const char *methods[] = {
190 "GetCardByName",
191 "GetSinkByName",
192 "GetSourceByName",
193 "GetSampleByName",
194 "UploadSample",
195 "LoadSampleFromFile",
196 "AddLazySample",
197 "AddLazySamplesFromDirectory",
198 "LoadModule",
199 "Exit",
200 NULL
201 };
202
203 static DBusHandlerResult handle_get_name(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
204 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
205 DBusMessage *reply = NULL;
206 const char *server_name = PACKAGE_NAME;
207 DBusMessageIter msg_iter;
208 DBusMessageIter variant_iter;
209
210 pa_assert(conn);
211 pa_assert(msg);
212 pa_assert(c);
213
214 if (!(reply = dbus_message_new_method_return(msg))) {
215 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
216 goto finish;
217 }
218 dbus_message_iter_init_append(reply, &msg_iter);
219 if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
220 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
221 goto finish;
222 }
223 if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) {
224 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
225 goto finish;
226 }
227 if (!dbus_message_iter_close_container(&msg_iter, &variant_iter)) {
228 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
229 goto finish;
230 }
231 if (!dbus_connection_send(conn, reply, NULL)) {
232 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
233 goto finish;
234 }
235 r = DBUS_HANDLER_RESULT_HANDLED;
236
237 finish:
238 if (reply)
239 dbus_message_unref(reply);
240
241 return r;
242 }
243
244 static DBusHandlerResult handle_get(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
245 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
246 const char* interface;
247 const char* property;
248 DBusMessage *reply = NULL;
249
250 pa_assert(conn);
251 pa_assert(msg);
252 pa_assert(c);
253
254 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
255 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
256 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
257 goto finish;
258 }
259 if (!dbus_connection_send(conn, reply, NULL)) {
260 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
261 goto finish;
262 }
263 r = DBUS_HANDLER_RESULT_HANDLED;
264 goto finish;
265 }
266
267 if (*interface && !pa_streq(interface, INTERFACE_CORE)) {
268 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
269 goto finish;
270 }
271
272 if (pa_streq(property, "Name")) {
273 r = handle_get_name(conn, msg, c);
274 goto finish;
275 }
276
277 if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
278 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
279 goto finish;
280 }
281 if (!dbus_connection_send(conn, reply, NULL)) {
282 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
283 goto finish;
284 }
285 r = DBUS_HANDLER_RESULT_HANDLED;
286
287 finish:
288 if (reply)
289 dbus_message_unref(reply);
290
291 return r;
292 }
293
294 static DBusHandlerResult handle_set(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
295 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
296 const char* interface;
297 const char* property;
298 DBusMessage *reply = NULL;
299
300 pa_assert(conn);
301 pa_assert(msg);
302 pa_assert(c);
303
304 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID)) {
305 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
306 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
307 goto finish;
308 }
309 if (!dbus_connection_send(conn, reply, NULL)) {
310 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
311 goto finish;
312 }
313 r = DBUS_HANDLER_RESULT_HANDLED;
314 goto finish;
315 }
316
317 if (*interface && !pa_streq(interface, INTERFACE_CORE)) {
318 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
319 goto finish;
320 }
321
322 if (pa_streq(property, "Name")) {
323 if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
324 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
325 goto finish;
326 }
327 if (!dbus_connection_send(conn, reply, NULL)) {
328 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
329 goto finish;
330 }
331 r = DBUS_HANDLER_RESULT_HANDLED;
332 goto finish;
333 }
334
335 if (!(reply = dbus_message_new_error_printf(msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s: No such property", property))) {
336 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
337 goto finish;
338 }
339 if (!dbus_connection_send(conn, reply, NULL)) {
340 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
341 goto finish;
342 }
343 r = DBUS_HANDLER_RESULT_HANDLED;
344 goto finish;
345
346 if (!(reply = dbus_message_new_error_printf(msg, DBUS_ERROR_ACCESS_DENIED, "%s: Property not settable", property))) {
347 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
348 goto finish;
349 }
350 if (!dbus_connection_send(conn, reply, NULL)) {
351 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
352 goto finish;
353 }
354 r = DBUS_HANDLER_RESULT_HANDLED;
355
356 finish:
357 if (reply)
358 dbus_message_unref(reply);
359
360 return r;
361 }
362
363 static DBusHandlerResult handle_get_all(DBusConnection *conn, DBusMessage *msg, pa_dbusobj_core *c) {
364 DBusHandlerResult r = DBUS_HANDLER_RESULT_HANDLED;
365 DBusMessage *reply = NULL;
366 char *interface = NULL;
367 char const *server_name = PACKAGE_NAME;
368 DBusMessageIter msg_iter;
369 DBusMessageIter dict_iter;
370 DBusMessageIter dict_entry_iter;
371 DBusMessageIter variant_iter;
372
373 pa_assert(conn);
374 pa_assert(msg);
375 pa_assert(c);
376
377 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &interface, DBUS_TYPE_INVALID)) {
378 if (!(reply = dbus_message_new_error(msg, DBUS_ERROR_INVALID_ARGS, "Invalid arguments"))) {
379 r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
380 goto finish;
381 }
382 if (!dbus_connection_send(conn, reply, NULL)) {
383 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
384 goto finish;
385 }
386 r = DBUS_HANDLER_RESULT_HANDLED;
387 goto finish;
388 }
389
390 if (!(reply = dbus_message_new_method_return(msg))) {
391 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
392 goto finish;
393 }
394 dbus_message_iter_init_append(reply, &msg_iter);
395 if (!dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter)) {
396 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
397 goto finish;
398 }
399 if (!dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &dict_entry_iter)) {
400 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
401 goto finish;
402 }
403 if (!dbus_message_iter_append_basic(&dict_entry_iter, DBUS_TYPE_STRING, &properties[1])) { /* Name */
404 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
405 goto finish;
406 }
407 if (!dbus_message_iter_open_container(&dict_entry_iter, DBUS_TYPE_VARIANT, "s", &variant_iter)) {
408 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
409 goto finish;
410 }
411 if (!dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, &server_name)) {
412 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
413 goto finish;
414 }
415 if (!dbus_message_iter_close_container(&dict_entry_iter, &variant_iter)) {
416 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
417 goto finish;
418 }
419 if (!dbus_message_iter_close_container(&dict_iter, &dict_entry_iter)) {
420 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
421 goto finish;
422 }
423 if (!dbus_message_iter_close_container(&msg_iter, &dict_iter)) {
424 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
425 goto finish;
426 }
427 if (!dbus_connection_send(conn, reply, NULL)) {
428 r = DBUS_HANDLER_RESULT_NEED_MEMORY;
429 goto finish;
430 }
431 r = DBUS_HANDLER_RESULT_HANDLED;
432
433 finish:
434 if (reply)
435 dbus_message_unref(reply);
436
437 return r;
438 }
439
440 static DBusHandlerResult receive_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
441 pa_dbusobj_core *c = user_data;
442
443 pa_assert(connection);
444 pa_assert(message);
445 pa_assert(c);
446
447 if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Get") ||
448 (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Get")))
449 return handle_get(connection, message, c);
450
451 if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "Set") ||
452 (!dbus_message_get_interface(message) && dbus_message_has_member(message, "Set")))
453 return handle_set(connection, message, c);
454
455 if (dbus_message_is_method_call(message, DBUS_INTERFACE_PROPERTIES, "GetAll") ||
456 (!dbus_message_get_interface(message) && dbus_message_has_member(message, "GetAll")))
457 return handle_get_all(connection, message, c);
458
459 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
460 }
461
462 pa_dbusobj_core *pa_dbusobj_core_new(pa_core *core) {
463 pa_dbusobj_core *c;
464
465 pa_assert(core);
466
467 c = pa_xnew(pa_dbusobj_core, 1);
468 c->core = core;
469
470 pa_dbus_add_interface(core, OBJECT_PATH, INTERFACE_CORE, properties, methods, introspection_snippet, receive_cb, c);
471
472
473 return c;
474 }
475
476 void pa_dbusobj_core_free(pa_dbusobj_core *c) {
477 pa_assert(c);
478
479 pa_dbus_remove_interface(c->core, OBJECT_PATH, INTERFACE_CORE);
480
481 pa_xfree(c);
482 }