2 * Copyright © 2006-2007 Daniel Stone
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
23 * Author: Daniel Stone <daniel@fooishbar.org>
26 #ifdef HAVE_DIX_CONFIG_H
27 #include <dix-config.h>
30 #include <dbus/dbus.h>
31 #include <sys/select.h>
33 #include "config-backends.h"
37 /* How often to attempt reconnecting when we get booted off the bus. */
38 #define RECONNECT_DELAY (10 * 1000) /* in ms */
40 struct dbus_core_info
{
42 DBusConnection
*connection
;
44 struct config_dbus_core_hook
*hooks
;
46 static struct dbus_core_info bus_info
;
48 static CARD32
reconnect_timer(OsTimerPtr timer
, CARD32 time
, pointer arg
);
51 wakeup_handler(pointer data
, int err
, pointer read_mask
)
53 struct dbus_core_info
*info
= data
;
55 if (info
->connection
&& FD_ISSET(info
->fd
, (fd_set
*) read_mask
)) {
57 dbus_connection_read_write_dispatch(info
->connection
, 0);
58 } while (info
->connection
&&
59 dbus_connection_get_is_connected(info
->connection
) &&
60 dbus_connection_get_dispatch_status(info
->connection
) ==
61 DBUS_DISPATCH_DATA_REMAINS
);
66 block_handler(pointer data
, struct timeval
**tv
, pointer read_mask
)
71 * Disconnect (if we haven't already been forcefully disconnected), clean up
72 * after ourselves, and call all registered disconnect hooks.
77 struct config_dbus_core_hook
*hook
;
80 TimerFree(bus_info
.timer
);
81 bus_info
.timer
= NULL
;
84 /* We should really have pre-disconnect hooks and run them here, for
85 * completeness. But then it gets awkward, given that you can't
86 * guarantee that they'll be called ... */
87 if (bus_info
.connection
)
88 dbus_connection_unref(bus_info
.connection
);
90 RemoveBlockAndWakeupHandlers(block_handler
, wakeup_handler
, &bus_info
);
91 if (bus_info
.fd
!= -1)
92 RemoveGeneralSocket(bus_info
.fd
);
94 bus_info
.connection
= NULL
;
96 for (hook
= bus_info
.hooks
; hook
; hook
= hook
->next
) {
98 hook
->disconnect(hook
->data
);
103 * This is a filter, which only handles the disconnected signal, which
104 * doesn't go to the normal message handling function. This takes
105 * precedence over the message handling function, so have have to be
106 * careful to ignore anything we don't want to deal with here.
108 static DBusHandlerResult
109 message_filter(DBusConnection
* connection
, DBusMessage
* message
, void *data
)
111 /* If we get disconnected, then take everything down, and attempt to
112 * reconnect immediately (assuming it's just a restart). The
113 * connection isn't valid at this point, so throw it out immediately. */
114 if (dbus_message_is_signal(message
, DBUS_INTERFACE_LOCAL
, "Disconnected")) {
115 DebugF("[config/dbus-core] disconnected from bus\n");
116 bus_info
.connection
= NULL
;
120 TimerFree(bus_info
.timer
);
121 bus_info
.timer
= TimerSet(NULL
, 0, 1, reconnect_timer
, NULL
);
123 return DBUS_HANDLER_RESULT_HANDLED
;
126 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
130 * Attempt to connect to the system bus, and set a filter to deal with
131 * disconnection (see message_filter above).
133 * @return 1 on success, 0 on failure.
139 struct config_dbus_core_hook
*hook
;
141 dbus_error_init(&error
);
142 bus_info
.connection
= dbus_bus_get(DBUS_BUS_SYSTEM
, &error
);
143 if (!bus_info
.connection
|| dbus_error_is_set(&error
)) {
144 DebugF("[config/dbus-core] error connecting to system bus: %s (%s)\n",
145 error
.name
, error
.message
);
149 /* Thankyou. Really, thankyou. */
150 dbus_connection_set_exit_on_disconnect(bus_info
.connection
, FALSE
);
152 if (!dbus_connection_get_unix_fd(bus_info
.connection
, &bus_info
.fd
)) {
153 ErrorF("[config/dbus-core] couldn't get fd for system bus\n");
157 if (!dbus_connection_add_filter(bus_info
.connection
, message_filter
,
159 ErrorF("[config/dbus-core] couldn't add filter: %s (%s)\n", error
.name
,
164 dbus_error_free(&error
);
165 AddGeneralSocket(bus_info
.fd
);
167 RegisterBlockAndWakeupHandlers(block_handler
, wakeup_handler
, &bus_info
);
169 for (hook
= bus_info
.hooks
; hook
; hook
= hook
->next
) {
171 hook
->connect(bus_info
.connection
, hook
->data
);
179 dbus_connection_unref(bus_info
.connection
);
180 bus_info
.connection
= NULL
;
182 dbus_error_free(&error
);
188 reconnect_timer(OsTimerPtr timer
, CARD32 time
, pointer arg
)
190 if (connect_to_bus()) {
191 TimerFree(bus_info
.timer
);
192 bus_info
.timer
= NULL
;
196 return RECONNECT_DELAY
;
201 config_dbus_core_add_hook(struct config_dbus_core_hook
*hook
)
203 struct config_dbus_core_hook
**prev
;
205 for (prev
= &bus_info
.hooks
; *prev
; prev
= &(*prev
)->next
);
210 /* If we're already connected, call the connect hook. */
211 if (bus_info
.connection
)
212 hook
->connect(bus_info
.connection
, hook
->data
);
218 config_dbus_core_remove_hook(struct config_dbus_core_hook
*hook
)
220 struct config_dbus_core_hook
**prev
;
222 for (prev
= &bus_info
.hooks
; *prev
; prev
= &(*prev
)->next
) {
231 config_dbus_core_init(void)
233 memset(&bus_info
, 0, sizeof(bus_info
));
235 bus_info
.hooks
= NULL
;
236 bus_info
.connection
= NULL
;
237 bus_info
.timer
= TimerSet(NULL
, 0, 1, reconnect_timer
, NULL
);
243 config_dbus_core_fini(void)