dbus-core.c (6776B)
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 "dix.h" 34 #include "os.h" 35 #include "dbus-core.h" 36 37 /* How often to attempt reconnecting when we get booted off the bus. */ 38 #define RECONNECT_DELAY (10 * 1000) /* in ms */ 39 40 struct dbus_core_info { 41 int fd; 42 DBusConnection *connection; 43 OsTimerPtr timer; 44 struct dbus_core_hook *hooks; 45 }; 46 static struct dbus_core_info bus_info = { .fd = -1 }; 47 48 static CARD32 reconnect_timer(OsTimerPtr timer, CARD32 time, void *arg); 49 50 static void 51 socket_handler(int fd, int ready, void *data) 52 { 53 struct dbus_core_info *info = data; 54 55 if (info->connection) { 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 65 /** 66 * Disconnect (if we haven't already been forcefully disconnected), clean up 67 * after ourselves, and call all registered disconnect hooks. 68 */ 69 static void 70 teardown(void) 71 { 72 struct dbus_core_hook *hook; 73 74 if (bus_info.timer) { 75 TimerFree(bus_info.timer); 76 bus_info.timer = NULL; 77 } 78 79 /* We should really have pre-disconnect hooks and run them here, for 80 * completeness. But then it gets awkward, given that you can't 81 * guarantee that they'll be called ... */ 82 if (bus_info.connection) 83 dbus_connection_unref(bus_info.connection); 84 85 if (bus_info.fd != -1) 86 RemoveNotifyFd(bus_info.fd); 87 bus_info.fd = -1; 88 bus_info.connection = NULL; 89 90 for (hook = bus_info.hooks; hook; hook = hook->next) { 91 if (hook->disconnect) 92 hook->disconnect(hook->data); 93 } 94 } 95 96 /** 97 * This is a filter, which only handles the disconnected signal, which 98 * doesn't go to the normal message handling function. This takes 99 * precedence over the message handling function, so have have to be 100 * careful to ignore anything we don't want to deal with here. 101 */ 102 static DBusHandlerResult 103 message_filter(DBusConnection * connection, DBusMessage * message, void *data) 104 { 105 /* If we get disconnected, then take everything down, and attempt to 106 * reconnect immediately (assuming it's just a restart). The 107 * connection isn't valid at this point, so throw it out immediately. */ 108 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { 109 DebugF("[dbus-core] disconnected from bus\n"); 110 bus_info.connection = NULL; 111 teardown(); 112 113 if (bus_info.timer) 114 TimerFree(bus_info.timer); 115 bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL); 116 117 return DBUS_HANDLER_RESULT_HANDLED; 118 } 119 120 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 121 } 122 123 /** 124 * Attempt to connect to the system bus, and set a filter to deal with 125 * disconnection (see message_filter above). 126 * 127 * @return 1 on success, 0 on failure. 128 */ 129 static int 130 connect_to_bus(void) 131 { 132 DBusError error; 133 struct dbus_core_hook *hook; 134 135 dbus_error_init(&error); 136 bus_info.connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); 137 if (!bus_info.connection || dbus_error_is_set(&error)) { 138 LogMessage(X_ERROR, "dbus-core: error connecting to system bus: %s (%s)\n", 139 error.name, error.message); 140 goto err_begin; 141 } 142 143 /* Thankyou. Really, thankyou. */ 144 dbus_connection_set_exit_on_disconnect(bus_info.connection, FALSE); 145 146 if (!dbus_connection_get_unix_fd(bus_info.connection, &bus_info.fd)) { 147 ErrorF("[dbus-core] couldn't get fd for system bus\n"); 148 goto err_unref; 149 } 150 151 if (!dbus_connection_add_filter(bus_info.connection, message_filter, 152 &bus_info, NULL)) { 153 ErrorF("[dbus-core] couldn't add filter: %s (%s)\n", error.name, 154 error.message); 155 goto err_fd; 156 } 157 158 dbus_error_free(&error); 159 SetNotifyFd(bus_info.fd, socket_handler, X_NOTIFY_READ, &bus_info); 160 161 for (hook = bus_info.hooks; hook; hook = hook->next) { 162 if (hook->connect) 163 hook->connect(bus_info.connection, hook->data); 164 } 165 166 return 1; 167 168 err_fd: 169 bus_info.fd = -1; 170 err_unref: 171 dbus_connection_unref(bus_info.connection); 172 bus_info.connection = NULL; 173 err_begin: 174 dbus_error_free(&error); 175 176 return 0; 177 } 178 179 static CARD32 180 reconnect_timer(OsTimerPtr timer, CARD32 time, void *arg) 181 { 182 if (connect_to_bus()) { 183 TimerFree(bus_info.timer); 184 bus_info.timer = NULL; 185 return 0; 186 } 187 else { 188 return RECONNECT_DELAY; 189 } 190 } 191 192 int 193 dbus_core_add_hook(struct dbus_core_hook *hook) 194 { 195 struct dbus_core_hook **prev; 196 197 for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next); 198 199 hook->next = NULL; 200 *prev = hook; 201 202 /* If we're already connected, call the connect hook. */ 203 if (bus_info.connection) 204 hook->connect(bus_info.connection, hook->data); 205 206 return 1; 207 } 208 209 void 210 dbus_core_remove_hook(struct dbus_core_hook *hook) 211 { 212 struct dbus_core_hook **prev; 213 214 for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next) { 215 if (*prev == hook) { 216 *prev = hook->next; 217 break; 218 } 219 } 220 } 221 222 int 223 dbus_core_init(void) 224 { 225 memset(&bus_info, 0, sizeof(bus_info)); 226 bus_info.fd = -1; 227 bus_info.hooks = NULL; 228 if (!connect_to_bus()) 229 bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL); 230 231 return 1; 232 } 233 234 void 235 dbus_core_fini(void) 236 { 237 teardown(); 238 }