1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* egg-dbus.c GLib main loop integration
3  *
4  * Copyright (C) 2002, 2003 CodeFactory AB
5  * Copyright (C) 2005 Red Hat, Inc.
6  *
7  * Licensed under the Academic Free License version 2.1
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24 
25 
26 #include "config.h"
27 
28 #include "egg-dbus.h"
29 
30 #include <dbus/dbus.h>
31 
32 /*
33  * dbus_watch_get_unix_fd() is introduced in dbus 1.1.1, deprecating
34  * dbus_watch_get_fd(). We still need to use the old function for official
35  * GNOME 2.20, anyway. See Bug #465936.
36  */
37 #define VER_LESS_THAN(MAJOR, MINOR, MICRO, J, N, C) \
38     (MAJOR < J || (MAJOR == J && (MINOR < N || (MINOR == N && MICRO < C))))
strip_trailoptnull39 #if VER_LESS_THAN(GKR_DBUS_MAJOR_VERSION, GKR_DBUS_MINOR_VERSION, GKR_DBUS_MICRO_VERSION, 1, 1, 1)
40 # define dbus_watch_get_unix_fd dbus_watch_get_fd
41 #endif
42 
43 /* ------------------------------------------------------------------------
44  * DBUS GLIB MAIN LOOP INTEGRATION
45  *
46  * Copied from dbus-gmain.c due to API instabilities in dbus-glib bindings. :(
47  */
48 
49 typedef struct {
50 	GSource source;             /* the parent GSource */
51 	DBusConnection *connection; /* the connection to dispatch */
52 } DBusGMessageQueue;
53 
54 static gboolean
55 message_queue_prepare (GSource *source, gint *timeout)
56 {
57 	DBusConnection *connection = ((DBusGMessageQueue *)source)->connection;
58 	*timeout = -1;
59 	return (dbus_connection_get_dispatch_status (connection) == DBUS_DISPATCH_DATA_REMAINS);
60 }
61 
62 static gboolean
63 message_queue_check (GSource *source)
64 {
65 	return FALSE;
66 }
67 
68 static gboolean
69 message_queue_dispatch (GSource *source, GSourceFunc  callback, gpointer user_data)
70 {
71 	DBusConnection *connection = ((DBusGMessageQueue *)source)->connection;
72 	dbus_connection_ref (connection);
73 
74 	/* Only dispatch once - we don't want to starve other GSource */
75 	dbus_connection_dispatch (connection);
76 	dbus_connection_unref (connection);
77 	return TRUE;
78 }
79 
80 static const GSourceFuncs message_queue_funcs = {
81 	message_queue_prepare,
82 	message_queue_check,
83 	message_queue_dispatch,
84 	NULL
85 };
86 
87 typedef struct {
88 	GMainContext *context;         /* the main context */
89 	GSList *ios;                   /* all IOHandler */
90 	GSList *timeouts;              /* all TimeoutHandler */
91 	DBusConnection *connection;    /* NULL if this is really for a server not a connection */
92 	GSource *message_queue_source; /* DBusGMessageQueue */
93 } ConnectionSetup;
94 
95 static ConnectionSetup *the_setup = NULL;
96 
97 typedef struct {
98 	ConnectionSetup *cs;
99 	GSource *source;
100 	DBusWatch *watch;
101 } IOHandler;
102 
103 typedef struct {
104 	ConnectionSetup *cs;
105 	GSource *source;
106 	DBusTimeout *timeout;
107 } TimeoutHandler;
108 
109 static ConnectionSetup*
110 connection_setup_new (GMainContext *context, DBusConnection *connection)
111 {
112 	ConnectionSetup *cs = g_new0 (ConnectionSetup, 1);
113 	g_assert (context != NULL);
114 
115 	cs->context = context;
116 	g_main_context_ref (cs->context);
117 
118 	if (connection) {
119 		cs->connection = connection;
120 		cs->message_queue_source = g_source_new ((GSourceFuncs *) &message_queue_funcs,
121 		                                         sizeof (DBusGMessageQueue));
122 		((DBusGMessageQueue*)cs->message_queue_source)->connection = connection;
123 		g_source_attach (cs->message_queue_source, cs->context);
124 	}
125 
126 	return cs;
127 }
128 
129 static void
130 io_handler_source_finalized (gpointer data)
131 {
132 	IOHandler *handler = data;
133 	if (handler->watch)
134 		dbus_watch_set_data (handler->watch, NULL, NULL);
135 	g_free (handler);
136 }
137 
138 static void
139 io_handler_destroy_source (void *data)
140 {
141 	IOHandler *handler = data;
142 	if (handler->source) {
143 		GSource *source = handler->source;
144 		handler->source = NULL;
145 		handler->cs->ios = g_slist_remove (handler->cs->ios, handler);
146 		g_source_destroy (source);
147 		g_source_unref (source);
148 	}
149 }
150 
151 static void
152 io_handler_watch_freed (void *data)
153 {
154 	IOHandler *handler = data;
155 	handler->watch = NULL;
156 	io_handler_destroy_source (handler);
157 }
158 
159 static gboolean
160 io_handler_dispatch (GIOChannel *source, GIOCondition condition, gpointer data)
161 {
162 	IOHandler *handler = data;
163 	guint dbus_condition = 0;
164 	DBusConnection *connection = handler->cs->connection;
165 
166 	if (connection)
167 		dbus_connection_ref (connection);
168 
169 	if (condition & G_IO_IN)
170 		dbus_condition |= DBUS_WATCH_READABLE;
171 	if (condition & G_IO_OUT)
172 		dbus_condition |= DBUS_WATCH_WRITABLE;
173 	if (condition & G_IO_ERR)
174 		dbus_condition |= DBUS_WATCH_ERROR;
175 	if (condition & G_IO_HUP)
176 		dbus_condition |= DBUS_WATCH_HANGUP;
177 
178 	/* Note that we don't touch the handler after this, because
179 	 * dbus may have disabled the watch and thus killed the
180 	 * handler.
181 	 */
182 	dbus_watch_handle (handler->watch, dbus_condition);
183 	handler = NULL;
184 
185 	if (connection)
186 		dbus_connection_unref (connection);
187 
188 	return TRUE;
189 }
190 
191 static void
192 connection_setup_add_watch (ConnectionSetup *cs, DBusWatch *watch)
193 {
194 	guint flags;
195 	GIOCondition condition;
196 	GIOChannel *channel;
197 	IOHandler *handler;
198 
199 	if (!dbus_watch_get_enabled (watch))
200 		return;
201 
202 	g_assert (dbus_watch_get_data (watch) == NULL);
203 
204 	flags = dbus_watch_get_flags (watch);
205 
206 	condition = G_IO_ERR | G_IO_HUP;
207 	if (flags & DBUS_WATCH_READABLE)
208 		condition |= G_IO_IN;
209 	if (flags & DBUS_WATCH_WRITABLE)
210 		condition |= G_IO_OUT;
211 
212 	handler = g_new0 (IOHandler, 1);
213 	handler->cs = cs;
214 	handler->watch = watch;
215 
216 	channel = g_io_channel_unix_new (dbus_watch_get_unix_fd (watch));
217 
218 	handler->source = g_io_create_watch (channel, condition);
219 	g_source_set_callback (handler->source, (GSourceFunc) io_handler_dispatch, handler,
220 	                       io_handler_source_finalized);
221 	g_source_attach (handler->source, cs->context);
222 
223 	cs->ios = g_slist_prepend (cs->ios, handler);
224 
225 	dbus_watch_set_data (watch, handler, io_handler_watch_freed);
226 	g_io_channel_unref (channel);
227 }
228 
229 static void
230 connection_setup_remove_watch (ConnectionSetup *cs, DBusWatch *watch)
231 {
232 	IOHandler *handler = dbus_watch_get_data (watch);
233 	if (handler != NULL)
234 		io_handler_destroy_source (handler);
235 }
236 
237 static void
238 timeout_handler_source_finalized (gpointer data)
239 {
240 	TimeoutHandler *handler = data;
241 	if (handler->timeout)
242 		dbus_timeout_set_data (handler->timeout, NULL, NULL);
243 	g_free (handler);
244 }
245 
246 static void
247 timeout_handler_destroy_source (void *data)
248 {
249 	TimeoutHandler *handler = data;
250 	if (handler->source) {
251 		GSource *source = handler->source;
252 		handler->source = NULL;
253 		handler->cs->timeouts = g_slist_remove (handler->cs->timeouts, handler);
254 		g_source_destroy (source);
255 		g_source_unref (source);
256 	}
257 }
258 
259 static void
260 timeout_handler_timeout_freed (void *data)
261 {
262 	TimeoutHandler *handler = data;
263 	handler->timeout = NULL;
264 	timeout_handler_destroy_source (handler);
265 }
266 
267 static gboolean
268 timeout_handler_dispatch (gpointer      data)
269 {
270 	TimeoutHandler *handler = data;
271 	dbus_timeout_handle (handler->timeout);
272 	return TRUE;
273 }
274 
275 static void
276 connection_setup_add_timeout (ConnectionSetup *cs,
277                               DBusTimeout     *timeout)
278 {
279 	TimeoutHandler *handler;
280 	if (!dbus_timeout_get_enabled (timeout))
281 		return;
282 	g_assert (dbus_timeout_get_data (timeout) == NULL);
283 
284 	handler = g_new0 (TimeoutHandler, 1);
285 	handler->cs = cs;
286 	handler->timeout = timeout;
287 
288 	handler->source = g_timeout_source_new (dbus_timeout_get_interval (timeout));
289 	g_source_set_callback (handler->source, timeout_handler_dispatch, handler,
290 	                       timeout_handler_source_finalized);
291 	g_source_attach (handler->source, handler->cs->context);
292 	cs->timeouts = g_slist_prepend (cs->timeouts, handler);
293 	dbus_timeout_set_data (timeout, handler, timeout_handler_timeout_freed);
294 }
295 
296 static void
297 connection_setup_remove_timeout (ConnectionSetup *cs, DBusTimeout *timeout)
298 {
299 	TimeoutHandler *handler = dbus_timeout_get_data (timeout);
300 	if (handler != NULL)
301 		timeout_handler_destroy_source (handler);
302 }
303 
304 static void
305 connection_setup_free (ConnectionSetup *cs)
306 {
307 	while (cs->ios)
308 		io_handler_destroy_source (cs->ios->data);
309 
310 	while (cs->timeouts)
311 		timeout_handler_destroy_source (cs->timeouts->data);
312 
313 	if (cs->message_queue_source) {
314 		GSource *source = cs->message_queue_source;
315 		cs->message_queue_source = NULL;
316 
317 		g_source_destroy (source);
318 		g_source_unref (source);
319 	}
320 
321 	g_main_context_unref (cs->context);
322 	g_free (cs);
323 }
324 
325 static dbus_bool_t
326 add_watch (DBusWatch *watch, gpointer data)
327 {
328 	ConnectionSetup *cs = data;
329 	connection_setup_add_watch (cs, watch);
330 	return TRUE;
331 }
332 
333 static void
334 remove_watch (DBusWatch *watch, gpointer data)
335 {
336 	ConnectionSetup *cs = data;
337 	connection_setup_remove_watch (cs, watch);
338 }
339 
340 static void
341 watch_toggled (DBusWatch *watch, void *data)
342 {
343 	if (dbus_watch_get_enabled (watch))
344 		add_watch (watch, data);
345 	else
346 		remove_watch (watch, data);
347 }
348 
349 static dbus_bool_t
350 add_timeout (DBusTimeout *timeout, void *data)
351 {
352 	ConnectionSetup *cs = data;
353 	if (!dbus_timeout_get_enabled (timeout))
354 		return TRUE;
355 	connection_setup_add_timeout (cs, timeout);
356 	return TRUE;
357 }
358 
359 static void
360 remove_timeout (DBusTimeout *timeout, void *data)
361 {
362 	ConnectionSetup *cs = data;
363 	connection_setup_remove_timeout (cs, timeout);
364 }
365 
366 static void
367 timeout_toggled (DBusTimeout *timeout, void *data)
368 {
369 	if (dbus_timeout_get_enabled (timeout))
370 		add_timeout (timeout, data);
371 	else
372 		remove_timeout (timeout, data);
373 }
374 
375 static void
376 wakeup_main (void *data)
377 {
378 	ConnectionSetup *cs = data;
379 	g_main_context_wakeup (cs->context);
380 }
381 
382 void
383 egg_dbus_connect_with_mainloop (DBusConnection *connection, GMainContext *context)
384 {
385 	ConnectionSetup *cs;
386 
387 	if (context == NULL)
388 		context = g_main_context_default ();
389 	cs = connection_setup_new (context, connection);
390 	the_setup = cs;
391 
392 	if (!dbus_connection_set_watch_functions (connection, add_watch,
393 	                                          remove_watch, watch_toggled,
394 	                                          cs, NULL))
395 		goto nomem;
396 
397 	if (!dbus_connection_set_timeout_functions (connection, add_timeout,
398 	                                            remove_timeout, timeout_toggled,
399 	                                            cs, NULL))
400 		goto nomem;
401 
402 	dbus_connection_set_wakeup_main_function (connection, wakeup_main, cs, NULL);
403 
404 	return;
405 
406 nomem:
407 	g_error ("Not enough memory to set up DBusConnection for use with GLib");
408 }
409 
410 void
411 egg_dbus_disconnect_from_mainloop (DBusConnection *connection, GMainContext *context)
412 {
413 	ConnectionSetup *cs = the_setup;
414 	the_setup = NULL;
415 
416 	if (cs)
417 		connection_setup_free (cs);
418 }
419