aboutsummaryrefslogtreecommitdiff
path: root/xorg-server/config
diff options
context:
space:
mode:
Diffstat (limited to 'xorg-server/config')
-rw-r--r--xorg-server/config/config.c278
-rw-r--r--xorg-server/config/dbus.c884
-rw-r--r--xorg-server/config/hal.c1294
-rw-r--r--xorg-server/config/udev.c534
4 files changed, 1495 insertions, 1495 deletions
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 <daniel@fooishbar.org>
- */
-
-#ifdef HAVE_DIX_CONFIG_H
-#include <dix-config.h>
-#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 <daniel@fooishbar.org>
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#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 <daniel@fooishbar.org>
- */
-
-#ifdef HAVE_DIX_CONFIG_H
-#include <dix-config.h>
-#endif
-
-#define DBUS_API_SUBJECT_TO_CHANGE
-#include <dbus/dbus.h>
-#include <string.h>
-
-#include <X11/X.h>
-
-#include "config-backends.h"
-#include "opaque.h" /* for 'display': there should be a better way. */
-#include "input.h"
-#include "inputstr.h"
-
-#define API_VERSION 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 <daniel@fooishbar.org>
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <string.h>
+
+#include <X11/X.h>
+
+#include "config-backends.h"
+#include "opaque.h" /* for 'display': there should be a better way. */
+#include "input.h"
+#include "inputstr.h"
+
+#define API_VERSION 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 <daniel@fooishbar.org>
- */
-
-#ifdef HAVE_DIX_CONFIG_H
-#include <dix-config.h>
-#endif
-
-#include <dbus/dbus.h>
-#include <hal/libhal.h>
-#include <string.h>
-#include <sys/select.h>
-
-#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.<propname>) */
- 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 <daniel@fooishbar.org>
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <dbus/dbus.h>
+#include <hal/libhal.h>
+#include <string.h>
+#include <sys/select.h>
+
+#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.<propname>) */
+ 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 <jcristau@debian.org>
- */
-
-#ifdef HAVE_DIX_CONFIG_H
-#include <dix-config.h>
-#endif
-
-#include <libudev.h>
-
-#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 <jcristau@debian.org>
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <libudev.h>
+
+#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);
+}