From c38dead3ea7e177728d90cd815cf4eead0c9f534 Mon Sep 17 00:00:00 2001 From: marha Date: Sat, 15 May 2010 16:28:11 +0000 Subject: xserver git update 15/5/2010 --- xorg-server/config/config.c | 278 +++++----- xorg-server/config/dbus.c | 884 ++++++++++++++--------------- xorg-server/config/hal.c | 1294 +++++++++++++++++++++---------------------- xorg-server/config/udev.c | 534 +++++++++--------- 4 files changed, 1495 insertions(+), 1495 deletions(-) (limited to 'xorg-server/config') diff --git a/xorg-server/config/config.c b/xorg-server/config/config.c index 7bf5e4179..1ffbdcdad 100644 --- a/xorg-server/config/config.c +++ b/xorg-server/config/config.c @@ -1,139 +1,139 @@ -/* - * Copyright © 2006-2007 Daniel Stone - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Daniel Stone - */ - -#ifdef HAVE_DIX_CONFIG_H -#include -#endif - -#include "os.h" -#include "inputstr.h" -#include "hotplug.h" -#include "config-backends.h" - -void -config_init(void) -{ -#ifdef CONFIG_UDEV - if (!config_udev_init()) - ErrorF("[config] failed to initialise udev\n"); -#elif defined(CONFIG_NEED_DBUS) - if (config_dbus_core_init()) { -# ifdef CONFIG_DBUS_API - if (!config_dbus_init()) - ErrorF("[config] failed to initialise D-Bus API\n"); -# endif -# ifdef CONFIG_HAL - if (!config_hal_init()) - ErrorF("[config] failed to initialise HAL\n"); -# endif - } - else { - ErrorF("[config] failed to initialise D-Bus core\n"); - } -#endif -} - -void -config_fini(void) -{ -#if defined(CONFIG_UDEV) - config_udev_fini(); -#elif defined(CONFIG_NEED_DBUS) -# ifdef CONFIG_HAL - config_hal_fini(); -# endif -# ifdef CONFIG_DBUS_API - config_dbus_fini(); -# endif - config_dbus_core_fini(); -#endif -} - -static void -remove_device(const char *backend, DeviceIntPtr dev) -{ - /* this only gets called for devices that have already been added */ - LogMessage(X_INFO, "config/%s: removing device %s\n", backend, dev->name); - - /* Call PIE here so we don't try to dereference a device that's - * already been removed. */ - OsBlockSignals(); - ProcessInputEvents(); - DeleteInputDeviceRequest(dev); - OsReleaseSignals(); -} - -void -remove_devices(const char *backend, const char *config_info) -{ - DeviceIntPtr dev, next; - - for (dev = inputInfo.devices; dev; dev = next) { - next = dev->next; - if (dev->config_info && strcmp(dev->config_info, config_info) == 0) - remove_device(backend, dev); - } - for (dev = inputInfo.off_devices; dev; dev = next) { - next = dev->next; - if (dev->config_info && strcmp(dev->config_info, config_info) == 0) - remove_device(backend, dev); - } -} - -BOOL -device_is_duplicate(const char *config_info) -{ - DeviceIntPtr dev; - - for (dev = inputInfo.devices; dev; dev = dev->next) - { - if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) - return TRUE; - } - - for (dev = inputInfo.off_devices; dev; dev = dev->next) - { - if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) - return TRUE; - } - - return FALSE; -} - -void -add_option(InputOption **options, const char *key, const char *value) -{ - if (!value || *value == '\0') - return; - - for (; *options; options = &(*options)->next) - ; - *options = xcalloc(sizeof(**options), 1); - if (!*options) /* Yeesh. */ - return; - (*options)->key = xstrdup(key); - (*options)->value = xstrdup(value); - (*options)->next = NULL; -} +/* + * Copyright © 2006-2007 Daniel Stone + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include "os.h" +#include "inputstr.h" +#include "hotplug.h" +#include "config-backends.h" + +void +config_init(void) +{ +#ifdef CONFIG_UDEV + if (!config_udev_init()) + ErrorF("[config] failed to initialise udev\n"); +#elif defined(CONFIG_NEED_DBUS) + if (config_dbus_core_init()) { +# ifdef CONFIG_DBUS_API + if (!config_dbus_init()) + ErrorF("[config] failed to initialise D-Bus API\n"); +# endif +# ifdef CONFIG_HAL + if (!config_hal_init()) + ErrorF("[config] failed to initialise HAL\n"); +# endif + } + else { + ErrorF("[config] failed to initialise D-Bus core\n"); + } +#endif +} + +void +config_fini(void) +{ +#if defined(CONFIG_UDEV) + config_udev_fini(); +#elif defined(CONFIG_NEED_DBUS) +# ifdef CONFIG_HAL + config_hal_fini(); +# endif +# ifdef CONFIG_DBUS_API + config_dbus_fini(); +# endif + config_dbus_core_fini(); +#endif +} + +static void +remove_device(const char *backend, DeviceIntPtr dev) +{ + /* this only gets called for devices that have already been added */ + LogMessage(X_INFO, "config/%s: removing device %s\n", backend, dev->name); + + /* Call PIE here so we don't try to dereference a device that's + * already been removed. */ + OsBlockSignals(); + ProcessInputEvents(); + DeleteInputDeviceRequest(dev); + OsReleaseSignals(); +} + +void +remove_devices(const char *backend, const char *config_info) +{ + DeviceIntPtr dev, next; + + for (dev = inputInfo.devices; dev; dev = next) { + next = dev->next; + if (dev->config_info && strcmp(dev->config_info, config_info) == 0) + remove_device(backend, dev); + } + for (dev = inputInfo.off_devices; dev; dev = next) { + next = dev->next; + if (dev->config_info && strcmp(dev->config_info, config_info) == 0) + remove_device(backend, dev); + } +} + +BOOL +device_is_duplicate(const char *config_info) +{ + DeviceIntPtr dev; + + for (dev = inputInfo.devices; dev; dev = dev->next) + { + if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) + return TRUE; + } + + for (dev = inputInfo.off_devices; dev; dev = dev->next) + { + if (dev->config_info && (strcmp(dev->config_info, config_info) == 0)) + return TRUE; + } + + return FALSE; +} + +void +add_option(InputOption **options, const char *key, const char *value) +{ + if (!value || *value == '\0') + return; + + for (; *options; options = &(*options)->next) + ; + *options = calloc(sizeof(**options), 1); + if (!*options) /* Yeesh. */ + return; + (*options)->key = xstrdup(key); + (*options)->value = xstrdup(value); + (*options)->next = NULL; +} diff --git a/xorg-server/config/dbus.c b/xorg-server/config/dbus.c index 86d9d287f..d6316623b 100644 --- a/xorg-server/config/dbus.c +++ b/xorg-server/config/dbus.c @@ -1,442 +1,442 @@ -/* - * Copyright © 2006-2007 Daniel Stone - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Daniel Stone - */ - -#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 2 - -#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; - - dbus_message_iter_init_append(reply, &reply_iter); - - if (!dbus_message_iter_init(message, &iter)) { - ErrorF("[config/dbus] couldn't initialise iterator\n"); - MALFORMED_MESSAGE(); - } - - 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/dbus] couldn't duplicate option!\n"); - ret = BadAlloc; - goto unwind; - } - - dbus_message_iter_next(&iter); - } - - ret = NewInputDeviceRequest(options, NULL, &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; - } - - /* XXX: If we fail halfway through, we don't seem to have any way to - * empty the iterator, so you'll end up with some device IDs, - * plus an error. This seems to be a shortcoming in the D-Bus - * API. */ - for (; dev; dev = dev->next) { - 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, TRUE); - - 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; - - dbus_message_iter_init_append(reply, &reply_iter); - - if (!dbus_message_iter_init(message, &iter)) { - ErrorF("[config/dbus] failed to init iterator\n"); - MALFORMED_MESSAGE(); - } - - if (!dbus_message_get_args(message, error, DBUS_TYPE_UINT32, - &deviceid, DBUS_TYPE_INVALID)) { - MALFORMED_MESSAGE_ERROR(); - } - - dixLookupDevice(&dev, deviceid, serverClient, DixDestroyAccess); - if (!dev) { - DebugF("[config/dbus] bogus device id %d given\n", deviceid); - ret = BadMatch; - goto unwind; - } - - DebugF("[config/dbus] 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, subiter; - - dbus_message_iter_init_append(reply, &iter); - - for (dev = inputInfo.devices; dev; dev = dev->next) { - if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL, - &subiter)) { - ErrorF("[config/dbus] couldn't init container\n"); - return BadAlloc; - } - if (!dbus_message_iter_append_basic(&subiter, DBUS_TYPE_UINT32, - &dev->id)) { - ErrorF("[config/dbus] couldn't append to iterator\n"); - return BadAlloc; - } - if (!dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING, - &dev->name)) { - ErrorF("[config/dbus] couldn't append to iterator\n"); - return BadAlloc; - } - if (!dbus_message_iter_close_container(&iter, &subiter)) { - ErrorF("[config/dbus] couldn't close container\n"); - return BadAlloc; - } - } - - return Success; -} - -static int -get_version(DBusMessage *message, DBusMessage *reply, DBusError *error) -{ - DBusMessageIter iter; - unsigned int version = API_VERSION; - - dbus_message_iter_init_append(reply, &iter); - if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &version)) { - ErrorF("[config/dbus] couldn't append version\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 if (strcmp(dbus_message_get_member(message), "version") == 0) - err = get_version(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); - - dbus_bus_request_name(info->connection, info->busname, 0, &error); - if (dbus_error_is_set(&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) -{ -} - -#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); - connection_data.busname[0] = '\0'; - connection_data.busobject[0] = '\0'; -} +/* + * Copyright © 2006-2007 Daniel Stone + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone + */ + +#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 2 + +#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; + + dbus_message_iter_init_append(reply, &reply_iter); + + if (!dbus_message_iter_init(message, &iter)) { + ErrorF("[config/dbus] couldn't initialise iterator\n"); + MALFORMED_MESSAGE(); + } + + options = calloc(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 = calloc(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/dbus] couldn't duplicate option!\n"); + ret = BadAlloc; + goto unwind; + } + + dbus_message_iter_next(&iter); + } + + ret = NewInputDeviceRequest(options, NULL, &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; + } + + /* XXX: If we fail halfway through, we don't seem to have any way to + * empty the iterator, so you'll end up with some device IDs, + * plus an error. This seems to be a shortcoming in the D-Bus + * API. */ + for (; dev; dev = dev->next) { + 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, TRUE); + + err = -ret; + dbus_message_iter_append_basic(&reply_iter, DBUS_TYPE_INT32, &err); + } + + while (options) { + tmpo = options; + options = options->next; + if (tmpo->key) + free(tmpo->key); + if (tmpo->value) + free(tmpo->value); + free(tmpo); + } + + return ret; +} + +static int +remove_device(DBusMessage *message, DBusMessage *reply, DBusError *error) +{ + int deviceid, ret, err; + DeviceIntPtr dev; + DBusMessageIter iter, reply_iter; + + dbus_message_iter_init_append(reply, &reply_iter); + + if (!dbus_message_iter_init(message, &iter)) { + ErrorF("[config/dbus] failed to init iterator\n"); + MALFORMED_MESSAGE(); + } + + if (!dbus_message_get_args(message, error, DBUS_TYPE_UINT32, + &deviceid, DBUS_TYPE_INVALID)) { + MALFORMED_MESSAGE_ERROR(); + } + + dixLookupDevice(&dev, deviceid, serverClient, DixDestroyAccess); + if (!dev) { + DebugF("[config/dbus] bogus device id %d given\n", deviceid); + ret = BadMatch; + goto unwind; + } + + DebugF("[config/dbus] 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, subiter; + + dbus_message_iter_init_append(reply, &iter); + + for (dev = inputInfo.devices; dev; dev = dev->next) { + if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL, + &subiter)) { + ErrorF("[config/dbus] couldn't init container\n"); + return BadAlloc; + } + if (!dbus_message_iter_append_basic(&subiter, DBUS_TYPE_UINT32, + &dev->id)) { + ErrorF("[config/dbus] couldn't append to iterator\n"); + return BadAlloc; + } + if (!dbus_message_iter_append_basic(&subiter, DBUS_TYPE_STRING, + &dev->name)) { + ErrorF("[config/dbus] couldn't append to iterator\n"); + return BadAlloc; + } + if (!dbus_message_iter_close_container(&iter, &subiter)) { + ErrorF("[config/dbus] couldn't close container\n"); + return BadAlloc; + } + } + + return Success; +} + +static int +get_version(DBusMessage *message, DBusMessage *reply, DBusError *error) +{ + DBusMessageIter iter; + unsigned int version = API_VERSION; + + dbus_message_iter_init_append(reply, &iter); + if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT32, &version)) { + ErrorF("[config/dbus] couldn't append version\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 if (strcmp(dbus_message_get_member(message), "version") == 0) + err = get_version(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); + + dbus_bus_request_name(info->connection, info->busname, 0, &error); + if (dbus_error_is_set(&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) +{ +} + +#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); + connection_data.busname[0] = '\0'; + connection_data.busobject[0] = '\0'; +} diff --git a/xorg-server/config/hal.c b/xorg-server/config/hal.c index d3daa84cd..738cb3185 100644 --- a/xorg-server/config/hal.c +++ b/xorg-server/config/hal.c @@ -1,647 +1,647 @@ -/* - * Copyright © 2007 Daniel Stone - * Copyright © 2007 Red Hat, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Daniel Stone - */ - -#ifdef HAVE_DIX_CONFIG_H -#include -#endif - -#include -#include -#include -#include - -#include "input.h" -#include "inputstr.h" -#include "hotplug.h" -#include "config-backends.h" -#include "os.h" - - -#define LIBHAL_PROP_KEY "input.x11_options." -#define LIBHAL_XKB_PROP_KEY "input.xkb." - - -struct config_hal_info { - DBusConnection *system_bus; - LibHalContext *hal_ctx; -}; - -/* Used for special handling of xkb options. */ -struct xkb_options { - char* layout; - char* model; - char* rules; - char* variant; - char* options; -}; - -static void -device_removed(LibHalContext *ctx, const char *udi) -{ - char *value; - - value = xalloc(strlen(udi) + 5); /* "hal:" + NULL */ - if (!value) - return; - sprintf(value, "hal:%s", udi); - - remove_devices("hal", value); - - xfree(value); -} - -static char * -get_prop_string(LibHalContext *hal_ctx, const char *udi, const char *name) -{ - char *prop, *ret; - - prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL); - LogMessageVerb(X_INFO, 10, "config/hal: getting %s on %s returned %s\n", name, udi, prop ? prop : "(null)"); - if (prop) { - ret = xstrdup(prop); - libhal_free_string(prop); - } - else { - return NULL; - } - - return ret; -} - -static char * -get_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop) -{ - char **props, *ret, *str; - int i, len = 0; - - props = libhal_device_get_property_strlist(hal_ctx, udi, prop, NULL); - if (props) { - for (i = 0; props[i]; i++) - len += strlen(props[i]); - - ret = xcalloc(sizeof(char), len + i); /* i - 1 commas, 1 NULL */ - if (!ret) { - libhal_free_string_array(props); - return NULL; - } - - str = ret; - for (i = 0; props[i]; i++) { - strcpy(str, props[i]); - str += strlen(props[i]); - *str++ = ','; - } - *(str-1) = '\0'; - - libhal_free_string_array(props); - } - else { - return NULL; - } - - return ret; -} - -static void -device_added(LibHalContext *hal_ctx, const char *udi) -{ - char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL; - InputOption *options = NULL, *tmpo = NULL; - InputAttributes attrs = {0}; - DeviceIntPtr dev = NULL; - DBusError error; - struct xkb_options xkb_opts = {0}; - int rc; - - LibHalPropertySet *set = NULL; - LibHalPropertySetIterator set_iter; - char *psi_key = NULL, *tmp_val; - - - dbus_error_init(&error); - - driver = get_prop_string(hal_ctx, udi, "input.x11_driver"); - if (!driver){ - /* verbose, don't tell the user unless they _want_ to see it */ - LogMessageVerb(X_INFO,7,"config/hal: no driver specified for device %s\n", udi); - goto unwind; - } - - path = get_prop_string(hal_ctx, udi, "input.device"); - if (!path) { - LogMessage(X_WARNING,"config/hal: no driver or path specified for %s\n", udi); - goto unwind; - } - attrs.device = xstrdup(path); - - name = get_prop_string(hal_ctx, udi, "info.product"); - if (!name) - name = xstrdup("(unnamed)"); - else - attrs.product = xstrdup(name); - - attrs.vendor = get_prop_string(hal_ctx, udi, "info.vendor"); - attrs.tags = xstrtokenize(get_prop_string(hal_ctx, udi, "input.tags"), ","); - - if (libhal_device_query_capability(hal_ctx, udi, "input.keys", NULL)) - attrs.flags |= ATTR_KEYBOARD; - if (libhal_device_query_capability(hal_ctx, udi, "input.mouse", NULL)) - attrs.flags |= ATTR_POINTER; - if (libhal_device_query_capability(hal_ctx, udi, "input.joystick", NULL)) - attrs.flags |= ATTR_JOYSTICK; - if (libhal_device_query_capability(hal_ctx, udi, "input.tablet", NULL)) - attrs.flags |= ATTR_TABLET; - if (libhal_device_query_capability(hal_ctx, udi, "input.touchpad", NULL)) - attrs.flags |= ATTR_TOUCHPAD; - if (libhal_device_query_capability(hal_ctx, udi, "input.touchscreen", NULL)) - attrs.flags |= ATTR_TOUCHSCREEN; - - options = xcalloc(sizeof(*options), 1); - if (!options){ - LogMessage(X_ERROR, "config/hal: couldn't allocate space for input options!\n"); - goto unwind; - } - - options->key = xstrdup("_source"); - options->value = xstrdup("server/hal"); - if (!options->key || !options->value) { - LogMessage(X_ERROR, "config/hal: couldn't allocate first key/value pair\n"); - goto unwind; - } - - /* most drivers use device.. not path. evdev uses both however, but the - * path version isn't documented apparently. support both for now. */ - add_option(&options, "path", path); - add_option(&options, "device", path); - - add_option(&options, "driver", driver); - add_option(&options, "name", name); - - config_info = xalloc(strlen(udi) + 5); /* "hal:" and NULL */ - if (!config_info) { - LogMessage(X_ERROR, "config/hal: couldn't allocate name\n"); - goto unwind; - } - sprintf(config_info, "hal:%s", udi); - - /* Check for duplicate devices */ - if (device_is_duplicate(config_info)) - { - LogMessage(X_WARNING, "config/hal: device %s already added. Ignoring.\n", name); - goto unwind; - } - - /* ok, grab options from hal.. iterate through all properties - * and lets see if any of them are options that we can add */ - set = libhal_device_get_all_properties(hal_ctx, udi, &error); - - if (!set) { - LogMessage(X_ERROR, "config/hal: couldn't get property list for %s: %s (%s)\n", - udi, error.name, error.message); - goto unwind; - } - - libhal_psi_init(&set_iter,set); - while (libhal_psi_has_more(&set_iter)) { - /* we are looking for supported keys.. extract and add to options */ - psi_key = libhal_psi_get_key(&set_iter); - - if (psi_key){ - - /* normal options first (input.x11_options.) */ - if (!strncasecmp(psi_key, LIBHAL_PROP_KEY, sizeof(LIBHAL_PROP_KEY)-1)){ - char* tmp; - - /* only support strings for all values */ - tmp_val = get_prop_string(hal_ctx, udi, psi_key); - - if (tmp_val){ - - /* xkb needs special handling. HAL specs include - * input.xkb.xyz options, but the x11-input.fdi specifies - * input.x11_options.Xkbxyz options. By default, we use - * the former, unless the specific X11 ones are specified. - * Since we can't predict the order in which the keys - * arrive, we need to store them. - */ - if ((tmp = strcasestr(psi_key, "xkb")) && strlen(tmp) >= 4) - { - if (!strcasecmp(&tmp[3], "layout")) - { - if (xkb_opts.layout) - xfree(xkb_opts.layout); - xkb_opts.layout = strdup(tmp_val); - } else if (!strcasecmp(&tmp[3], "model")) - { - if (xkb_opts.model) - xfree(xkb_opts.model); - xkb_opts.model = strdup(tmp_val); - } else if (!strcasecmp(&tmp[3], "rules")) - { - if (xkb_opts.rules) - xfree(xkb_opts.rules); - xkb_opts.rules = strdup(tmp_val); - } else if (!strcasecmp(&tmp[3], "variant")) - { - if (xkb_opts.variant) - xfree(xkb_opts.variant); - xkb_opts.variant = strdup(tmp_val); - } else if (!strcasecmp(&tmp[3], "options")) - { - if (xkb_opts.options) - xfree(xkb_opts.options); - xkb_opts.options = strdup(tmp_val); - } - } else - { - /* all others */ - add_option(&options, psi_key + sizeof(LIBHAL_PROP_KEY)-1, tmp_val); - xfree(tmp_val); - } - } else - { - /* server 1.4 had xkb_options as strlist. */ - if ((tmp = strcasestr(psi_key, "xkb")) && - (strlen(tmp) >= 4) && - (!strcasecmp(&tmp[3], "options")) && - (tmp_val = get_prop_string_array(hal_ctx, udi, psi_key))) - { - if (xkb_opts.options) - xfree(xkb_opts.options); - xkb_opts.options = strdup(tmp_val); - } - } - } else if (!strncasecmp(psi_key, LIBHAL_XKB_PROP_KEY, sizeof(LIBHAL_XKB_PROP_KEY)-1)){ - char* tmp; - - /* only support strings for all values */ - tmp_val = get_prop_string(hal_ctx, udi, psi_key); - - if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) { - - tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; - - if (!strcasecmp(tmp, "layout")) - { - if (!xkb_opts.layout) - xkb_opts.layout = strdup(tmp_val); - } else if (!strcasecmp(tmp, "rules")) - { - if (!xkb_opts.rules) - xkb_opts.rules = strdup(tmp_val); - } else if (!strcasecmp(tmp, "variant")) - { - if (!xkb_opts.variant) - xkb_opts.variant = strdup(tmp_val); - } else if (!strcasecmp(tmp, "model")) - { - if (!xkb_opts.model) - xkb_opts.model = strdup(tmp_val); - } else if (!strcasecmp(tmp, "options")) - { - if (!xkb_opts.options) - xkb_opts.options = strdup(tmp_val); - } - xfree(tmp_val); - } else - { - /* server 1.4 had xkb options as strlist */ - tmp_val = get_prop_string_array(hal_ctx, udi, psi_key); - if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) - { - tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; - if (!strcasecmp(tmp, ".options") && (!xkb_opts.options)) - xkb_opts.options = strdup(tmp_val); - } - } - } - } - - /* psi_key doesn't need to be freed */ - libhal_psi_next(&set_iter); - } - - - /* Now add xkb options */ - if (xkb_opts.layout) - add_option(&options, "xkb_layout", xkb_opts.layout); - if (xkb_opts.rules) - add_option(&options, "xkb_rules", xkb_opts.rules); - if (xkb_opts.variant) - add_option(&options, "xkb_variant", xkb_opts.variant); - if (xkb_opts.model) - add_option(&options, "xkb_model", xkb_opts.model); - if (xkb_opts.options) - add_option(&options, "xkb_options", xkb_opts.options); - - /* this isn't an error, but how else do you output something that the user can see? */ - LogMessage(X_INFO, "config/hal: Adding input device %s\n", name); - if ((rc = NewInputDeviceRequest(options, &attrs, &dev)) != Success) { - LogMessage(X_ERROR, "config/hal: NewInputDeviceRequest failed (%d)\n", rc); - dev = NULL; - goto unwind; - } - - for (; dev; dev = dev->next){ - if (dev->config_info) - xfree(dev->config_info); - dev->config_info = xstrdup(config_info); - } - -unwind: - if (set) - libhal_free_property_set(set); - if (path) - xfree(path); - if (driver) - xfree(driver); - if (name) - xfree(name); - if (config_info) - xfree(config_info); - while (!dev && (tmpo = options)) { - options = tmpo->next; - xfree(tmpo->key); - xfree(tmpo->value); - xfree(tmpo); - } - - xfree(attrs.product); - xfree(attrs.vendor); - xfree(attrs.device); - if (attrs.tags) { - char **tag = attrs.tags; - while (*tag) { - xfree(*tag); - tag++; - } - xfree(attrs.tags); - } - - if (xkb_opts.layout) - xfree(xkb_opts.layout); - if (xkb_opts.rules) - xfree(xkb_opts.rules); - if (xkb_opts.model) - xfree(xkb_opts.model); - if (xkb_opts.variant) - xfree(xkb_opts.variant); - if (xkb_opts.options) - xfree(xkb_opts.options); - - dbus_error_free(&error); - - return; -} - -static void -disconnect_hook(void *data) -{ - DBusError error; - struct config_hal_info *info = data; - - if (info->hal_ctx) { - if (dbus_connection_get_is_connected(info->system_bus)) { - dbus_error_init(&error); - if (!libhal_ctx_shutdown(info->hal_ctx, &error)) - LogMessage(X_WARNING, "config/hal: disconnect_hook couldn't shut down context: %s (%s)\n", - error.name, error.message); - dbus_error_free(&error); - } - libhal_ctx_free(info->hal_ctx); - } - - info->hal_ctx = NULL; - info->system_bus = NULL; -} - -static BOOL -connect_and_register(DBusConnection *connection, struct config_hal_info *info) -{ - DBusError error; - char **devices; - int num_devices, i; - - if (info->hal_ctx) - return TRUE; /* already registered, pretend we did something */ - - info->system_bus = connection; - - dbus_error_init(&error); - - info->hal_ctx = libhal_ctx_new(); - if (!info->hal_ctx) { - LogMessage(X_ERROR, "config/hal: couldn't create HAL context\n"); - goto out_err; - } - - if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) { - LogMessage(X_ERROR, "config/hal: couldn't associate HAL context with bus\n"); - goto out_err; - } - if (!libhal_ctx_init(info->hal_ctx, &error)) { - LogMessage(X_ERROR, "config/hal: couldn't initialise context: %s (%s)\n", - error.name ? error.name : "unknown error", - error.message ? error.message : "null"); - goto out_err; - } - if (!libhal_device_property_watch_all(info->hal_ctx, &error)) { - LogMessage(X_ERROR, "config/hal: couldn't watch all properties: %s (%s)\n", - error.name ? error.name : "unknown error", - error.message ? error.message : "null"); - goto out_ctx; - } - libhal_ctx_set_device_added(info->hal_ctx, device_added); - libhal_ctx_set_device_removed(info->hal_ctx, device_removed); - - devices = libhal_find_device_by_capability(info->hal_ctx, "input", - &num_devices, &error); - /* FIXME: Get default devices if error is set. */ - if (dbus_error_is_set(&error)) { - LogMessage(X_ERROR, "config/hal: couldn't find input device: %s (%s)\n", - error.name ? error.name : "unknown error", - error.message ? error.message : "null"); - goto out_ctx; - } - for (i = 0; i < num_devices; i++) - device_added(info->hal_ctx, devices[i]); - libhal_free_string_array(devices); - - dbus_error_free(&error); - - return TRUE; - -out_ctx: - dbus_error_free(&error); - - if (!libhal_ctx_shutdown(info->hal_ctx, &error)) { - LogMessage(X_WARNING, "config/hal: couldn't shut down context: %s (%s)\n", - error.name ? error.name : "unknown error", - error.message ? error.message : "null"); - dbus_error_free(&error); - } - -out_err: - dbus_error_free(&error); - - if (info->hal_ctx) { - libhal_ctx_free(info->hal_ctx); - } - - info->hal_ctx = NULL; - info->system_bus = NULL; - - return FALSE; -} - - -/** - * Handle NewOwnerChanged signals to deal with HAL startup at X server runtime. - * - * NewOwnerChanged is send once when HAL shuts down, and once again when it - * comes back up. Message has three arguments, first is the name - * (org.freedesktop.Hal), the second one is the old owner, third one is new - * owner. - */ -static DBusHandlerResult -ownerchanged_handler(DBusConnection *connection, DBusMessage *message, void *data) -{ - int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - - if (dbus_message_is_signal(message, - "org.freedesktop.DBus", - "NameOwnerChanged")) { - DBusError error; - char *name, *old_owner, *new_owner; - - dbus_error_init(&error); - dbus_message_get_args(message, &error, - DBUS_TYPE_STRING, &name, - DBUS_TYPE_STRING, &old_owner, - DBUS_TYPE_STRING, &new_owner, - DBUS_TYPE_INVALID); - - if (dbus_error_is_set(&error)) { - ErrorF("[config/hal] failed to get NameOwnerChanged args: %s (%s)\n", - error.name, error.message); - } else if (name && strcmp(name, "org.freedesktop.Hal") == 0) { - - if (!old_owner || !strlen(old_owner)) { - DebugF("[config/hal] HAL startup detected.\n"); - if (connect_and_register(connection, (struct config_hal_info*)data)) - dbus_connection_unregister_object_path(connection, - "/org/freedesktop/DBus"); - else - ErrorF("[config/hal] Failed to connect to HAL bus.\n"); - } - - ret = DBUS_HANDLER_RESULT_HANDLED; - } - dbus_error_free(&error); - } - - return ret; -} - -/** - * Register a handler for the NameOwnerChanged signal. - */ -static BOOL -listen_for_startup(DBusConnection *connection, void *data) -{ - DBusObjectPathVTable vtable = { .message_function = ownerchanged_handler, }; - DBusError error; - const char MATCH_RULE[] = "sender='org.freedesktop.DBus'," - "interface='org.freedesktop.DBus'," - "type='signal'," - "path='/org/freedesktop/DBus'," - "member='NameOwnerChanged'"; - int rc = FALSE; - - dbus_error_init(&error); - dbus_bus_add_match(connection, MATCH_RULE, &error); - if (!dbus_error_is_set(&error)) { - if (dbus_connection_register_object_path(connection, - "/org/freedesktop/DBus", - &vtable, - data)) - rc = TRUE; - else - ErrorF("[config/hal] cannot register object path.\n"); - } else { - ErrorF("[config/hal] couldn't add match rule: %s (%s)\n", error.name, - error.message); - ErrorF("[config/hal] cannot detect a HAL startup.\n"); - } - - dbus_error_free(&error); - - return rc; -} - -static void -connect_hook(DBusConnection *connection, void *data) -{ - struct config_hal_info *info = data; - - if (listen_for_startup(connection, data) && - connect_and_register(connection, info)) - dbus_connection_unregister_object_path(connection, - "/org/freedesktop/DBus"); - - return; -} - -static struct config_hal_info hal_info; -static struct config_dbus_core_hook hook = { - .connect = connect_hook, - .disconnect = disconnect_hook, - .data = &hal_info, -}; - -int -config_hal_init(void) -{ - memset(&hal_info, 0, sizeof(hal_info)); - hal_info.system_bus = NULL; - hal_info.hal_ctx = NULL; - - if (!config_dbus_core_add_hook(&hook)) { - LogMessage(X_ERROR, "config/hal: failed to add D-Bus hook\n"); - return 0; - } - - /* verbose message */ - LogMessageVerb(X_INFO,7,"config/hal: initialized"); - - return 1; -} - -void -config_hal_fini(void) -{ - config_dbus_core_remove_hook(&hook); -} +/* + * Copyright © 2007 Daniel Stone + * Copyright © 2007 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Daniel Stone + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#include "input.h" +#include "inputstr.h" +#include "hotplug.h" +#include "config-backends.h" +#include "os.h" + + +#define LIBHAL_PROP_KEY "input.x11_options." +#define LIBHAL_XKB_PROP_KEY "input.xkb." + + +struct config_hal_info { + DBusConnection *system_bus; + LibHalContext *hal_ctx; +}; + +/* Used for special handling of xkb options. */ +struct xkb_options { + char* layout; + char* model; + char* rules; + char* variant; + char* options; +}; + +static void +device_removed(LibHalContext *ctx, const char *udi) +{ + char *value; + + value = malloc(strlen(udi) + 5); /* "hal:" + NULL */ + if (!value) + return; + sprintf(value, "hal:%s", udi); + + remove_devices("hal", value); + + free(value); +} + +static char * +get_prop_string(LibHalContext *hal_ctx, const char *udi, const char *name) +{ + char *prop, *ret; + + prop = libhal_device_get_property_string(hal_ctx, udi, name, NULL); + LogMessageVerb(X_INFO, 10, "config/hal: getting %s on %s returned %s\n", name, udi, prop ? prop : "(null)"); + if (prop) { + ret = xstrdup(prop); + libhal_free_string(prop); + } + else { + return NULL; + } + + return ret; +} + +static char * +get_prop_string_array(LibHalContext *hal_ctx, const char *udi, const char *prop) +{ + char **props, *ret, *str; + int i, len = 0; + + props = libhal_device_get_property_strlist(hal_ctx, udi, prop, NULL); + if (props) { + for (i = 0; props[i]; i++) + len += strlen(props[i]); + + ret = calloc(sizeof(char), len + i); /* i - 1 commas, 1 NULL */ + if (!ret) { + libhal_free_string_array(props); + return NULL; + } + + str = ret; + for (i = 0; props[i]; i++) { + strcpy(str, props[i]); + str += strlen(props[i]); + *str++ = ','; + } + *(str-1) = '\0'; + + libhal_free_string_array(props); + } + else { + return NULL; + } + + return ret; +} + +static void +device_added(LibHalContext *hal_ctx, const char *udi) +{ + char *path = NULL, *driver = NULL, *name = NULL, *config_info = NULL; + InputOption *options = NULL, *tmpo = NULL; + InputAttributes attrs = {0}; + DeviceIntPtr dev = NULL; + DBusError error; + struct xkb_options xkb_opts = {0}; + int rc; + + LibHalPropertySet *set = NULL; + LibHalPropertySetIterator set_iter; + char *psi_key = NULL, *tmp_val; + + + dbus_error_init(&error); + + driver = get_prop_string(hal_ctx, udi, "input.x11_driver"); + if (!driver){ + /* verbose, don't tell the user unless they _want_ to see it */ + LogMessageVerb(X_INFO,7,"config/hal: no driver specified for device %s\n", udi); + goto unwind; + } + + path = get_prop_string(hal_ctx, udi, "input.device"); + if (!path) { + LogMessage(X_WARNING,"config/hal: no driver or path specified for %s\n", udi); + goto unwind; + } + attrs.device = xstrdup(path); + + name = get_prop_string(hal_ctx, udi, "info.product"); + if (!name) + name = xstrdup("(unnamed)"); + else + attrs.product = xstrdup(name); + + attrs.vendor = get_prop_string(hal_ctx, udi, "info.vendor"); + attrs.tags = xstrtokenize(get_prop_string(hal_ctx, udi, "input.tags"), ","); + + if (libhal_device_query_capability(hal_ctx, udi, "input.keys", NULL)) + attrs.flags |= ATTR_KEYBOARD; + if (libhal_device_query_capability(hal_ctx, udi, "input.mouse", NULL)) + attrs.flags |= ATTR_POINTER; + if (libhal_device_query_capability(hal_ctx, udi, "input.joystick", NULL)) + attrs.flags |= ATTR_JOYSTICK; + if (libhal_device_query_capability(hal_ctx, udi, "input.tablet", NULL)) + attrs.flags |= ATTR_TABLET; + if (libhal_device_query_capability(hal_ctx, udi, "input.touchpad", NULL)) + attrs.flags |= ATTR_TOUCHPAD; + if (libhal_device_query_capability(hal_ctx, udi, "input.touchscreen", NULL)) + attrs.flags |= ATTR_TOUCHSCREEN; + + options = calloc(sizeof(*options), 1); + if (!options){ + LogMessage(X_ERROR, "config/hal: couldn't allocate space for input options!\n"); + goto unwind; + } + + options->key = xstrdup("_source"); + options->value = xstrdup("server/hal"); + if (!options->key || !options->value) { + LogMessage(X_ERROR, "config/hal: couldn't allocate first key/value pair\n"); + goto unwind; + } + + /* most drivers use device.. not path. evdev uses both however, but the + * path version isn't documented apparently. support both for now. */ + add_option(&options, "path", path); + add_option(&options, "device", path); + + add_option(&options, "driver", driver); + add_option(&options, "name", name); + + config_info = malloc(strlen(udi) + 5); /* "hal:" and NULL */ + if (!config_info) { + LogMessage(X_ERROR, "config/hal: couldn't allocate name\n"); + goto unwind; + } + sprintf(config_info, "hal:%s", udi); + + /* Check for duplicate devices */ + if (device_is_duplicate(config_info)) + { + LogMessage(X_WARNING, "config/hal: device %s already added. Ignoring.\n", name); + goto unwind; + } + + /* ok, grab options from hal.. iterate through all properties + * and lets see if any of them are options that we can add */ + set = libhal_device_get_all_properties(hal_ctx, udi, &error); + + if (!set) { + LogMessage(X_ERROR, "config/hal: couldn't get property list for %s: %s (%s)\n", + udi, error.name, error.message); + goto unwind; + } + + libhal_psi_init(&set_iter,set); + while (libhal_psi_has_more(&set_iter)) { + /* we are looking for supported keys.. extract and add to options */ + psi_key = libhal_psi_get_key(&set_iter); + + if (psi_key){ + + /* normal options first (input.x11_options.) */ + if (!strncasecmp(psi_key, LIBHAL_PROP_KEY, sizeof(LIBHAL_PROP_KEY)-1)){ + char* tmp; + + /* only support strings for all values */ + tmp_val = get_prop_string(hal_ctx, udi, psi_key); + + if (tmp_val){ + + /* xkb needs special handling. HAL specs include + * input.xkb.xyz options, but the x11-input.fdi specifies + * input.x11_options.Xkbxyz options. By default, we use + * the former, unless the specific X11 ones are specified. + * Since we can't predict the order in which the keys + * arrive, we need to store them. + */ + if ((tmp = strcasestr(psi_key, "xkb")) && strlen(tmp) >= 4) + { + if (!strcasecmp(&tmp[3], "layout")) + { + if (xkb_opts.layout) + free(xkb_opts.layout); + xkb_opts.layout = strdup(tmp_val); + } else if (!strcasecmp(&tmp[3], "model")) + { + if (xkb_opts.model) + free(xkb_opts.model); + xkb_opts.model = strdup(tmp_val); + } else if (!strcasecmp(&tmp[3], "rules")) + { + if (xkb_opts.rules) + free(xkb_opts.rules); + xkb_opts.rules = strdup(tmp_val); + } else if (!strcasecmp(&tmp[3], "variant")) + { + if (xkb_opts.variant) + free(xkb_opts.variant); + xkb_opts.variant = strdup(tmp_val); + } else if (!strcasecmp(&tmp[3], "options")) + { + if (xkb_opts.options) + free(xkb_opts.options); + xkb_opts.options = strdup(tmp_val); + } + } else + { + /* all others */ + add_option(&options, psi_key + sizeof(LIBHAL_PROP_KEY)-1, tmp_val); + free(tmp_val); + } + } else + { + /* server 1.4 had xkb_options as strlist. */ + if ((tmp = strcasestr(psi_key, "xkb")) && + (strlen(tmp) >= 4) && + (!strcasecmp(&tmp[3], "options")) && + (tmp_val = get_prop_string_array(hal_ctx, udi, psi_key))) + { + if (xkb_opts.options) + free(xkb_opts.options); + xkb_opts.options = strdup(tmp_val); + } + } + } else if (!strncasecmp(psi_key, LIBHAL_XKB_PROP_KEY, sizeof(LIBHAL_XKB_PROP_KEY)-1)){ + char* tmp; + + /* only support strings for all values */ + tmp_val = get_prop_string(hal_ctx, udi, psi_key); + + if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) { + + tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; + + if (!strcasecmp(tmp, "layout")) + { + if (!xkb_opts.layout) + xkb_opts.layout = strdup(tmp_val); + } else if (!strcasecmp(tmp, "rules")) + { + if (!xkb_opts.rules) + xkb_opts.rules = strdup(tmp_val); + } else if (!strcasecmp(tmp, "variant")) + { + if (!xkb_opts.variant) + xkb_opts.variant = strdup(tmp_val); + } else if (!strcasecmp(tmp, "model")) + { + if (!xkb_opts.model) + xkb_opts.model = strdup(tmp_val); + } else if (!strcasecmp(tmp, "options")) + { + if (!xkb_opts.options) + xkb_opts.options = strdup(tmp_val); + } + free(tmp_val); + } else + { + /* server 1.4 had xkb options as strlist */ + tmp_val = get_prop_string_array(hal_ctx, udi, psi_key); + if (tmp_val && strlen(psi_key) >= sizeof(LIBHAL_XKB_PROP_KEY)) + { + tmp = &psi_key[sizeof(LIBHAL_XKB_PROP_KEY) - 1]; + if (!strcasecmp(tmp, ".options") && (!xkb_opts.options)) + xkb_opts.options = strdup(tmp_val); + } + } + } + } + + /* psi_key doesn't need to be freed */ + libhal_psi_next(&set_iter); + } + + + /* Now add xkb options */ + if (xkb_opts.layout) + add_option(&options, "xkb_layout", xkb_opts.layout); + if (xkb_opts.rules) + add_option(&options, "xkb_rules", xkb_opts.rules); + if (xkb_opts.variant) + add_option(&options, "xkb_variant", xkb_opts.variant); + if (xkb_opts.model) + add_option(&options, "xkb_model", xkb_opts.model); + if (xkb_opts.options) + add_option(&options, "xkb_options", xkb_opts.options); + + /* this isn't an error, but how else do you output something that the user can see? */ + LogMessage(X_INFO, "config/hal: Adding input device %s\n", name); + if ((rc = NewInputDeviceRequest(options, &attrs, &dev)) != Success) { + LogMessage(X_ERROR, "config/hal: NewInputDeviceRequest failed (%d)\n", rc); + dev = NULL; + goto unwind; + } + + for (; dev; dev = dev->next){ + if (dev->config_info) + free(dev->config_info); + dev->config_info = xstrdup(config_info); + } + +unwind: + if (set) + libhal_free_property_set(set); + if (path) + free(path); + if (driver) + free(driver); + if (name) + free(name); + if (config_info) + free(config_info); + while (!dev && (tmpo = options)) { + options = tmpo->next; + free(tmpo->key); + free(tmpo->value); + free(tmpo); + } + + free(attrs.product); + free(attrs.vendor); + free(attrs.device); + if (attrs.tags) { + char **tag = attrs.tags; + while (*tag) { + free(*tag); + tag++; + } + free(attrs.tags); + } + + if (xkb_opts.layout) + free(xkb_opts.layout); + if (xkb_opts.rules) + free(xkb_opts.rules); + if (xkb_opts.model) + free(xkb_opts.model); + if (xkb_opts.variant) + free(xkb_opts.variant); + if (xkb_opts.options) + free(xkb_opts.options); + + dbus_error_free(&error); + + return; +} + +static void +disconnect_hook(void *data) +{ + DBusError error; + struct config_hal_info *info = data; + + if (info->hal_ctx) { + if (dbus_connection_get_is_connected(info->system_bus)) { + dbus_error_init(&error); + if (!libhal_ctx_shutdown(info->hal_ctx, &error)) + LogMessage(X_WARNING, "config/hal: disconnect_hook couldn't shut down context: %s (%s)\n", + error.name, error.message); + dbus_error_free(&error); + } + libhal_ctx_free(info->hal_ctx); + } + + info->hal_ctx = NULL; + info->system_bus = NULL; +} + +static BOOL +connect_and_register(DBusConnection *connection, struct config_hal_info *info) +{ + DBusError error; + char **devices; + int num_devices, i; + + if (info->hal_ctx) + return TRUE; /* already registered, pretend we did something */ + + info->system_bus = connection; + + dbus_error_init(&error); + + info->hal_ctx = libhal_ctx_new(); + if (!info->hal_ctx) { + LogMessage(X_ERROR, "config/hal: couldn't create HAL context\n"); + goto out_err; + } + + if (!libhal_ctx_set_dbus_connection(info->hal_ctx, info->system_bus)) { + LogMessage(X_ERROR, "config/hal: couldn't associate HAL context with bus\n"); + goto out_err; + } + if (!libhal_ctx_init(info->hal_ctx, &error)) { + LogMessage(X_ERROR, "config/hal: couldn't initialise context: %s (%s)\n", + error.name ? error.name : "unknown error", + error.message ? error.message : "null"); + goto out_err; + } + if (!libhal_device_property_watch_all(info->hal_ctx, &error)) { + LogMessage(X_ERROR, "config/hal: couldn't watch all properties: %s (%s)\n", + error.name ? error.name : "unknown error", + error.message ? error.message : "null"); + goto out_ctx; + } + libhal_ctx_set_device_added(info->hal_ctx, device_added); + libhal_ctx_set_device_removed(info->hal_ctx, device_removed); + + devices = libhal_find_device_by_capability(info->hal_ctx, "input", + &num_devices, &error); + /* FIXME: Get default devices if error is set. */ + if (dbus_error_is_set(&error)) { + LogMessage(X_ERROR, "config/hal: couldn't find input device: %s (%s)\n", + error.name ? error.name : "unknown error", + error.message ? error.message : "null"); + goto out_ctx; + } + for (i = 0; i < num_devices; i++) + device_added(info->hal_ctx, devices[i]); + libhal_free_string_array(devices); + + dbus_error_free(&error); + + return TRUE; + +out_ctx: + dbus_error_free(&error); + + if (!libhal_ctx_shutdown(info->hal_ctx, &error)) { + LogMessage(X_WARNING, "config/hal: couldn't shut down context: %s (%s)\n", + error.name ? error.name : "unknown error", + error.message ? error.message : "null"); + dbus_error_free(&error); + } + +out_err: + dbus_error_free(&error); + + if (info->hal_ctx) { + libhal_ctx_free(info->hal_ctx); + } + + info->hal_ctx = NULL; + info->system_bus = NULL; + + return FALSE; +} + + +/** + * Handle NewOwnerChanged signals to deal with HAL startup at X server runtime. + * + * NewOwnerChanged is send once when HAL shuts down, and once again when it + * comes back up. Message has three arguments, first is the name + * (org.freedesktop.Hal), the second one is the old owner, third one is new + * owner. + */ +static DBusHandlerResult +ownerchanged_handler(DBusConnection *connection, DBusMessage *message, void *data) +{ + int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + if (dbus_message_is_signal(message, + "org.freedesktop.DBus", + "NameOwnerChanged")) { + DBusError error; + char *name, *old_owner, *new_owner; + + dbus_error_init(&error); + dbus_message_get_args(message, &error, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_STRING, &old_owner, + DBUS_TYPE_STRING, &new_owner, + DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&error)) { + ErrorF("[config/hal] failed to get NameOwnerChanged args: %s (%s)\n", + error.name, error.message); + } else if (name && strcmp(name, "org.freedesktop.Hal") == 0) { + + if (!old_owner || !strlen(old_owner)) { + DebugF("[config/hal] HAL startup detected.\n"); + if (connect_and_register(connection, (struct config_hal_info*)data)) + dbus_connection_unregister_object_path(connection, + "/org/freedesktop/DBus"); + else + ErrorF("[config/hal] Failed to connect to HAL bus.\n"); + } + + ret = DBUS_HANDLER_RESULT_HANDLED; + } + dbus_error_free(&error); + } + + return ret; +} + +/** + * Register a handler for the NameOwnerChanged signal. + */ +static BOOL +listen_for_startup(DBusConnection *connection, void *data) +{ + DBusObjectPathVTable vtable = { .message_function = ownerchanged_handler, }; + DBusError error; + const char MATCH_RULE[] = "sender='org.freedesktop.DBus'," + "interface='org.freedesktop.DBus'," + "type='signal'," + "path='/org/freedesktop/DBus'," + "member='NameOwnerChanged'"; + int rc = FALSE; + + dbus_error_init(&error); + dbus_bus_add_match(connection, MATCH_RULE, &error); + if (!dbus_error_is_set(&error)) { + if (dbus_connection_register_object_path(connection, + "/org/freedesktop/DBus", + &vtable, + data)) + rc = TRUE; + else + ErrorF("[config/hal] cannot register object path.\n"); + } else { + ErrorF("[config/hal] couldn't add match rule: %s (%s)\n", error.name, + error.message); + ErrorF("[config/hal] cannot detect a HAL startup.\n"); + } + + dbus_error_free(&error); + + return rc; +} + +static void +connect_hook(DBusConnection *connection, void *data) +{ + struct config_hal_info *info = data; + + if (listen_for_startup(connection, data) && + connect_and_register(connection, info)) + dbus_connection_unregister_object_path(connection, + "/org/freedesktop/DBus"); + + return; +} + +static struct config_hal_info hal_info; +static struct config_dbus_core_hook hook = { + .connect = connect_hook, + .disconnect = disconnect_hook, + .data = &hal_info, +}; + +int +config_hal_init(void) +{ + memset(&hal_info, 0, sizeof(hal_info)); + hal_info.system_bus = NULL; + hal_info.hal_ctx = NULL; + + if (!config_dbus_core_add_hook(&hook)) { + LogMessage(X_ERROR, "config/hal: failed to add D-Bus hook\n"); + return 0; + } + + /* verbose message */ + LogMessageVerb(X_INFO,7,"config/hal: initialized"); + + return 1; +} + +void +config_hal_fini(void) +{ + config_dbus_core_remove_hook(&hook); +} diff --git a/xorg-server/config/udev.c b/xorg-server/config/udev.c index 452fb5a8d..304ee2c93 100644 --- a/xorg-server/config/udev.c +++ b/xorg-server/config/udev.c @@ -1,267 +1,267 @@ -/* - * Copyright © 2009 Julien Cristau - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice (including the next - * paragraph) shall be included in all copies or substantial portions of the - * Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Author: Julien Cristau - */ - -#ifdef HAVE_DIX_CONFIG_H -#include -#endif - -#include - -#include "input.h" -#include "inputstr.h" -#include "hotplug.h" -#include "config-backends.h" -#include "os.h" - -#define UDEV_XKB_PROP_KEY "xkb" - -static struct udev_monitor *udev_monitor; - -static void -device_added(struct udev_device *udev_device) -{ - const char *path, *name = NULL; - char *config_info = NULL; - const char *syspath; - const char *key, *value, *tmp; - InputOption *options = NULL, *tmpo; - InputAttributes attrs = {}; - DeviceIntPtr dev = NULL; - struct udev_list_entry *set, *entry; - struct udev_device *parent; - int rc; - - path = udev_device_get_devnode(udev_device); - - syspath = udev_device_get_syspath(udev_device); - - if (!path || !syspath) - return; - - if (!udev_device_get_property_value(udev_device, "ID_INPUT")) - return; - - options = xcalloc(sizeof(*options), 1); - if (!options) - return; - - options->key = xstrdup("_source"); - options->value = xstrdup("server/udev"); - if (!options->key || !options->value) - goto unwind; - - parent = udev_device_get_parent(udev_device); - if (parent) { - name = udev_device_get_sysattr_value(parent, "name"); - if (!name) - name = udev_device_get_property_value(parent, "NAME"); - } - if (!name) - name = "(unnamed)"; - else - attrs.product = name; - add_option(&options, "name", name); - - add_option(&options, "path", path); - add_option(&options, "device", path); - attrs.device = path; - attrs.tags = xstrtokenize(udev_device_get_property_value(udev_device, "ID_INPUT.tags"), ","); - - config_info = Xprintf("udev:%s", syspath); - if (!config_info) - goto unwind; - - if (device_is_duplicate(config_info)) { - LogMessage(X_WARNING, "config/udev: device %s already added. " - "Ignoring.\n", name); - goto unwind; - } - - set = udev_device_get_properties_list_entry(udev_device); - udev_list_entry_foreach(entry, set) { - key = udev_list_entry_get_name(entry); - if (!key) - continue; - value = udev_list_entry_get_value(entry); - if (!strncasecmp(key, UDEV_XKB_PROP_KEY, - sizeof(UDEV_XKB_PROP_KEY) - 1)) { - tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1; - if (!strcasecmp(tmp, "rules")) - add_option(&options, "xkb_rules", value); - else if (!strcasecmp(tmp, "layout")) - add_option(&options, "xkb_layout", value); - else if (!strcasecmp(tmp, "variant")) - add_option(&options, "xkb_variant", value); - else if (!strcasecmp(tmp, "model")) - add_option(&options, "xkb_model", value); - else if (!strcasecmp(tmp, "options")) - add_option(&options, "xkb_options", value); - } else if (!strcmp(key, "ID_VENDOR")) { - attrs.vendor = value; - } else if (!strcmp(key, "ID_INPUT_KEY")) { - attrs.flags |= ATTR_KEYBOARD; - } else if (!strcmp(key, "ID_INPUT_MOUSE")) { - attrs.flags |= ATTR_POINTER; - } else if (!strcmp(key, "ID_INPUT_JOYSTICK")) { - attrs.flags |= ATTR_JOYSTICK; - } else if (!strcmp(key, "ID_INPUT_TABLET")) { - attrs.flags |= ATTR_TABLET; - } else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) { - attrs.flags |= ATTR_TOUCHPAD; - } else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) { - attrs.flags |= ATTR_TOUCHSCREEN; - } - } - LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n", - name, path); - rc = NewInputDeviceRequest(options, &attrs, &dev); - if (rc != Success) - goto unwind; - - for (; dev; dev = dev->next) { - xfree(dev->config_info); - dev->config_info = xstrdup(config_info); - } - - unwind: - xfree(config_info); - while (!dev && (tmpo = options)) { - options = tmpo->next; - xfree(tmpo->key); - xfree(tmpo->value); - xfree(tmpo); - } - - if (attrs.tags) { - char **tag = attrs.tags; - while (*tag) { - xfree(*tag); - tag++; - } - xfree(attrs.tags); - } - - return; -} - -static void -device_removed(struct udev_device *device) -{ - char *value; - const char *syspath = udev_device_get_syspath(device); - - value = Xprintf("udev:%s", syspath); - if (!value) - return; - - remove_devices("udev", value); - - xfree(value); -} - -static void -wakeup_handler(pointer data, int err, pointer read_mask) -{ - int udev_fd = udev_monitor_get_fd(udev_monitor); - struct udev_device *udev_device; - const char *action; - - if (err < 0) - return; - - if (FD_ISSET(udev_fd, (fd_set *)read_mask)) { - udev_device = udev_monitor_receive_device(udev_monitor); - if (!udev_device) - return; - action = udev_device_get_action(udev_device); - if (action) { - if (!strcmp(action, "add")) - device_added(udev_device); - else if (!strcmp(action, "remove")) - device_removed(udev_device); - } - udev_device_unref(udev_device); - } -} - -static void -block_handler(pointer data, struct timeval **tv, pointer read_mask) -{ -} - -int -config_udev_init(void) -{ - struct udev *udev; - struct udev_enumerate *enumerate; - struct udev_list_entry *devices, *device; - - udev = udev_new(); - if (!udev) - return 0; - udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); - if (!udev_monitor) - return 0; - - if (udev_monitor_enable_receiving(udev_monitor)) { - ErrorF("config/udev: failed to bind the udev monitor\n"); - return 0; - } - - enumerate = udev_enumerate_new(udev); - if (!enumerate) - return 0; - udev_enumerate_scan_devices(enumerate); - devices = udev_enumerate_get_list_entry(enumerate); - udev_list_entry_foreach(device, devices) { - const char *syspath = udev_list_entry_get_name(device); - struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath); - device_added(udev_device); - udev_device_unref(udev_device); - } - udev_enumerate_unref(enumerate); - - RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); - AddGeneralSocket(udev_monitor_get_fd(udev_monitor)); - - return 1; -} - -void -config_udev_fini(void) -{ - struct udev *udev; - - if (!udev_monitor) - return; - - udev = udev_monitor_get_udev(udev_monitor); - - RemoveGeneralSocket(udev_monitor_get_fd(udev_monitor)); - RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, udev_monitor); - udev_monitor_unref(udev_monitor); - udev_monitor = NULL; - udev_unref(udev); -} +/* + * Copyright © 2009 Julien Cristau + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Julien Cristau + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include + +#include "input.h" +#include "inputstr.h" +#include "hotplug.h" +#include "config-backends.h" +#include "os.h" + +#define UDEV_XKB_PROP_KEY "xkb" + +static struct udev_monitor *udev_monitor; + +static void +device_added(struct udev_device *udev_device) +{ + const char *path, *name = NULL; + char *config_info = NULL; + const char *syspath; + const char *key, *value, *tmp; + InputOption *options = NULL, *tmpo; + InputAttributes attrs = {}; + DeviceIntPtr dev = NULL; + struct udev_list_entry *set, *entry; + struct udev_device *parent; + int rc; + + path = udev_device_get_devnode(udev_device); + + syspath = udev_device_get_syspath(udev_device); + + if (!path || !syspath) + return; + + if (!udev_device_get_property_value(udev_device, "ID_INPUT")) + return; + + options = calloc(sizeof(*options), 1); + if (!options) + return; + + options->key = xstrdup("_source"); + options->value = xstrdup("server/udev"); + if (!options->key || !options->value) + goto unwind; + + parent = udev_device_get_parent(udev_device); + if (parent) { + name = udev_device_get_sysattr_value(parent, "name"); + if (!name) + name = udev_device_get_property_value(parent, "NAME"); + } + if (!name) + name = "(unnamed)"; + else + attrs.product = name; + add_option(&options, "name", name); + + add_option(&options, "path", path); + add_option(&options, "device", path); + attrs.device = path; + attrs.tags = xstrtokenize(udev_device_get_property_value(udev_device, "ID_INPUT.tags"), ","); + + config_info = Xprintf("udev:%s", syspath); + if (!config_info) + goto unwind; + + if (device_is_duplicate(config_info)) { + LogMessage(X_WARNING, "config/udev: device %s already added. " + "Ignoring.\n", name); + goto unwind; + } + + set = udev_device_get_properties_list_entry(udev_device); + udev_list_entry_foreach(entry, set) { + key = udev_list_entry_get_name(entry); + if (!key) + continue; + value = udev_list_entry_get_value(entry); + if (!strncasecmp(key, UDEV_XKB_PROP_KEY, + sizeof(UDEV_XKB_PROP_KEY) - 1)) { + tmp = key + sizeof(UDEV_XKB_PROP_KEY) - 1; + if (!strcasecmp(tmp, "rules")) + add_option(&options, "xkb_rules", value); + else if (!strcasecmp(tmp, "layout")) + add_option(&options, "xkb_layout", value); + else if (!strcasecmp(tmp, "variant")) + add_option(&options, "xkb_variant", value); + else if (!strcasecmp(tmp, "model")) + add_option(&options, "xkb_model", value); + else if (!strcasecmp(tmp, "options")) + add_option(&options, "xkb_options", value); + } else if (!strcmp(key, "ID_VENDOR")) { + attrs.vendor = value; + } else if (!strcmp(key, "ID_INPUT_KEY")) { + attrs.flags |= ATTR_KEYBOARD; + } else if (!strcmp(key, "ID_INPUT_MOUSE")) { + attrs.flags |= ATTR_POINTER; + } else if (!strcmp(key, "ID_INPUT_JOYSTICK")) { + attrs.flags |= ATTR_JOYSTICK; + } else if (!strcmp(key, "ID_INPUT_TABLET")) { + attrs.flags |= ATTR_TABLET; + } else if (!strcmp(key, "ID_INPUT_TOUCHPAD")) { + attrs.flags |= ATTR_TOUCHPAD; + } else if (!strcmp(key, "ID_INPUT_TOUCHSCREEN")) { + attrs.flags |= ATTR_TOUCHSCREEN; + } + } + LogMessage(X_INFO, "config/udev: Adding input device %s (%s)\n", + name, path); + rc = NewInputDeviceRequest(options, &attrs, &dev); + if (rc != Success) + goto unwind; + + for (; dev; dev = dev->next) { + free(dev->config_info); + dev->config_info = xstrdup(config_info); + } + + unwind: + free(config_info); + while (!dev && (tmpo = options)) { + options = tmpo->next; + free(tmpo->key); + free(tmpo->value); + free(tmpo); + } + + if (attrs.tags) { + char **tag = attrs.tags; + while (*tag) { + free(*tag); + tag++; + } + free(attrs.tags); + } + + return; +} + +static void +device_removed(struct udev_device *device) +{ + char *value; + const char *syspath = udev_device_get_syspath(device); + + value = Xprintf("udev:%s", syspath); + if (!value) + return; + + remove_devices("udev", value); + + free(value); +} + +static void +wakeup_handler(pointer data, int err, pointer read_mask) +{ + int udev_fd = udev_monitor_get_fd(udev_monitor); + struct udev_device *udev_device; + const char *action; + + if (err < 0) + return; + + if (FD_ISSET(udev_fd, (fd_set *)read_mask)) { + udev_device = udev_monitor_receive_device(udev_monitor); + if (!udev_device) + return; + action = udev_device_get_action(udev_device); + if (action) { + if (!strcmp(action, "add")) + device_added(udev_device); + else if (!strcmp(action, "remove")) + device_removed(udev_device); + } + udev_device_unref(udev_device); + } +} + +static void +block_handler(pointer data, struct timeval **tv, pointer read_mask) +{ +} + +int +config_udev_init(void) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *device; + + udev = udev_new(); + if (!udev) + return 0; + udev_monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (!udev_monitor) + return 0; + + if (udev_monitor_enable_receiving(udev_monitor)) { + ErrorF("config/udev: failed to bind the udev monitor\n"); + return 0; + } + + enumerate = udev_enumerate_new(udev); + if (!enumerate) + return 0; + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + udev_list_entry_foreach(device, devices) { + const char *syspath = udev_list_entry_get_name(device); + struct udev_device *udev_device = udev_device_new_from_syspath(udev, syspath); + device_added(udev_device); + udev_device_unref(udev_device); + } + udev_enumerate_unref(enumerate); + + RegisterBlockAndWakeupHandlers(block_handler, wakeup_handler, NULL); + AddGeneralSocket(udev_monitor_get_fd(udev_monitor)); + + return 1; +} + +void +config_udev_fini(void) +{ + struct udev *udev; + + if (!udev_monitor) + return; + + udev = udev_monitor_get_udev(udev_monitor); + + RemoveGeneralSocket(udev_monitor_get_fd(udev_monitor)); + RemoveBlockAndWakeupHandlers(block_handler, wakeup_handler, udev_monitor); + udev_monitor_unref(udev_monitor); + udev_monitor = NULL; + udev_unref(udev); +} -- cgit v1.2.3