]> code.delx.au - pulseaudio/blob - src/modules/dbus-util.c
Merge commit 'origin/master-tx'
[pulseaudio] / src / modules / 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 #include <pulsecore/log.h>
32 #include <pulsecore/shared.h>
33
34 #include "dbus-util.h"
35
36 struct pa_dbus_connection {
37 PA_REFCNT_DECLARE;
38
39 pa_core *core;
40 DBusConnection *connection;
41 const char *property_name;
42 pa_defer_event* dispatch_event;
43 };
44
45 static void dispatch_cb(pa_mainloop_api *ea, pa_defer_event *ev, void *userdata) {
46 DBusConnection *conn = userdata;
47
48 if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_COMPLETE) {
49 /* no more data to process, disable the deferred */
50 ea->defer_enable(ev, 0);
51 }
52 }
53
54 /* DBusDispatchStatusFunction callback for the pa mainloop */
55 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus status, void *userdata) {
56 pa_dbus_connection *c = userdata;
57
58 pa_assert(c);
59
60 switch(status) {
61
62 case DBUS_DISPATCH_COMPLETE:
63 c->core->mainloop->defer_enable(c->dispatch_event, 0);
64 break;
65
66 case DBUS_DISPATCH_DATA_REMAINS:
67 case DBUS_DISPATCH_NEED_MEMORY:
68 default:
69 c->core->mainloop->defer_enable(c->dispatch_event, 1);
70 break;
71 }
72 }
73
74 static pa_io_event_flags_t get_watch_flags(DBusWatch *watch) {
75 unsigned int flags;
76 pa_io_event_flags_t events = 0;
77
78 pa_assert(watch);
79
80 flags = dbus_watch_get_flags(watch);
81
82 /* no watch flags for disabled watches */
83 if (!dbus_watch_get_enabled(watch))
84 return PA_IO_EVENT_NULL;
85
86 if (flags & DBUS_WATCH_READABLE)
87 events |= PA_IO_EVENT_INPUT;
88 if (flags & DBUS_WATCH_WRITABLE)
89 events |= PA_IO_EVENT_OUTPUT;
90
91 return events | PA_IO_EVENT_HANGUP | PA_IO_EVENT_ERROR;
92 }
93
94 /* pa_io_event_cb_t IO event handler */
95 static void handle_io_event(pa_mainloop_api *ea, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
96 unsigned int flags = 0;
97 DBusWatch *watch = userdata;
98
99 #if HAVE_DBUS_WATCH_GET_UNIX_FD
100 pa_assert(fd == dbus_watch_get_unix_fd(watch));
101 #else
102 pa_assert(fd == dbus_watch_get_fd(watch));
103 #endif
104
105 if (!dbus_watch_get_enabled(watch)) {
106 pa_log_warn("Asked to handle disabled watch: %p %i", (void*) watch, fd);
107 return;
108 }
109
110 if (events & PA_IO_EVENT_INPUT)
111 flags |= DBUS_WATCH_READABLE;
112 if (events & PA_IO_EVENT_OUTPUT)
113 flags |= DBUS_WATCH_WRITABLE;
114 if (events & PA_IO_EVENT_HANGUP)
115 flags |= DBUS_WATCH_HANGUP;
116 if (events & PA_IO_EVENT_ERROR)
117 flags |= DBUS_WATCH_ERROR;
118
119 dbus_watch_handle(watch, flags);
120 }
121
122 /* pa_time_event_cb_t timer event handler */
123 static void handle_time_event(pa_mainloop_api *ea, pa_time_event* e, const struct timeval *tv, void *userdata) {
124 DBusTimeout *timeout = userdata;
125
126 if (dbus_timeout_get_enabled(timeout)) {
127 struct timeval next = *tv;
128 dbus_timeout_handle(timeout);
129
130 /* restart it for the next scheduled time */
131 pa_timeval_add(&next, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
132 ea->time_restart(e, &next);
133 }
134 }
135
136 /* DBusAddWatchFunction callback for pa mainloop */
137 static dbus_bool_t add_watch(DBusWatch *watch, void *data) {
138 pa_core *c = PA_CORE(data);
139 pa_io_event *ev;
140
141 pa_assert(watch);
142 pa_assert(c);
143
144 ev = c->mainloop->io_new(
145 c->mainloop,
146 #if HAVE_DBUS_WATCH_GET_UNIX_FD
147 dbus_watch_get_unix_fd(watch),
148 #else
149 dbus_watch_get_fd(watch),
150 #endif
151 get_watch_flags(watch), handle_io_event, watch);
152
153 dbus_watch_set_data(watch, ev, NULL);
154
155 return TRUE;
156 }
157
158 /* DBusRemoveWatchFunction callback for pa mainloop */
159 static void remove_watch(DBusWatch *watch, void *data) {
160 pa_core *c = PA_CORE(data);
161 pa_io_event *ev;
162
163 pa_assert(watch);
164 pa_assert(c);
165
166 if ((ev = dbus_watch_get_data(watch)))
167 c->mainloop->io_free(ev);
168 }
169
170 /* DBusWatchToggledFunction callback for pa mainloop */
171 static void toggle_watch(DBusWatch *watch, void *data) {
172 pa_core *c = PA_CORE(data);
173 pa_io_event *ev;
174
175 pa_assert(watch);
176 pa_core_assert_ref(c);
177
178 pa_assert_se(ev = dbus_watch_get_data(watch));
179
180 /* get_watch_flags() checks if the watch is enabled */
181 c->mainloop->io_enable(ev, get_watch_flags(watch));
182 }
183
184 /* DBusAddTimeoutFunction callback for pa mainloop */
185 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data) {
186 pa_core *c = PA_CORE(data);
187 pa_time_event *ev;
188 struct timeval tv;
189
190 pa_assert(timeout);
191 pa_assert(c);
192
193 if (!dbus_timeout_get_enabled(timeout))
194 return FALSE;
195
196 pa_gettimeofday(&tv);
197 pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
198
199 ev = c->mainloop->time_new(c->mainloop, &tv, handle_time_event, timeout);
200
201 dbus_timeout_set_data(timeout, ev, NULL);
202
203 return TRUE;
204 }
205
206 /* DBusRemoveTimeoutFunction callback for pa mainloop */
207 static void remove_timeout(DBusTimeout *timeout, void *data) {
208 pa_core *c = PA_CORE(data);
209 pa_time_event *ev;
210
211 pa_assert(timeout);
212 pa_assert(c);
213
214 if ((ev = dbus_timeout_get_data(timeout)))
215 c->mainloop->time_free(ev);
216 }
217
218 /* DBusTimeoutToggledFunction callback for pa mainloop */
219 static void toggle_timeout(DBusTimeout *timeout, void *data) {
220 pa_core *c = PA_CORE(data);
221 pa_time_event *ev;
222
223 pa_assert(timeout);
224 pa_assert(c);
225
226 pa_assert_se(ev = dbus_timeout_get_data(timeout));
227
228 if (dbus_timeout_get_enabled(timeout)) {
229 struct timeval tv;
230
231 pa_gettimeofday(&tv);
232 pa_timeval_add(&tv, (pa_usec_t) dbus_timeout_get_interval(timeout) * 1000);
233
234 c->mainloop->time_restart(ev, &tv);
235 } else
236 c->mainloop->time_restart(ev, NULL);
237 }
238
239 static void wakeup_main(void *userdata) {
240 pa_dbus_connection *c = userdata;
241
242 pa_assert(c);
243
244 /* this will wakeup the mainloop and dispatch events, although
245 * it may not be the cleanest way of accomplishing it */
246 c->core->mainloop->defer_enable(c->dispatch_event, 1);
247 }
248
249 static pa_dbus_connection* pa_dbus_connection_new(pa_core* c, DBusConnection *conn, const char* name) {
250 pa_dbus_connection *pconn;
251
252 pconn = pa_xnew(pa_dbus_connection, 1);
253 PA_REFCNT_INIT(pconn);
254 pconn->core = c;
255 pconn->property_name = name;
256 pconn->connection = conn;
257 pconn->dispatch_event = c->mainloop->defer_new(c->mainloop, dispatch_cb, conn);
258
259 pa_shared_set(c, name, pconn);
260
261 return pconn;
262 }
263
264 DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
265 pa_assert(c);
266 pa_assert(PA_REFCNT_VALUE(c) > 0);
267 pa_assert(c->connection);
268
269 return c->connection;
270 }
271
272 void pa_dbus_connection_unref(pa_dbus_connection *c) {
273 pa_assert(c);
274 pa_assert(PA_REFCNT_VALUE(c) > 0);
275
276 if (PA_REFCNT_DEC(c) > 0)
277 return;
278
279 if (dbus_connection_get_is_connected(c->connection)) {
280 dbus_connection_close(c->connection);
281 /* must process remaining messages, bit of a kludge to handle
282 * both unload and shutdown */
283 while (dbus_connection_read_write_dispatch(c->connection, -1));
284 }
285
286 /* already disconnected, just free */
287 pa_shared_remove(c->core, c->property_name);
288 c->core->mainloop->defer_free(c->dispatch_event);
289 dbus_connection_unref(c->connection);
290 pa_xfree(c);
291 }
292
293 pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
294 pa_assert(c);
295 pa_assert(PA_REFCNT_VALUE(c) > 0);
296
297 PA_REFCNT_INC(c);
298
299 return c;
300 }
301
302 pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *error) {
303
304 static const char *const prop_name[] = {
305 [DBUS_BUS_SESSION] = "dbus-connection-session",
306 [DBUS_BUS_SYSTEM] = "dbus-connection-system",
307 [DBUS_BUS_STARTER] = "dbus-connection-starter"
308 };
309 DBusConnection *conn;
310 pa_dbus_connection *pconn;
311
312 pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
313
314 if ((pconn = pa_shared_get(c, prop_name[type])))
315 return pa_dbus_connection_ref(pconn);
316
317 if (!(conn = dbus_bus_get_private(type, error)))
318 return NULL;
319
320 pconn = pa_dbus_connection_new(c, conn, prop_name[type]);
321
322 dbus_connection_set_exit_on_disconnect(conn, FALSE);
323 dbus_connection_set_dispatch_status_function(conn, dispatch_status, pconn, NULL);
324 dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggle_watch, c, NULL);
325 dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout, toggle_timeout, c, NULL);
326 dbus_connection_set_wakeup_main_function(conn, wakeup_main, pconn, NULL);
327
328 return pconn;
329 }
330
331 int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
332 const char *t;
333 va_list ap;
334 unsigned k = 0;
335
336 pa_assert(c);
337 pa_assert(error);
338
339 va_start(ap, error);
340 while ((t = va_arg(ap, const char*))) {
341 dbus_bus_add_match(c, t, error);
342
343 if (dbus_error_is_set(error))
344 goto fail;
345
346 k++;
347 }
348 va_end(ap);
349 return 0;
350
351 fail:
352
353 va_end(ap);
354 va_start(ap, error);
355 for (; k > 0; k--) {
356 DBusError e;
357
358 pa_assert_se(t = va_arg(ap, const char*));
359
360 dbus_error_init(&e);
361 dbus_bus_remove_match(c, t, &e);
362 dbus_error_free(&e);
363 }
364 va_end(ap);
365
366 return -1;
367 }
368
369 void pa_dbus_remove_matches(DBusConnection *c, ...) {
370 const char *t;
371 va_list ap;
372 DBusError error;
373
374 pa_assert(c);
375
376 dbus_error_init(&error);
377
378 va_start(ap, c);
379 while ((t = va_arg(ap, const char*))) {
380 dbus_bus_remove_match(c, t, &error);
381 dbus_error_free(&error);
382 }
383 va_end(ap);
384 }
385
386 pa_dbus_pending *pa_dbus_pending_new(DBusMessage *m, DBusPendingCall *pending, void *context_data, void *call_data) {
387 pa_dbus_pending *p;
388
389 pa_assert(pending);
390
391 p = pa_xnew(pa_dbus_pending, 1);
392 p->message = m;
393 p->pending = pending;
394 p->context_data = context_data;
395 p->call_data = call_data;
396
397 PA_LLIST_INIT(pa_dbus_pending, p);
398
399 return p;
400 }
401
402 void pa_dbus_pending_free(pa_dbus_pending *p) {
403 pa_assert(p);
404
405 if (p->pending) {
406 dbus_pending_call_cancel(p->pending); /* p->pending is freed by cancel() */
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)
419 dbus_pending_call_block((*p)->pending);
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 }