]> code.delx.au - pulseaudio/blob - src/pulsecore/dbus-util.c
dbus: memory leak, free pending calls
[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 void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
280 pa_assert(c);
281
282 if (dbus_connection_get_is_connected(c->connection)) {
283 dbus_connection_close(c->connection);
284 /* must process remaining messages, bit of a kludge to handle
285 * both unload and shutdown */
286 while (dbus_connection_read_write_dispatch(c->connection, -1))
287 ;
288 }
289
290 c->mainloop->defer_free(c->dispatch_event);
291 dbus_connection_unref(c->connection);
292 pa_xfree(c);
293 }
294
295 DBusConnection* pa_dbus_wrap_connection_get(pa_dbus_wrap_connection *c) {
296 pa_assert(c);
297 pa_assert(c->connection);
298
299 return c->connection;
300 }
301
302 int pa_dbus_add_matches(DBusConnection *c, DBusError *error, ...) {
303 const char *t;
304 va_list ap;
305 unsigned k = 0;
306
307 pa_assert(c);
308 pa_assert(error);
309
310 va_start(ap, error);
311 while ((t = va_arg(ap, const char*))) {
312 dbus_bus_add_match(c, t, error);
313
314 if (dbus_error_is_set(error))
315 goto fail;
316
317 k++;
318 }
319 va_end(ap);
320 return 0;
321
322 fail:
323
324 va_end(ap);
325 va_start(ap, error);
326 for (; k > 0; k--) {
327 DBusError e;
328
329 pa_assert_se(t = va_arg(ap, const char*));
330
331 dbus_error_init(&e);
332 dbus_bus_remove_match(c, t, &e);
333 dbus_error_free(&e);
334 }
335 va_end(ap);
336
337 return -1;
338 }
339
340 void pa_dbus_remove_matches(DBusConnection *c, ...) {
341 const char *t;
342 va_list ap;
343 DBusError error;
344
345 pa_assert(c);
346
347 dbus_error_init(&error);
348
349 va_start(ap, c);
350 while ((t = va_arg(ap, const char*))) {
351 dbus_bus_remove_match(c, t, &error);
352 dbus_error_free(&error);
353 }
354 va_end(ap);
355 }
356
357 pa_dbus_pending *pa_dbus_pending_new(
358 DBusConnection *c,
359 DBusMessage *m,
360 DBusPendingCall *pending,
361 void *context_data,
362 void *call_data) {
363
364 pa_dbus_pending *p;
365
366 pa_assert(pending);
367
368 p = pa_xnew(pa_dbus_pending, 1);
369 p->connection = c;
370 p->message = m;
371 p->pending = pending;
372 p->context_data = context_data;
373 p->call_data = call_data;
374
375 PA_LLIST_INIT(pa_dbus_pending, p);
376
377 return p;
378 }
379
380 void pa_dbus_pending_free(pa_dbus_pending *p) {
381 pa_assert(p);
382
383 if (p->pending) {
384 dbus_pending_call_cancel(p->pending);
385 dbus_pending_call_unref(p->pending);
386 }
387
388 if (p->message)
389 dbus_message_unref(p->message);
390
391 pa_xfree(p);
392 }
393
394 void pa_dbus_sync_pending_list(pa_dbus_pending **p) {
395 pa_assert(p);
396
397 while (*p && dbus_connection_read_write_dispatch((*p)->connection, -1))
398 ;
399 }
400
401 void pa_dbus_free_pending_list(pa_dbus_pending **p) {
402 pa_dbus_pending *i;
403
404 pa_assert(p);
405
406 while ((i = *p)) {
407 PA_LLIST_REMOVE(pa_dbus_pending, *p, i);
408 pa_dbus_pending_free(i);
409 }
410 }