Imported Upstream version 1.15.1
[deb_xorg-server.git] / config / dbus-core.c
CommitLineData
a09e091a
JB
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 */
25
26#ifdef HAVE_DIX_CONFIG_H
27#include <dix-config.h>
28#endif
29
30#include <dbus/dbus.h>
31#include <sys/select.h>
32
33#include "config-backends.h"
34#include "dix.h"
35#include "os.h"
36
37/* How often to attempt reconnecting when we get booted off the bus. */
38#define RECONNECT_DELAY (10 * 1000) /* in ms */
39
40struct dbus_core_info {
41 int fd;
42 DBusConnection *connection;
43 OsTimerPtr timer;
44 struct config_dbus_core_hook *hooks;
45};
46static struct dbus_core_info bus_info;
47
48static CARD32 reconnect_timer(OsTimerPtr timer, CARD32 time, pointer arg);
49
50static void
51wakeup_handler(pointer data, int err, pointer read_mask)
52{
53 struct dbus_core_info *info = data;
54
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) ==
61 DBUS_DISPATCH_DATA_REMAINS);
62 }
63}
64
65static void
66block_handler(pointer data, struct timeval **tv, pointer read_mask)
67{
68}
69
70/**
71 * Disconnect (if we haven't already been forcefully disconnected), clean up
72 * after ourselves, and call all registered disconnect hooks.
73 */
74static void
75teardown(void)
76{
77 struct config_dbus_core_hook *hook;
78
79 if (bus_info.timer) {
80 TimerFree(bus_info.timer);
81 bus_info.timer = NULL;
82 }
83
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);
89
90 RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, &bus_info);
91 if (bus_info.fd != -1)
92 RemoveGeneralSocket(bus_info.fd);
93 bus_info.fd = -1;
94 bus_info.connection = NULL;
95
96 for (hook = bus_info.hooks; hook; hook = hook->next) {
97 if (hook->disconnect)
98 hook->disconnect(hook->data);
99 }
100}
101
102/**
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.
107 */
108static DBusHandlerResult
109message_filter(DBusConnection * connection, DBusMessage * message, void *data)
110{
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;
117 teardown();
118
119 if (bus_info.timer)
120 TimerFree(bus_info.timer);
121 bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL);
122
123 return DBUS_HANDLER_RESULT_HANDLED;
124 }
125
126 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
127}
128
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 */
135static int
136connect_to_bus(void)
137{
138 DBusError error;
139 struct config_dbus_core_hook *hook;
140
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 }
148
149 /* Thankyou. Really, thankyou. */
150 dbus_connection_set_exit_on_disconnect(bus_info.connection, FALSE);
151
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 }
156
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 }
163
164 dbus_error_free(&error);
165 AddGeneralSocket(bus_info.fd);
166
167 RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, &bus_info);
168
169 for (hook = bus_info.hooks; hook; hook = hook->next) {
170 if (hook->connect)
171 hook->connect(bus_info.connection, hook->data);
172 }
173
174 return 1;
175
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);
183
184 return 0;
185}
186
187static CARD32
188reconnect_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}
199
200int
201config_dbus_core_add_hook(struct config_dbus_core_hook *hook)
202{
203 struct config_dbus_core_hook **prev;
204
205 for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next);
206
207 hook->next = NULL;
208 *prev = hook;
209
210 /* If we're already connected, call the connect hook. */
211 if (bus_info.connection)
212 hook->connect(bus_info.connection, hook->data);
213
214 return 1;
215}
216
217void
218config_dbus_core_remove_hook(struct config_dbus_core_hook *hook)
219{
220 struct config_dbus_core_hook **prev;
221
222 for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next) {
223 if (*prev == hook) {
224 *prev = hook->next;
225 break;
226 }
227 }
228}
229
230int
231config_dbus_core_init(void)
232{
233 memset(&bus_info, 0, sizeof(bus_info));
234 bus_info.fd = -1;
235 bus_info.hooks = NULL;
236 bus_info.connection = NULL;
237 bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL);
238
239 return 1;
240}
241
242void
243config_dbus_core_fini(void)
244{
245 teardown();
246}