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