]> code.delx.au - pulseaudio/blob - src/pulsecore/dbus-util.c
module-dbus-protocol: Allow anyone to connect the daemon in system mode.
[pulseaudio] / src / pulsecore / dbus-util.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2006 Shams E. King
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdarg.h>
28
29 #include <pulse/xmalloc.h>
30 #include <pulse/timeval.h>
31
32 #include <pulsecore/core-util.h>
33 #include <pulsecore/log.h>
34
35 #include "dbus-util.h"
36
37 struct pa_dbus_wrap_connection {
38 pa_mainloop_api *mainloop;
39 DBusConnection *connection;
40 pa_defer_event* dispatch_event;
41 };
42
43 static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
44 DBusConnection *conn = userdata;
45
46 if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
47 /* no more data to process, disable the deferred */
48 ea->defer_enable(ev, 0);
49 }
50 }
51
52 /* DBusDispatchStatusFunction callback for the pa mainloop */
53 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
54 pa_dbus_wrap_connection *c = userdata;
55
56 pa_assert(c);
57
58 switch(status) {
59
60 case DBUS_DISPATCH_COMPLETE:
61 c->mainloop->defer_enable(c->dispatch_event, 0);
62 break;
63
64 case DBUS_DISPATCH_DATA_REMAINS:
65 case DBUS_DISPATCH_NEED_MEMORY:
66 default:
67 c->mainloop->defer_enable(c->dispatch_event, 1);
68 break;
69 }
70 }
71
72 static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
73 unsigned int flags;
74 pa_io_event_flags_t events = 0;
75
76 pa_assert(watch);
77
78 flags = dbus_watch_get_flags(watch);
79
80 /* no watch flags for disabled watches */
81 if (!dbus_watch_get_enabled(watch))
82 return PA_IO_EVENT_NULL;
83
84 if (flags & DBUS_WATCH_READABLE)
85 events |= PA_IO_EVENT_INPUT;
86 if (flags & DBUS_WATCH_WRITABLE)
87 events |= PA_IO_EVENT_OUTPUT;
88
89 return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
90 }
91
92 /* pa_io_event_cb_t IO event handler */
93 static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
94 unsigned int flags = 0;
95 DBusWatch *watch = userdata;
96
97 #if HAVE_DBUS_WATCH_GET_UNIX_FD
98 pa_assert(fd == dbus_watch_get_unix_fd(watch));
99 #else
100 pa_assert(fd == dbus_watch_get_fd(watch));
101 #endif
102
103 if (!dbus_watch_get_enabled(watch)) {
104 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
105 return;
106 }
107
108 if (events & PA_IO_EVENT_INPUT)
109 flags |= DBUS_WATCH_READABLE;
110 if (events & PA_IO_EVENT_OUTPUT)
111 flags |= DBUS_WATCH_WRITABLE;
112 if (events & PA_IO_EVENT_HANGUP)
113 flags |= DBUS_WATCH_HANGUP;
114 if (events & PA_IO_EVENT_ERROR)
115 flags |= DBUS_WATCH_ERROR;
116
117 dbus_watch_handle(watch, flags);
118 }
119
120 /* pa_time_event_cb_t timer event handler */
121 static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) {
122 DBusTimeout *timeout = userdata;
123
124 if (dbus_timeout_get_enabled(timeout)) {
125 struct timeval next = *tv;
126 dbus_timeout_handle(timeout);
127
128 /* restart it for the next scheduled time */
129 pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
130 ea->time_restart(e, &next);
131 }
132 }
133
134 /* DBusAddWatchFunction callback for pa mainloop */
135 static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
136 pa_dbus_wrap_connection *c = data;
137 pa_io_event *ev;
138
139 pa_assert(watch);
140 pa_assert(c);
141
142 ev = c->mainloop->io_new(
143 c->mainloop,
144 #if HAVE_DBUS_WATCH_GET_UNIX_FD
145 dbus_watch_get_unix_fd(watch),
146 #else
147 dbus_watch_get_fd(watch),
148 #endif
149 get_watch_flags(watch), handle_io_event, watch);
150
151 dbus_watch_set_data(watch, ev, NULL);
152
153 return TRUE;
154 }
155
156 /* DBusRemoveWatchFunction callback for pa mainloop */
157 static void remove_watch(DBusWatch *watch, void *data) {
158 pa_dbus_wrap_connection *c = data;
159 pa_io_event *ev;
160
161 pa_assert(watch);
162 pa_assert(c);
163
164 if ((ev = dbus_watch_get_data(watch)))
165 c->mainloop->io_free(ev);
166 }
167
168 /* DBusWatchToggledFunction callback for pa mainloop */
169 static void toggle_watch(DBusWatch *watch, void *data) {
170 pa_dbus_wrap_connection *c = data;
171 pa_io_event *ev;
172
173 pa_assert(watch);
174 pa_assert(c);
175
176 pa_assert_se(ev = dbus_watch_get_data(watch));
177
178 /* get_watch_flags() checks if the watch is enabled */
179 c->mainloop->io_enable(ev, get_watch_flags(watch));
180 }
181
182 /* DBusAddTimeoutFunction callback for pa mainloop */
183 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
184 pa_dbus_wrap_connection *c = data;
185 pa_time_event *ev;
186 struct timeval tv;
187
188 pa_assert(timeout);
189 pa_assert(c);
190
191 if (!dbus_timeout_get_enabled(timeout))
192 return FALSE;
193
194 pa_gettimeofday(&tv);
195 pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
196
197 ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout);
198
199 dbus_timeout_set_data(timeout, ev, NULL);
200
201 return TRUE;
202 }
203
204 /* DBusRemoveTimeoutFunction callback for pa mainloop */
205 static void remove_timeout(DBusTimeout *timeout, void *data) {
206 pa_dbus_wrap_connection *c = data;
207 pa_time_event *ev;
208
209 pa_assert(timeout);
210 pa_assert(c);
211
212 if ((ev = dbus_timeout_get_data(timeout)))
213 c->mainloop->time_free(ev);
214 }
215
216 /* DBusTimeoutToggledFunction callback for pa mainloop */
217 static void toggle_timeout(DBusTimeout *timeout, void *data) {
218 pa_dbus_wrap_connection *c = data;
219 pa_time_event *ev;
220
221 pa_assert(timeout);
222 pa_assert(c);
223
224 pa_assert_se(ev = dbus_timeout_get_data(timeout));
225
226 if (dbus_timeout_get_enabled(timeout)) {
227 struct timeval tv;
228
229 pa_gettimeofday(&tv);
230 pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
231
232 c->mainloop->time_restart(ev, &tv);
233 } else
234 c->mainloop->time_restart(ev, NULL);
235 }
236
237 static void wakeup_main(void *userdata) {
238 pa_dbus_wrap_connection *c = userdata;
239
240 pa_assert(c);
241
242 /* this will wakeup the mainloop and dispatch events, although
243 * it may not be the cleanest way of accomplishing it */
244 c->mainloop->defer_enable(c->dispatch_event, 1);
245 }
246
247 pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBusType type, DBusError *error) {
248 DBusConnection *conn;
249 pa_dbus_wrap_connection *pconn;
250 char *id;
251
252 pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
253
254 if (!(conn = dbus_bus_get_private(type, error)))
255 return NULL;
256
257 pconn = pa_xnew(pa_dbus_wrap_connection, 1);
258 pconn->mainloop = m;
259 pconn->connection = conn;
260
261 dbus_connection_set_exit_on_disconnect(conn, FALSE);
262 dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
263 dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
264 dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
265 dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
266
267 pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
268
269 pa_log_debug("Successfully connected to D-Bus %s bus %s as %s",
270 type == DBUS_BUS_SYSTEM ? "system" : (type == DBUS_BUS_SESSION ? "session" : "starter"),
271 pa_strnull((id = dbus_connection_get_server_id(conn))),
272 pa_strnull(dbus_bus_get_unique_name(conn)));
273
274 dbus_free(id);
275
276 return pconn;
277 }
278
279 pa_dbus_wrap_connection* pa_dbus_wrap_connection_new_from_existing(pa_mainloop_api *m, DBusConnection *conn) {
280 pa_dbus_wrap_connection *pconn;
281
282 pa_assert(m);
283 pa_assert(conn);
284
285 pconn = pa_xnew(pa_dbus_wrap_connection, 1);
286 pconn->mainloop = m;
287 pconn->connection = dbus_connection_ref(conn);
288
289 dbus_connection_set_exit_on_disconnect(conn, FALSE);
290 dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
291 dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, pconn, NULL);
292 dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, pconn, NULL);
293 dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
294
295 pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
296
297 return pconn;
298 }
299
300 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
301 pa_assert(c);
302
303 if (dbus_connection_get_is_connected(c->connection)) {
304 dbus_connection_close(c->connection);
305 /* must process remaining messages, bit of a kludge to handle
306 * both unload and shutdown */
307 while (dbus_connection_read_write_dispatch(c->connection, -1))
308 ;
309 }
310
311 c->mainloop->defer_free(c->dispatch_event);
312 dbus_connection_unref(c->connection);
313 pa_xfree(c);
314 }
315
316 DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
317 pa_assert(c);
318 pa_assert(c->connection);
319
320 return c->connection;
321 }
322
323 int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
324 const char *t;
325 va_list ap;
326 unsigned k = 0;
327
328 pa_assert(c);
329 pa_assert(error);
330
331 va_start(ap, error);
332 while ((t = va_arg(ap, const char*))) {
333 dbus_bus_add_match(c, t, error);
334
335 if (dbus_error_is_set(error))
336 goto fail;
337
338 k++;
339 }
340 va_end(ap);
341 return 0;
342
343 fail:
344
345 va_end(ap);
346 va_start(ap, error);
347 for (; k > 0; k--) {
348 DBusError e;
349
350 pa_assert_se(t = va_arg(ap, const char*));
351
352 dbus_error_init(&e);
353 dbus_bus_remove_match(c, t, &e);
354 dbus_error_free(&e);
355 }
356 va_end(ap);
357
358 return -1;
359 }
360
361 void pa_dbus_remove_matches(DBusConnection *c, ...) {
362 const char *t;
363 va_list ap;
364 DBusError error;
365
366 pa_assert(c);
367
368 dbus_error_init(&error);
369
370 va_start(ap, c);
371 while ((t = va_arg(ap, const char*))) {
372 dbus_bus_remove_match(c, t, &error);
373 dbus_error_free(&error);
374 }
375 va_end(ap);
376 }
377
378 pa_dbus_pending *pa_dbus_pending_new(
379 DBusConnection *c,
380 DBusMessage *m,
381 DBusPendingCall *pending,
382 void *context_data,
383 void *call_data) {
384
385 pa_dbus_pending *p;
386
387 pa_assert(pending);
388
389 p = pa_xnew(pa_dbus_pending, 1);
390 p->connection = c;
391 p->message = m;
392 p->pending = pending;
393 p->context_data = context_data;
394 p->call_data = call_data;
395
396 PA_LLIST_INIT(pa_dbus_pending, p);
397
398 return p;
399 }
400
401 void pa_dbus_pending_free(pa_dbus_pending *p) {
402 pa_assert(p);
403
404 if (p->pending) {
405 dbus_pending_call_cancel(p->pending);
406 dbus_pending_call_unref(p->pending);
407 }
408
409 if (p->message)
410 dbus_message_unref(p->message);
411
412 pa_xfree(p);
413 }
414
415 void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
416 pa_assert(p);
417
418 while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
419 ;
420 }
421
422 void pa_dbus_free_pending_list(pa_dbus_pending **p) {
423 pa_dbus_pending *i;
424
425 pa_assert(p);
426
427 while ((i = *p)) {
428 PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
429 pa_dbus_pending_free(i);
430 }
431 }