1 /*
2 * Copyright © 2006-2007 Daniel Stone
3 *
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:
10 *
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
13 * Software.
14 *
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.
22 *
23 * Author: Daniel Stone <daniel@fooishbar.org>
24 */
26 #ifdef HAVE_DIX_CONFIG_H
27 #include <dix-config.h>
28 #endif
30 #include <dbus/dbus.h>
31 #include <sys/select.h>
33 #include "config-backends.h"
34 #include "dix.h"
35 #include "os.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 {
41 int fd;
42 DBusConnection *connection;
43 OsTimerPtr timer;
44 struct config_dbus_core_hook *hooks;
45 };
46 static struct dbus_core_info bus_info;
48 static CARD32 reconnect_timer(OsTimerPtr timer, CARD32 time, pointer arg);
50 static void
51 wakeup_handler(pointer data, int err, pointer read_mask)
52 {
53 struct dbus_core_info *info = data;
55 if (info->connection && FD_ISSET(info->fd, (fd_set *) read_mask)) {
56 do {
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) == DBUS_DISPATCH_DATA_REMAINS);
61 }
62 }
64 static void
65 block_handler(pointer data, struct timeval **tv, pointer read_mask)
66 {
67 }
69 /**
70 * Disconnect (if we haven't already been forcefully disconnected), clean up
71 * after ourselves, and call all registered disconnect hooks.
72 */
73 static void
74 teardown(void)
75 {
76 struct config_dbus_core_hook *hook;
78 if (bus_info.timer) {
79 TimerFree(bus_info.timer);
80 bus_info.timer = NULL;
81 }
83 /* We should really have pre-disconnect hooks and run them here, for
84 * completeness. But then it gets awkward, given that you can't
85 * guarantee that they'll be called ... */
86 if (bus_info.connection)
87 dbus_connection_unref(bus_info.connection);
89 RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, &bus_info);
90 if (bus_info.fd != -1)
91 RemoveGeneralSocket(bus_info.fd);
92 bus_info.fd = -1;
93 bus_info.connection = NULL;
95 for (hook = bus_info.hooks; hook; hook = hook->next) {
96 if (hook->disconnect)
97 hook->disconnect(hook->data);
98 }
99 }
101 /**
102 * This is a filter, which only handles the disconnected signal, which
103 * doesn't go to the normal message handling function. This takes
104 * precedence over the message handling function, so have have to be
105 * careful to ignore anything we don't want to deal with here.
106 */
107 static DBusHandlerResult
108 message_filter(DBusConnection *connection, DBusMessage *message, void *data)
109 {
110 /* If we get disconnected, then take everything down, and attempt to
111 * reconnect immediately (assuming it's just a restart). The
112 * connection isn't valid at this point, so throw it out immediately. */
113 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
114 "Disconnected")) {
115 DebugF("[config/dbus-core] disconnected from bus\n");
116 bus_info.connection = NULL;
117 teardown();
119 if (bus_info.timer)
120 TimerFree(bus_info.timer);
121 bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL);
123 return DBUS_HANDLER_RESULT_HANDLED;
124 }
126 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
127 }
129 /**
130 * Attempt to connect to the system bus, and set a filter to deal with
131 * disconnection (see message_filter above).
132 *
133 * @return 1 on success, 0 on failure.
134 */
135 static int
136 connect_to_bus(void)
137 {
138 DBusError error;
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);
146 goto err_begin;
147 }
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");
154 goto err_unref;
155 }
157 if (!dbus_connection_add_filter(bus_info.connection, message_filter,
158 &bus_info, NULL)) {
159 ErrorF("[config/dbus-core] couldn't add filter: %s (%s)\n", error.name,
160 error.message);
161 goto err_fd;
162 }
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) {
170 if (hook->connect)
171 hook->connect(bus_info.connection, hook->data);
172 }
174 return 1;
176 err_fd:
177 bus_info.fd = -1;
178 err_unref:
179 dbus_connection_unref(bus_info.connection);
180 bus_info.connection = NULL;
181 err_begin:
182 dbus_error_free(&error);
184 return 0;
185 }
187 static CARD32
188 reconnect_timer(OsTimerPtr timer, CARD32 time, pointer arg)
189 {
190 if (connect_to_bus()) {
191 TimerFree(bus_info.timer);
192 bus_info.timer = NULL;
193 return 0;
194 }
195 else {
196 return RECONNECT_DELAY;
197 }
198 }
200 int
201 config_dbus_core_add_hook(struct config_dbus_core_hook *hook)
202 {
203 struct config_dbus_core_hook **prev;
205 for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next)
206 ;
208 hook->next = NULL;
209 *prev = hook;
211 /* If we're already connected, call the connect hook. */
212 if (bus_info.connection)
213 hook->connect(bus_info.connection, hook->data);
215 return 1;
216 }
218 void
219 config_dbus_core_remove_hook(struct config_dbus_core_hook *hook)
220 {
221 struct config_dbus_core_hook **prev;
223 for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next) {
224 if (*prev == hook) {
225 *prev = hook->next;
226 break;
227 }
228 }
229 }
231 int
232 config_dbus_core_init(void)
233 {
234 memset(&bus_info, 0, sizeof(bus_info));
235 bus_info.fd = -1;
236 bus_info.hooks = NULL;
237 bus_info.connection = NULL;
238 bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL);
240 return 1;
241 }
243 void
244 config_dbus_core_fini(void)
245 {
246 teardown();
247 }