105b261ecSmrg/* 205b261ecSmrg * Copyright © 2006-2007 Daniel Stone 305b261ecSmrg * 405b261ecSmrg * Permission is hereby granted, free of charge, to any person obtaining a 505b261ecSmrg * copy of this software and associated documentation files (the "Software"), 605b261ecSmrg * to deal in the Software without restriction, including without limitation 705b261ecSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 805b261ecSmrg * and/or sell copies of the Software, and to permit persons to whom the 905b261ecSmrg * Software is furnished to do so, subject to the following conditions: 1005b261ecSmrg * 1105b261ecSmrg * The above copyright notice and this permission notice (including the next 1205b261ecSmrg * paragraph) shall be included in all copies or substantial portions of the 1305b261ecSmrg * Software. 1405b261ecSmrg * 1505b261ecSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1605b261ecSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1705b261ecSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1805b261ecSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1905b261ecSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2005b261ecSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 2105b261ecSmrg * DEALINGS IN THE SOFTWARE. 2205b261ecSmrg * 2305b261ecSmrg * Author: Daniel Stone <daniel@fooishbar.org> 2405b261ecSmrg */ 2505b261ecSmrg 2605b261ecSmrg#ifdef HAVE_DIX_CONFIG_H 2705b261ecSmrg#include <dix-config.h> 2805b261ecSmrg#endif 2905b261ecSmrg 3005b261ecSmrg#include <dbus/dbus.h> 3105b261ecSmrg#include <sys/select.h> 3205b261ecSmrg 3305b261ecSmrg#include "dix.h" 3405b261ecSmrg#include "os.h" 3535c4bbdfSmrg#include "dbus-core.h" 3605b261ecSmrg 3705b261ecSmrg/* How often to attempt reconnecting when we get booted off the bus. */ 3835c4bbdfSmrg#define RECONNECT_DELAY (10 * 1000) /* in ms */ 3905b261ecSmrg 4005b261ecSmrgstruct dbus_core_info { 4105b261ecSmrg int fd; 4205b261ecSmrg DBusConnection *connection; 4305b261ecSmrg OsTimerPtr timer; 4435c4bbdfSmrg struct dbus_core_hook *hooks; 4505b261ecSmrg}; 461b5d61b8Smrgstatic struct dbus_core_info bus_info = { .fd = -1 }; 4705b261ecSmrg 4835c4bbdfSmrgstatic CARD32 reconnect_timer(OsTimerPtr timer, CARD32 time, void *arg); 4905b261ecSmrg 5005b261ecSmrgstatic void 511b5d61b8Smrgsocket_handler(int fd, int ready, void *data) 5205b261ecSmrg{ 5305b261ecSmrg struct dbus_core_info *info = data; 5405b261ecSmrg 551b5d61b8Smrg if (info->connection) { 5605b261ecSmrg do { 5705b261ecSmrg dbus_connection_read_write_dispatch(info->connection, 0); 584642e01fSmrg } while (info->connection && 594642e01fSmrg dbus_connection_get_is_connected(info->connection) && 6035c4bbdfSmrg dbus_connection_get_dispatch_status(info->connection) == 6135c4bbdfSmrg DBUS_DISPATCH_DATA_REMAINS); 6205b261ecSmrg } 6305b261ecSmrg} 6405b261ecSmrg 6505b261ecSmrg/** 6605b261ecSmrg * Disconnect (if we haven't already been forcefully disconnected), clean up 6705b261ecSmrg * after ourselves, and call all registered disconnect hooks. 6805b261ecSmrg */ 6905b261ecSmrgstatic void 7005b261ecSmrgteardown(void) 7105b261ecSmrg{ 7235c4bbdfSmrg struct dbus_core_hook *hook; 7305b261ecSmrg 7405b261ecSmrg if (bus_info.timer) { 7505b261ecSmrg TimerFree(bus_info.timer); 7605b261ecSmrg bus_info.timer = NULL; 7705b261ecSmrg } 7805b261ecSmrg 7905b261ecSmrg /* We should really have pre-disconnect hooks and run them here, for 8005b261ecSmrg * completeness. But then it gets awkward, given that you can't 8105b261ecSmrg * guarantee that they'll be called ... */ 8205b261ecSmrg if (bus_info.connection) 8305b261ecSmrg dbus_connection_unref(bus_info.connection); 8405b261ecSmrg 8505b261ecSmrg if (bus_info.fd != -1) 861b5d61b8Smrg RemoveNotifyFd(bus_info.fd); 8705b261ecSmrg bus_info.fd = -1; 8805b261ecSmrg bus_info.connection = NULL; 8905b261ecSmrg 9005b261ecSmrg for (hook = bus_info.hooks; hook; hook = hook->next) { 9105b261ecSmrg if (hook->disconnect) 9205b261ecSmrg hook->disconnect(hook->data); 9305b261ecSmrg } 9405b261ecSmrg} 9505b261ecSmrg 9605b261ecSmrg/** 9705b261ecSmrg * This is a filter, which only handles the disconnected signal, which 9805b261ecSmrg * doesn't go to the normal message handling function. This takes 9905b261ecSmrg * precedence over the message handling function, so have have to be 10005b261ecSmrg * careful to ignore anything we don't want to deal with here. 10105b261ecSmrg */ 10205b261ecSmrgstatic DBusHandlerResult 10335c4bbdfSmrgmessage_filter(DBusConnection * connection, DBusMessage * message, void *data) 10405b261ecSmrg{ 10505b261ecSmrg /* If we get disconnected, then take everything down, and attempt to 10605b261ecSmrg * reconnect immediately (assuming it's just a restart). The 10705b261ecSmrg * connection isn't valid at this point, so throw it out immediately. */ 10835c4bbdfSmrg if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { 10935c4bbdfSmrg DebugF("[dbus-core] disconnected from bus\n"); 11005b261ecSmrg bus_info.connection = NULL; 11105b261ecSmrg teardown(); 11205b261ecSmrg 11305b261ecSmrg if (bus_info.timer) 11405b261ecSmrg TimerFree(bus_info.timer); 11505b261ecSmrg bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL); 11605b261ecSmrg 11705b261ecSmrg return DBUS_HANDLER_RESULT_HANDLED; 11805b261ecSmrg } 11905b261ecSmrg 12005b261ecSmrg return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 12105b261ecSmrg} 12205b261ecSmrg 12305b261ecSmrg/** 12405b261ecSmrg * Attempt to connect to the system bus, and set a filter to deal with 12505b261ecSmrg * disconnection (see message_filter above). 12605b261ecSmrg * 12705b261ecSmrg * @return 1 on success, 0 on failure. 12805b261ecSmrg */ 12905b261ecSmrgstatic int 13005b261ecSmrgconnect_to_bus(void) 13105b261ecSmrg{ 13205b261ecSmrg DBusError error; 13335c4bbdfSmrg struct dbus_core_hook *hook; 13405b261ecSmrg 13505b261ecSmrg dbus_error_init(&error); 13605b261ecSmrg bus_info.connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); 13705b261ecSmrg if (!bus_info.connection || dbus_error_is_set(&error)) { 13835c4bbdfSmrg LogMessage(X_ERROR, "dbus-core: error connecting to system bus: %s (%s)\n", 13905b261ecSmrg error.name, error.message); 14005b261ecSmrg goto err_begin; 14105b261ecSmrg } 14205b261ecSmrg 14305b261ecSmrg /* Thankyou. Really, thankyou. */ 14405b261ecSmrg dbus_connection_set_exit_on_disconnect(bus_info.connection, FALSE); 14505b261ecSmrg 14605b261ecSmrg if (!dbus_connection_get_unix_fd(bus_info.connection, &bus_info.fd)) { 14735c4bbdfSmrg ErrorF("[dbus-core] couldn't get fd for system bus\n"); 14805b261ecSmrg goto err_unref; 14905b261ecSmrg } 15005b261ecSmrg 15105b261ecSmrg if (!dbus_connection_add_filter(bus_info.connection, message_filter, 15205b261ecSmrg &bus_info, NULL)) { 15335c4bbdfSmrg ErrorF("[dbus-core] couldn't add filter: %s (%s)\n", error.name, 15405b261ecSmrg error.message); 15505b261ecSmrg goto err_fd; 15605b261ecSmrg } 15705b261ecSmrg 15805b261ecSmrg dbus_error_free(&error); 1591b5d61b8Smrg SetNotifyFd(bus_info.fd, socket_handler, X_NOTIFY_READ, &bus_info); 16005b261ecSmrg 16105b261ecSmrg for (hook = bus_info.hooks; hook; hook = hook->next) { 16205b261ecSmrg if (hook->connect) 16305b261ecSmrg hook->connect(bus_info.connection, hook->data); 16405b261ecSmrg } 16505b261ecSmrg 16605b261ecSmrg return 1; 16705b261ecSmrg 16835c4bbdfSmrg err_fd: 16905b261ecSmrg bus_info.fd = -1; 17035c4bbdfSmrg err_unref: 17105b261ecSmrg dbus_connection_unref(bus_info.connection); 17205b261ecSmrg bus_info.connection = NULL; 17335c4bbdfSmrg err_begin: 17405b261ecSmrg dbus_error_free(&error); 17505b261ecSmrg 17605b261ecSmrg return 0; 17705b261ecSmrg} 17805b261ecSmrg 17905b261ecSmrgstatic CARD32 18035c4bbdfSmrgreconnect_timer(OsTimerPtr timer, CARD32 time, void *arg) 18105b261ecSmrg{ 18205b261ecSmrg if (connect_to_bus()) { 18305b261ecSmrg TimerFree(bus_info.timer); 18405b261ecSmrg bus_info.timer = NULL; 18505b261ecSmrg return 0; 18605b261ecSmrg } 18705b261ecSmrg else { 18805b261ecSmrg return RECONNECT_DELAY; 18905b261ecSmrg } 19005b261ecSmrg} 19105b261ecSmrg 19205b261ecSmrgint 19335c4bbdfSmrgdbus_core_add_hook(struct dbus_core_hook *hook) 19405b261ecSmrg{ 19535c4bbdfSmrg struct dbus_core_hook **prev; 19605b261ecSmrg 19735c4bbdfSmrg for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next); 19805b261ecSmrg 19905b261ecSmrg hook->next = NULL; 20005b261ecSmrg *prev = hook; 20105b261ecSmrg 20205b261ecSmrg /* If we're already connected, call the connect hook. */ 20305b261ecSmrg if (bus_info.connection) 20405b261ecSmrg hook->connect(bus_info.connection, hook->data); 20505b261ecSmrg 20605b261ecSmrg return 1; 20705b261ecSmrg} 20805b261ecSmrg 20905b261ecSmrgvoid 21035c4bbdfSmrgdbus_core_remove_hook(struct dbus_core_hook *hook) 21105b261ecSmrg{ 21235c4bbdfSmrg struct dbus_core_hook **prev; 21305b261ecSmrg 21405b261ecSmrg for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next) { 21505b261ecSmrg if (*prev == hook) { 21605b261ecSmrg *prev = hook->next; 21705b261ecSmrg break; 21805b261ecSmrg } 21905b261ecSmrg } 22005b261ecSmrg} 22105b261ecSmrg 22205b261ecSmrgint 22335c4bbdfSmrgdbus_core_init(void) 22405b261ecSmrg{ 22505b261ecSmrg memset(&bus_info, 0, sizeof(bus_info)); 22605b261ecSmrg bus_info.fd = -1; 22705b261ecSmrg bus_info.hooks = NULL; 22835c4bbdfSmrg if (!connect_to_bus()) 22935c4bbdfSmrg bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL); 23005b261ecSmrg 23105b261ecSmrg return 1; 23205b261ecSmrg} 23305b261ecSmrg 23405b261ecSmrgvoid 23535c4bbdfSmrgdbus_core_fini(void) 23605b261ecSmrg{ 23705b261ecSmrg teardown(); 23805b261ecSmrg} 239