summaryrefslogtreecommitdiff
path: root/config
diff options
context:
space:
mode:
authorDaniel Stone <daniel@fooishbar.org>2007-07-08 14:28:58 +0300
committerDaniel Stone <daniel@fooishbar.org>2007-08-01 01:53:31 +0300
commit1cdadc2f43d9069572814510d04b1a560c488fcb (patch)
tree09e403c8ff69abbc00e635c787c83e43fd3a26d7 /config
parent8bfa41e1bf3f588780d7e9f6f900b1fde0570a7e (diff)
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.
Diffstat (limited to 'config')
-rw-r--r--config/Makefile.am13
-rw-r--r--config/config-backends.h52
-rw-r--r--config/config.c494
-rw-r--r--config/dbus-core.c233
-rw-r--r--config/dbus.c407
5 files changed, 719 insertions, 480 deletions
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 <dix-config.h>
+#endif
+
+#ifdef HAVE_DBUS
+#include <dbus/dbus.h>
+
+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 <dix-config.h>
#endif
-#ifdef HAVE_DBUS
-#define DBUS_API_SUBJECT_TO_CHANGE
-#include <dbus/dbus.h>
-#include <string.h>
-#include <sys/select.h>
-
-#include <X11/X.h>
-
-#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 <dix-config.h>
+#endif
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <sys/select.h>
+
+#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 <dix-config.h>
+#endif
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <string.h>
+
+#include <X11/X.h>
+
+#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);
+}