Commit 1cdadc2f authored by Daniel Stone's avatar Daniel Stone
Browse files

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.
parent 8bfa41e1
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
/*
* 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
/*
* 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 */
/*
* 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 */