From 1cdadc2f43d9069572814510d04b1a560c488fcb Mon Sep 17 00:00:00 2001 From: Daniel Stone Date: Sun, 8 Jul 2007 14:28:58 +0300 Subject: Hotplug: Separate D-Bus into core and hotplug API components Break up D-Bus into two components: a D-Bus core that can be used by any part of the server (for the moment, just the D-Bus hotplug API, and the forthcoming HAL hotplug API), and the old D-Bus hotplug API. --- config/Makefile.am | 13 +- config/config-backends.h | 52 +++++ config/config.c | 494 ++--------------------------------------------- config/dbus-core.c | 233 ++++++++++++++++++++++ config/dbus.c | 407 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 719 insertions(+), 480 deletions(-) create mode 100644 config/config-backends.h create mode 100644 config/dbus-core.c create mode 100644 config/dbus.c (limited to 'config') diff --git a/config/Makefile.am b/config/Makefile.am index 43c9aa330..527034493 100644 --- a/config/Makefile.am +++ b/config/Makefile.am @@ -1,10 +1,19 @@ AM_CFLAGS = @DIX_CFLAGS@ +noinst_LIBRARIES = libconfig.a +libconfig_a_SOURCES = config.c + +if HAVE_DBUS +AM_CFLAGS += @DBUS_CFLAGS@ +libconfig_a_SOURCES += dbus-core.c +endif + +if CONFIG_DBUS_API dbusconfigdir = $(sysconfdir)/dbus-1/system.d dbusconfig_DATA = xorg-server.conf noinst_LIBRARIES = libconfig.a - -libconfig_a_SOURCES = config.c +libconfig_a_SOURCES += dbus.c +endif EXTRA_DIST = xorg-server.conf diff --git a/config/config-backends.h b/config/config-backends.h new file mode 100644 index 000000000..8a10a3e49 --- /dev/null +++ b/config/config-backends.h @@ -0,0 +1,52 @@ +/* + * Copyright © 2006-2007 Daniel Stone + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders and/or authors + * not be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. The copyright holders + * and/or authors make no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THE COPYRIGHT HOLDERS AND/OR AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND/OR AUTHORS BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#ifdef HAVE_DBUS +#include + +typedef void (*config_dbus_core_connect_hook)(DBusConnection *connection, + void *data); +typedef void (*config_dbus_core_disconnect_hook)(void *data); + +struct config_dbus_core_hook { + config_dbus_core_connect_hook connect; + config_dbus_core_disconnect_hook disconnect; + void *data; + + struct config_dbus_core_hook *next; +}; + +int config_dbus_core_init(void); +void config_dbus_core_fini(void); +int config_dbus_core_add_hook(struct config_dbus_core_hook *hook); +void config_dbus_core_remove_hook(struct config_dbus_core_hook *hook); +#endif + +#ifdef CONFIG_DBUS_API +int config_dbus_init(void); +void config_dbus_fini(void); +#endif diff --git a/config/config.c b/config/config.c index 9b38faf49..a6d36c0ee 100644 --- a/config/config.c +++ b/config/config.c @@ -1,5 +1,5 @@ /* - * Copyright © 2006 Daniel Stone + * Copyright © 2006-2007 Daniel Stone * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -25,490 +25,28 @@ #include #endif -#ifdef HAVE_DBUS -#define DBUS_API_SUBJECT_TO_CHANGE -#include -#include -#include - -#include - -#include "opaque.h" /* for 'display': there has to be a better way */ - /* the above comment lies. there is no better way. */ -#include "input.h" -#include "inputstr.h" #include "hotplug.h" -#include "os.h" - -#define CONFIG_MATCH_RULE "type='method_call',interface='org.x.config.input'" - -#define MALFORMED_MSG "[config] malformed message, dropping" -#define MALFORMED_MESSAGE() { DebugF(MALFORMED_MSG "\n"); \ - ret = BadValue; \ - goto unwind; } -#define MALFORMED_MESSAGE_ERROR() { DebugF(MALFORMED_MSG ": %s, %s", \ - error->name, error->message); \ - ret = BadValue; \ - goto unwind; } - -/* How often to attempt reconnecting when we get booted off the bus. */ -#define RECONNECT_DELAY 10000 /* in ms */ - -struct config_data { - int fd; - DBusConnection *connection; - char busobject[32]; - char busname[64]; -}; - -static struct config_data *configData; - -static CARD32 configReconnect(OsTimerPtr timer, CARD32 time, pointer arg); - -static void -configWakeupHandler(pointer blockData, int err, pointer pReadMask) -{ - struct config_data *data = blockData; - - if (data->connection && FD_ISSET(data->fd, (fd_set *) pReadMask)) - dbus_connection_read_write_dispatch(data->connection, 0); -} - -static void -configBlockHandler(pointer data, struct timeval **tv, pointer pReadMask) -{ -} - -static void -configTeardown(void) -{ - if (configData) { - RemoveGeneralSocket(configData->fd); - RemoveBlockAndWakeupHandlers(configBlockHandler, configWakeupHandler, - configData); - xfree(configData); - configData = NULL; - } -} - -static int -configAddDevice(DBusMessage *message, DBusMessageIter *iter, - DBusMessage *reply, DBusMessageIter *r_iter, - DBusError *error) -{ - DBusMessageIter subiter; - InputOption *tmpo = NULL, *options = NULL; - char *tmp = NULL; - int ret = BadMatch; - DeviceIntPtr dev = NULL; - - DebugF("[config] adding device\n"); - - /* signature should be [ss][ss]... */ - options = (InputOption *) xcalloc(sizeof(InputOption), 1); - if (!options) { - ErrorF("[config] couldn't allocate option\n"); - return BadAlloc; - } - - options->key = xstrdup("_source"); - options->value = xstrdup("client/dbus"); - if(!options->key || !options->value) { - ErrorF("[config] couldn't allocate first key/value pair\n"); - ret = BadAlloc; - goto unwind; - } - - while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) { - tmpo = (InputOption *) xcalloc(sizeof(InputOption), 1); - if (!tmpo) { - ErrorF("[config] couldn't allocate option\n"); - ret = BadAlloc; - goto unwind; - } - tmpo->next = options; - options = tmpo; - - dbus_message_iter_recurse(iter, &subiter); - - if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING) - MALFORMED_MESSAGE(); - - dbus_message_iter_get_basic(&subiter, &tmp); - if (!tmp) - MALFORMED_MESSAGE(); - if (tmp[0] == '_') { - ErrorF("[config] attempted subterfuge: option name %s given\n", - tmp); - MALFORMED_MESSAGE(); - } - options->key = xstrdup(tmp); - if (!options->key) { - ErrorF("[config] couldn't duplicate key!\n"); - ret = BadAlloc; - goto unwind; - } - - if (!dbus_message_iter_has_next(&subiter)) - MALFORMED_MESSAGE(); - dbus_message_iter_next(&subiter); - if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING) - MALFORMED_MESSAGE(); - - dbus_message_iter_get_basic(&subiter, &tmp); - if (!tmp) - MALFORMED_MESSAGE(); - options->value = xstrdup(tmp); - if (!options->value) { - ErrorF("[config] couldn't duplicate option!\n"); - ret = BadAlloc; - goto unwind; - } - - dbus_message_iter_next(iter); - } - - ret = NewInputDeviceRequest(options, &dev); - if (ret != Success) { - DebugF("[config] NewInputDeviceRequest failed\n"); - goto unwind; - } - - if (!dev) { - DebugF("[config] NewInputDeviceRequest succeeded, without device\n"); - ret = BadMatch; - goto unwind; - } - - if (!dbus_message_iter_append_basic(r_iter, DBUS_TYPE_INT32, &(dev->id))) { - ErrorF("[config] couldn't append to iterator\n"); - ret = BadAlloc; - goto unwind; - } - -unwind: - if (dev && ret != Success) - RemoveDevice(dev); - - while (options) { - tmpo = options; - options = options->next; - if (tmpo->key) - xfree(tmpo->key); - if (tmpo->value) - xfree(tmpo->value); - xfree(tmpo); - } - - return ret; -} - -static int -configRemoveDevice(DBusMessage *message, DBusMessageIter *iter, - DBusError *error) -{ - int deviceid = -1; - int ret = BadMatch; - DeviceIntPtr pDev = NULL; - - if (!dbus_message_get_args(message, error, DBUS_TYPE_INT32, - &deviceid, DBUS_TYPE_INVALID)) { - MALFORMED_MESSAGE_ERROR(); - } - - if (deviceid < 0 || !(pDev = LookupDeviceIntRec(deviceid))) { - DebugF("[config] bogus device id %d given\n", deviceid); - ret = BadMatch; - goto unwind; - } - - DebugF("[config] removing device %s (id %d)\n", pDev->name, deviceid); - - /* Call PIE here so we don't try to dereference a device that's - * already been removed. */ - OsBlockSignals(); - ProcessInputEvents(); - DeleteInputDeviceRequest(pDev); - OsReleaseSignals(); - - return Success; - -unwind: - return ret; -} - -static int -configListDevices(DBusMessage *message, DBusMessageIter *iter, - DBusMessage *reply, DBusMessageIter *r_iter, - DBusError *error) -{ - DeviceIntPtr d; - int ret = BadMatch; - - for (d = inputInfo.devices; d; d = d->next) { - if (!dbus_message_iter_append_basic(r_iter, DBUS_TYPE_INT32, - &(d->id))) { - ErrorF("[config] couldn't append to iterator\n"); - ret = BadAlloc; - goto unwind; - } - if (!dbus_message_iter_append_basic(r_iter, DBUS_TYPE_STRING, - &(d->name))) { - ErrorF("[config] couldn't append to iterator\n"); - ret = BadAlloc; - goto unwind; - } - } - -unwind: - return ret; -} - -static DBusHandlerResult -configMessage(DBusConnection *connection, DBusMessage *message, void *closure) -{ - DBusMessageIter iter; - DBusError error; - DBusMessage *reply; - DBusMessageIter r_iter; - DBusConnection *bus = closure; - int ret = BadDrawable; /* nonsensical value */ - - dbus_error_init(&error); - - DebugF("[config] received a message\n"); - - if (strcmp(dbus_message_get_interface(message), - "org.x.config.input") == 0) { - - if (!(reply = dbus_message_new_method_return(message))) { - ErrorF("[config] failed to create the reply message\n"); - dbus_error_free(&error); - return DBUS_HANDLER_RESULT_NEED_MEMORY; - } - dbus_message_iter_init_append(reply, &r_iter); - - /* listDevices doesn't take any arguments */ - if (strcmp(dbus_message_get_member(message), "listDevices") == 0) - ret = configListDevices(message, NULL, reply, &r_iter, &error); - else - { - if (!dbus_message_iter_init(message, &iter)) { - ErrorF("[config] failed to init iterator\n"); - dbus_message_unref(reply); - dbus_error_free(&error); - return DBUS_HANDLER_RESULT_NEED_MEMORY; /* ?? */ - } - - if (strcmp(dbus_message_get_member(message), "add") == 0) - ret = configAddDevice(message, &iter, reply, &r_iter, &error); - else if (strcmp(dbus_message_get_member(message), "remove") == 0) - ret = configRemoveDevice(message, &iter, &error); - } - - if (ret != BadDrawable && ret != BadAlloc) { - if (!strlen(dbus_message_get_signature(reply))) - { - ret = -ret; /* return errors as negative numbers */ - if (!dbus_message_iter_append_basic(&r_iter, DBUS_TYPE_INT32, &ret)) { - ErrorF("[config] couldn't append to iterator\n"); - dbus_message_unref(reply); - dbus_error_free(&error); - return DBUS_HANDLER_RESULT_HANDLED; - } - } - - if (!dbus_connection_send(bus, reply, NULL)) - ErrorF("[config] failed to send reply\n"); - } - dbus_message_unref(reply); - dbus_connection_flush(bus); - } - - dbus_error_free(&error); - - if (ret == BadAlloc) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - else if (ret == BadDrawable) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - else - return DBUS_HANDLER_RESULT_HANDLED; -} - -/** - * This is a filter, which only handles the disconnected signal, which - * doesn't go to the normal message handling function. This takes - * precedence over the message handling function, so have have to be - * careful to ignore anything we don't want to deal with here. - * - * Yes, this is brutally stupid. - */ -static DBusHandlerResult -configFilter(DBusConnection *connection, DBusMessage *message, void *closure) -{ - if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, - "Disconnected")) { - ErrorF("[dbus] disconnected from bus\n"); - TimerSet(NULL, 0, RECONNECT_DELAY, configReconnect, NULL); - configTeardown(); - return DBUS_HANDLER_RESULT_HANDLED; - } - - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} - -static Bool -configSetup(void) -{ - DBusError error; - DBusObjectPathVTable vtable = { .message_function = configMessage }; - - if (!configData) - configData = (struct config_data *) xcalloc(sizeof(struct config_data), 1); - if (!configData) { - ErrorF("[dbus] failed to allocate data struct\n"); - return FALSE; - } - - dbus_error_init(&error); - configData->connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); - if (!configData->connection || dbus_error_is_set(&error)) { - DebugF("[dbus] some kind of error occurred while connecting: %s (%s)\n", - error.name, error.message); - dbus_error_free(&error); - xfree(configData); - configData = NULL; - return FALSE; - } - - dbus_connection_set_exit_on_disconnect(configData->connection, FALSE); - - if (!dbus_connection_get_unix_fd(configData->connection, &configData->fd)) { - dbus_connection_unref(configData->connection); - ErrorF("[dbus] couldn't get fd for bus\n"); - dbus_error_free(&error); - xfree(configData); - configData = NULL; - return FALSE; - } - - snprintf(configData->busname, sizeof(configData->busname), - "org.x.config.display%d", atoi(display)); - if (!dbus_bus_request_name(configData->connection, configData->busname, - 0, &error) || dbus_error_is_set(&error)) { - ErrorF("[dbus] couldn't take over org.x.config: %s (%s)\n", - error.name, error.message); - dbus_error_free(&error); - dbus_connection_unref(configData->connection); - xfree(configData); - configData = NULL; - return FALSE; - } - - /* blocks until we get a reply. */ - dbus_bus_add_match(configData->connection, CONFIG_MATCH_RULE, &error); - if (dbus_error_is_set(&error)) { - ErrorF("[dbus] couldn't match X.Org rule: %s (%s)\n", error.name, - error.message); - dbus_error_free(&error); - dbus_bus_release_name(configData->connection, configData->busname, - &error); - dbus_connection_unref(configData->connection); - xfree(configData); - configData = NULL; - return FALSE; - } - - if (!dbus_connection_add_filter(configData->connection, configFilter, - configData, NULL)) { - - ErrorF("[dbus] couldn't add signal filter: %s (%s)\n", error.name, - error.message); - dbus_error_free(&error); - dbus_bus_release_name(configData->connection, configData->busname, - &error); - dbus_bus_remove_match(configData->connection, CONFIG_MATCH_RULE, - &error); - dbus_connection_unref(configData->connection); - xfree(configData); - configData = NULL; - return FALSE; - } - - snprintf(configData->busobject, sizeof(configData->busobject), - "/org/x/config/%d", atoi(display)); - if (!dbus_connection_register_object_path(configData->connection, - configData->busobject, &vtable, - configData->connection)) { - ErrorF("[dbus] couldn't register object path\n"); - dbus_bus_release_name(configData->connection, configData->busname, - &error); - dbus_bus_remove_match(configData->connection, CONFIG_MATCH_RULE, - &error); - dbus_connection_unref(configData->connection); - dbus_error_free(&error); - xfree(configData); - configData = NULL; - return FALSE; - } - - DebugF("[dbus] registered object path %s\n", configData->busobject); - - dbus_error_free(&error); - AddGeneralSocket(configData->fd); - - RegisterBlockAndWakeupHandlers(configBlockHandler, configWakeupHandler, - configData); - - return TRUE; -} - -static CARD32 -configReconnect(OsTimerPtr timer, CARD32 time, pointer arg) -{ - if (configSetup()) - return 0; - else - return RECONNECT_DELAY; -} - -void -configInitialise(void) -{ - TimerSet(NULL, 0, 1, configReconnect, NULL); -} +#include "config-backends.h" void -configFini(void) +config_init() { - DBusError error; - - if (configData) { - dbus_error_init(&error); - dbus_connection_unregister_object_path(configData->connection, - configData->busobject); - dbus_connection_remove_filter(configData->connection, configFilter, - configData); - dbus_bus_remove_match(configData->connection, CONFIG_MATCH_RULE, - &error); - dbus_bus_release_name(configData->connection, configData->busname, - &error); - dbus_connection_unref(configData->connection); - dbus_error_free(&error); - configTeardown(); +#if defined(CONFIG_DBUS_API) + if (config_dbus_core_init()) { + if (!config_dbus_init()) + ErrorF("[config] failed to initialise D-Bus API\n"); } -} - -#else /* !HAVE_DBUS */ - -void -configInitialise() -{ + else { + ErrorF("[config] failed to initialise D-Bus core\n"); + } +#endif } void -configFini() +config_fini() { +#if defined(CONFIG_DBUS_API) + config_dbus_fini(); + config_dbus_core_fini(); +#endif } - -#endif /* HAVE_DBUS */ diff --git a/config/dbus-core.c b/config/dbus-core.c new file mode 100644 index 000000000..b90e413f3 --- /dev/null +++ b/config/dbus-core.c @@ -0,0 +1,233 @@ +/* + * Copyright © 2006-2007 Daniel Stone + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders and/or authors + * not be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. The copyright holders + * and/or authors make no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THE COPYRIGHT HOLDERS AND/OR AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND/OR AUTHORS BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#define DBUS_API_SUBJECT_TO_CHANGE +#include +#include + +#include "config-backends.h" +#include "dix.h" +#include "os.h" + +/* How often to attempt reconnecting when we get booted off the bus. */ +#define RECONNECT_DELAY (10 * 1000) /* in ms */ + +struct dbus_core_info { + int fd; + DBusConnection *connection; + OsTimerPtr timer; + struct config_dbus_core_hook *hooks; +}; +static struct dbus_core_info bus_info; + +static CARD32 reconnect_timer(OsTimerPtr timer, CARD32 time, pointer arg); + +static void +wakeup_handler(pointer data, int err, pointer read_mask) +{ + struct dbus_core_info *info = data; + + if (info->connection && FD_ISSET(info->fd, (fd_set *) read_mask)) + dbus_connection_read_write_dispatch(info->connection, 0); +} + +static void +block_handler(pointer data, struct timeval **tv, pointer read_mask) +{ +} + +/** + * Disconnect (if we haven't already been forcefully disconnected), clean up + * after ourselves, and call all registered disconnect hooks. + */ +static void +teardown(void) +{ + struct config_dbus_core_hook *hook; + + if (bus_info.timer) { + TimerCancel(bus_info.timer); + bus_info.timer = NULL; + } + + /* We should really have pre-disconnect hooks and run them here, for + * completeness. But then it gets awkward, given that you can't + * guarantee that they'll be called ... */ + if (bus_info.connection) + dbus_connection_unref(bus_info.connection); + + RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, &bus_info); + RemoveGeneralSocket(bus_info.fd); + bus_info.fd = -1; + bus_info.connection = NULL; + + for (hook = bus_info.hooks; hook; hook = hook->next) { + if (hook->disconnect) + hook->disconnect(hook->data); + } +} + +/** + * This is a filter, which only handles the disconnected signal, which + * doesn't go to the normal message handling function. This takes + * precedence over the message handling function, so have have to be + * careful to ignore anything we don't want to deal with here. + */ +static DBusHandlerResult +message_filter(DBusConnection *connection, DBusMessage *message, void *data) +{ + /* If we get disconnected, then take everything down, and attempt to + * reconnect immediately (assuming it's just a restart). The + * connection isn't valid at this point, so throw it out immediately. */ + if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, + "Disconnected")) { + DebugF("[config/dbus-core] disconnected from bus\n"); + bus_info.connection = NULL; + teardown(); + + bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL); + + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +/** + * Attempt to connect to the system bus, and set a filter to deal with + * disconnection (see message_filter above). + * + * @return 1 on success, 0 on failure. + */ +static int +connect_to_bus(void) +{ + DBusError error; + struct config_dbus_core_hook *hook; + + dbus_error_init(&error); + bus_info.connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error); + if (!bus_info.connection || dbus_error_is_set(&error)) { + DebugF("[config/dbus-core] error connecting to system bus: %s (%s)\n", + error.name, error.message); + goto err_begin; + } + + /* Thankyou. Really, thankyou. */ + dbus_connection_set_exit_on_disconnect(bus_info.connection, FALSE); + + if (!dbus_connection_get_unix_fd(bus_info.connection, &bus_info.fd)) { + ErrorF("[config/dbus-core] couldn't get fd for system bus\n"); + goto err_unref; + } + + if (!dbus_connection_add_filter(bus_info.connection, message_filter, + &bus_info, NULL)) { + ErrorF("[config/dbus-core] couldn't add filter: %s (%s)\n", error.name, + error.message); + goto err_fd; + } + + dbus_error_free(&error); + AddGeneralSocket(bus_info.fd); + + RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, &bus_info); + + for (hook = bus_info.hooks; hook; hook = hook->next) { + if (hook->connect) + hook->connect(bus_info.connection, hook->data); + } + + return 1; + +err_fd: + bus_info.fd = -1; +err_unref: + dbus_connection_unref(bus_info.connection); + bus_info.connection = NULL; +err_begin: + dbus_error_free(&error); + + return 0; +} + +static CARD32 +reconnect_timer(OsTimerPtr timer, CARD32 time, pointer arg) +{ + if (connect_to_bus()) { + bus_info.timer = NULL; + return 0; + } + else { + return RECONNECT_DELAY; + } +} + +int +config_dbus_core_add_hook(struct config_dbus_core_hook *hook) +{ + struct config_dbus_core_hook **prev; + + for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next) + ; + *prev = hook; + + /* If we're already connected, call the connect hook. */ + if (bus_info.connection) + hook->connect(bus_info.connection, hook->data); + + return 1; +} + +void +config_dbus_core_remove_hook(struct config_dbus_core_hook *hook) +{ + struct config_dbus_core_hook **prev; + + for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next) { + if (*prev == hook) + *prev = hook->next; + } +} + +int +config_dbus_core_init(void) +{ + memset(&bus_info, 0, sizeof(bus_info)); + bus_info.fd = -1; + bus_info.hooks = NULL; + bus_info.connection = NULL; + bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL); + + return 1; +} + +void +config_dbus_core_fini(void) +{ + teardown(); +} diff --git a/config/dbus.c b/config/dbus.c new file mode 100644 index 000000000..2450e0c00 --- /dev/null +++ b/config/dbus.c @@ -0,0 +1,407 @@ +/* + * Copyright © 2006-2007 Daniel Stone + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders and/or authors + * not be used in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. The copyright holders + * and/or authors make no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * THE COPYRIGHT HOLDERS AND/OR AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND/OR AUTHORS BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#define DBUS_API_SUBJECT_TO_CHANGE +#include +#include + +#include + +#include "config-backends.h" +#include "opaque.h" /* for 'display': there should be a better way. */ +#include "input.h" +#include "inputstr.h" + +#define API_VERSION 1 + +#define MATCH_RULE "type='method_call',interface='org.x.config.input'" + +#define MALFORMED_MSG "[config/dbus] malformed message, dropping" +#define MALFORMED_MESSAGE() { DebugF(MALFORMED_MSG "\n"); \ + ret = BadValue; \ + goto unwind; } +#define MALFORMED_MESSAGE_ERROR() { DebugF(MALFORMED_MSG ": %s, %s", \ + error->name, error->message); \ + ret = BadValue; \ + goto unwind; } + +struct connection_info { + char busobject[32]; + char busname[64]; + DBusConnection *connection; +}; + +static void +reset_info(struct connection_info *info) +{ + info->connection = NULL; + info->busname[0] = '\0'; + info->busobject[0] = '\0'; +} + +static int +add_device(DBusMessage *message, DBusMessage *reply, DBusError *error) +{ + DBusMessageIter iter, reply_iter, subiter; + InputOption *tmpo = NULL, *options = NULL; + char *tmp = NULL; + int ret, err; + DeviceIntPtr dev = NULL; + + if (!dbus_message_iter_init(message, &iter)) { + ErrorF("[config/dbus] couldn't initialise iterator\n"); + return BadAlloc; + } + dbus_message_iter_init_append(reply, &reply_iter); + + options = xcalloc(sizeof(*options), 1); + if (!options) { + ErrorF("[config/dbus] couldn't allocate option\n"); + return BadAlloc; + } + + options->key = xstrdup("_source"); + options->value = xstrdup("client/dbus"); + if (!options->key || !options->value) { + ErrorF("[config/dbus] couldn't allocate first key/value pair\n"); + ret = BadAlloc; + goto unwind; + } + + /* signature should be [ss][ss]... */ + while (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) { + tmpo = xcalloc(sizeof(*tmpo), 1); + if (!tmpo) { + ErrorF("[config/dbus] couldn't allocate option\n"); + ret = BadAlloc; + goto unwind; + } + tmpo->next = options; + options = tmpo; + + dbus_message_iter_recurse(&iter, &subiter); + + if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING) + MALFORMED_MESSAGE(); + + dbus_message_iter_get_basic(&subiter, &tmp); + if (!tmp) + MALFORMED_MESSAGE(); + /* The _ prefix refers to internal settings, and may not be given by + * the client. */ + if (tmp[0] == '_') { + ErrorF("[config/dbus] attempted subterfuge: option name %s given\n", + tmp); + MALFORMED_MESSAGE(); + } + options->key = xstrdup(tmp); + if (!options->key) { + ErrorF("[config/dbus] couldn't duplicate key!\n"); + ret = BadAlloc; + goto unwind; + } + + if (!dbus_message_iter_has_next(&subiter)) + MALFORMED_MESSAGE(); + dbus_message_iter_next(&subiter); + if (dbus_message_iter_get_arg_type(&subiter) != DBUS_TYPE_STRING) + MALFORMED_MESSAGE(); + + dbus_message_iter_get_basic(&subiter, &tmp); + if (!tmp) + MALFORMED_MESSAGE(); + options->value = xstrdup(tmp); + if (!options->value) { + ErrorF("[config] couldn't duplicate option!\n"); + ret = BadAlloc; + goto unwind; + } + + dbus_message_iter_next(&iter); + } + + ret = NewInputDeviceRequest(options, &dev); + if (ret != Success) { + DebugF("[config/dbus] NewInputDeviceRequest failed\n"); + goto unwind; + } + + if (!dev) { + DebugF("[config/dbus] NewInputDeviceRequest provided no device\n"); + ret = BadImplementation; + goto unwind; + } + + if (!dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, + &dev->id)) { + ErrorF("[config/dbus] couldn't append to iterator\n"); + ret = BadAlloc; + goto unwind; + } + +unwind: + if (ret != Success) { + if (dev) + RemoveDevice(dev); + + err = -ret; + dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &err); + } + + while (options) { + tmpo = options; + options = options->next; + if (tmpo->key) + xfree(tmpo->key); + if (tmpo->value) + xfree(tmpo->value); + xfree(tmpo); + } + + return ret; +} + +static int +remove_device(DBusMessage *message, DBusMessage *reply, DBusError *error) +{ + int deviceid, ret, err; + DeviceIntPtr dev; + DBusMessageIter iter, reply_iter; + + if (!dbus_message_iter_init(message, &iter)) { + ErrorF("[config] failed to init iterator\n"); + return BadAlloc; + } + dbus_message_iter_init_append(reply, &reply_iter); + + if (!dbus_message_get_args(message, error, DBUS_TYPE_INT32, + &deviceid, DBUS_TYPE_INVALID)) { + MALFORMED_MESSAGE_ERROR(); + } + + dev = LookupDeviceIntRec(deviceid); + if (!dev) { + DebugF("[config] bogus device id %d given\n", deviceid); + ret = BadMatch; + goto unwind; + } + + DebugF("[config] removing device %s (id %d)\n", dev->name, deviceid); + + /* Call PIE here so we don't try to dereference a device that's + * already been removed. */ + OsBlockSignals(); + ProcessInputEvents(); + DeleteInputDeviceRequest(dev); + OsReleaseSignals(); + + ret = Success; + +unwind: + err = (ret == Success) ? ret : -ret; + dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &err); + + return ret; +} + +static int +list_devices(DBusMessage *message, DBusMessage *reply, DBusError *error) +{ + DeviceIntPtr dev; + DBusMessageIter iter; + + dbus_message_iter_init_append(reply, &iter); + + for (dev = inputInfo.devices; dev; dev = dev->next) { + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, + &dev->id)) { + ErrorF("[config/dbus] couldn't append to iterator\n"); + return BadAlloc; + } + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + &dev->name)) { + ErrorF("[config/dbus] couldn't append to iterator\n"); + return BadAlloc; + } + } + + return Success; +} + +static DBusHandlerResult +message_handler(DBusConnection *connection, DBusMessage *message, void *data) +{ + DBusError error; + DBusMessage *reply; + struct connection_info *info = data; + + /* ret is the overall D-Bus handler result, whereas err is the internal + * X error from our individual functions. */ + int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + int err; + + DebugF("[config/dbus] received a message for %s\n", + dbus_message_get_interface(message)); + + dbus_error_init(&error); + + reply = dbus_message_new_method_return(message); + if (!reply) { + ErrorF("[config/dbus] failed to create reply\n"); + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto err_start; + } + + if (strcmp(dbus_message_get_member(message), "add") == 0) + err = add_device(message, reply, &error); + else if (strcmp(dbus_message_get_member(message), "remove") == 0) + err = remove_device(message, reply, &error); + else if (strcmp(dbus_message_get_member(message), "listDevices") == 0) + err = list_devices(message, reply, &error); + else + goto err_reply; + + /* Failure to allocate is a special case. */ + if (err == BadAlloc) { + ret = DBUS_HANDLER_RESULT_NEED_MEMORY; + goto err_reply; + } + + /* While failure here is always an OOM, we don't return that, + * since that would result in devices being double-added/removed. */ + if (dbus_connection_send(info->connection, reply, NULL)) + dbus_connection_flush(info->connection); + else + ErrorF("[config/dbus] failed to send reply\n"); + + ret = DBUS_HANDLER_RESULT_HANDLED; + +err_reply: + dbus_message_unref(reply); +err_start: + dbus_error_free(&error); + + return ret; +} + +static void +connect_hook(DBusConnection *connection, void *data) +{ + DBusError error; + DBusObjectPathVTable vtable = { .message_function = message_handler, }; + struct connection_info *info = data; + + info->connection = connection; + + dbus_error_init(&error); + + if (!dbus_bus_request_name(info->connection, info->busname, + 0, &error)) { + ErrorF("[config/dbus] couldn't take over org.x.config: %s (%s)\n", + error.name, error.message); + goto err_start; + } + + /* blocks until we get a reply. */ + dbus_bus_add_match(info->connection, MATCH_RULE, &error); + if (dbus_error_is_set(&error)) { + ErrorF("[config/dbus] couldn't add match: %s (%s)\n", error.name, + error.message); + goto err_name; + } + + if (!dbus_connection_register_object_path(info->connection, + info->busobject, &vtable, + info)) { + ErrorF("[config/dbus] couldn't register object path\n"); + goto err_match; + } + + DebugF("[dbus] registered %s, %s\n", info->busname, info->busobject); + + dbus_error_free(&error); + + return; + +err_match: + dbus_bus_remove_match(info->connection, MATCH_RULE, &error); +err_name: + dbus_bus_release_name(info->connection, info->busname, &error); +err_start: + dbus_error_free(&error); + + reset_info(info); +} + +static void +disconnect_hook(void *data) +{ + struct connection_info *info = data; + + reset_info(info); +} + +#if 0 +void +pre_disconnect_hook(void) +{ + DBusError error; + + dbus_error_init(&error); + dbus_connection_unregister_object_path(connection_data->connection, + connection_data->busobject); + dbus_bus_remove_match(connection_data->connection, MATCH_RULE, + &error); + dbus_bus_release_name(connection_data->connection, + connection_data->busname, &error); + dbus_error_free(&error); +} +#endif + +static struct connection_info connection_data; +static struct config_dbus_core_hook core_hook = { + .connect = connect_hook, + .disconnect = disconnect_hook, + .data = &connection_data, +}; + +int +config_dbus_init(void) +{ + snprintf(connection_data.busname, sizeof(connection_data.busname), + "org.x.config.display%d", atoi(display)); + snprintf(connection_data.busobject, sizeof(connection_data.busobject), + "/org/x/config/%d", atoi(display)); + + return config_dbus_core_add_hook(&core_hook); +} + +void +config_dbus_fini(void) +{ + config_dbus_core_remove_hook(&core_hook); +} -- cgit v1.2.3