[PATCH v3 libevdev] Enable event logging on request

Peter Hutterer peter.hutterer at who-t.net
Sun Apr 27 16:10:12 PDT 2014


Check an environment variable in the form for
  LIBEVDEV_LOG_EVENTS="error"
or
  LIBEVDEV_LOG_EVENTS="info:event0,event3"

If set, log all or the selected devices to the normal event logger, with the
given priority.

Key codes in the normal alphanumeric range are obfuscated to avoid accidental
leakage of information, higher-order keys are sent as normal.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
Changes to v2:
- use the dev_t minor to hardcode the event node, simplifies a few other
bits
- change from "OBFUSCATED" to "<hidden>"
- add test to make sure keys are hidden
- rename enable_event_logging() to setup_event_logging()
- expand documentation

 libevdev/libevdev-int.h     |   5 +
 libevdev/libevdev.c         | 141 +++++++++++++++++
 libevdev/libevdev.h         |  34 +++++
 test/test-libevdev-events.c | 363 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 543 insertions(+)

diff --git a/libevdev/libevdev-int.h b/libevdev/libevdev-int.h
index 98c75ce..5a164bd 100644
--- a/libevdev/libevdev-int.h
+++ b/libevdev/libevdev-int.h
@@ -107,6 +107,11 @@ struct libevdev {
 		unsigned long *tracking_id_changes;
 		size_t tracking_id_changes_sz;	 /* in bytes */
 	} mt_sync;
+
+	struct {
+		char *device_node;
+		enum libevdev_log_priority priority;
+	} debug;
 };
 
 struct logdata {
diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
index a316831..d8c9d61 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -21,13 +21,16 @@
  */
 
 #include <config.h>
+#include <dirent.h>
 #include <errno.h>
+#include <limits.h>
 #include <poll.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <stdarg.h>
 #include <stdbool.h>
+#include <sys/stat.h>
 
 #include "libevdev.h"
 #include "libevdev-int.h"
@@ -44,6 +47,32 @@ enum event_filter_status {
 
 static int sync_mt_state(struct libevdev *dev, int create_events);
 
+static inline void
+log_event(struct libevdev *dev, int flags, const struct input_event *ev)
+{
+	const char *mode;
+	const char *type, *code;
+
+	if (dev->debug.priority == 0)
+		return;
+
+	if (flags & LIBEVDEV_READ_FLAG_SYNC)
+		mode = "S"; /* sync mode */
+	else if (flags & LIBEVDEV_READ_FLAG_NORMAL)
+		mode = "N"; /* normal mode */
+	else
+		mode = "I"; /* internal sync */
+
+	type = libevdev_event_type_get_name(ev->type);
+	if (ev->type == EV_KEY && ev->code < KEY_STOP)
+		code = "<hidden>";
+	else
+		code = libevdev_event_code_get_name(ev->type, ev->code);
+
+	log_msg_cond(dev->debug.priority, "%s: EVENT(%s): %s %s %d\n",
+		     dev->debug.device_node, mode, type, code, ev->value);
+}
+
 static inline int*
 slot_value(const struct libevdev *dev, int slot, int axis)
 {
@@ -161,6 +190,7 @@ libevdev_reset(struct libevdev *dev)
 	free(dev->mt_sync.mt_state);
 	free(dev->mt_sync.tracking_id_changes);
 	free(dev->mt_sync.slot_update);
+	free(dev->debug.device_node);
 	memset(dev, 0, sizeof(*dev));
 	dev->fd = -1;
 	dev->initialized = false;
@@ -235,6 +265,106 @@ libevdev_get_log_priority(void)
 	return log_data.priority;
 }
 
+static char*
+get_event_node(struct libevdev *dev)
+{
+	const int EVDEV_MINOR_BASE = 64;
+	char *node;
+	struct stat st;
+	int minor;
+
+	if (fstat(dev->fd, &st) == -1)
+		return NULL;
+
+	minor = minor(st.st_rdev) - EVDEV_MINOR_BASE;
+
+	if (asprintf(&node, "event%d", minor) < 6)
+		return NULL;
+
+	return node;
+}
+
+/* Enable event logging for this device if LIBEVDEV_LOG_EVENTS is set */
+static void
+setup_event_logging(struct libevdev *dev)
+{
+	enum libevdev_log_priority pri = 0;
+	const char *env;
+	char *tmp = NULL,
+	     *tok = NULL, *saveptr;
+	char *node;
+
+	env = getenv("LIBEVDEV_LOG_EVENTS");
+	if (!env || strlen(env) == 0)
+		return;
+
+	if (dev->debug.device_node) {
+		free(dev->debug.device_node);
+		dev->debug.device_node = NULL;
+	}
+
+	node = get_event_node(dev);
+	if (!node) {
+		log_error("Failed to get device node, debugging disabled\n");
+		return;
+	}
+
+	if (strncmp(env, "error", 5) == 0) {
+		pri = LIBEVDEV_LOG_ERROR;
+		tok = (char*)&env[5];
+	} else if (strncmp(env, "info", 4) == 0) {
+		pri = LIBEVDEV_LOG_INFO;
+		tok = (char*)&env[4];
+	} else if (strncmp(env, "debug", 5) == 0) {
+		pri = LIBEVDEV_LOG_DEBUG;
+		tok = (char*)&env[5];
+	}
+
+	if (tok && *tok == '\0') {
+		dev->debug.device_node = node;
+		dev->debug.priority = pri;
+		return;
+	}
+
+	if (!tok || *tok != ':') {
+		log_error("Invalid value '%s' for $LIBEVDEV_LOG_EVENTS, "
+			  "debugging disabled\n", env);
+		free(node);
+		return;
+	}
+
+	tmp = strdup(tok + 1);
+	if (!tmp) {
+		log_error("Allocation failure, debugging disabled\n");
+		free(node);
+		return;
+	}
+
+	tok = strtok_r(tmp, ",", &saveptr);
+
+	while (tok) {
+		if (strncmp(tok, "event", 5) != 0) {
+			log_error("Invalid value '%s' for $LIBEVDEV_LOG_EVENTS\n", env);
+			break;
+		}
+
+		if (strcmp(tok, node) == 0) {
+			dev->debug.device_node = node;
+			dev->debug.priority = pri;
+			break;
+		}
+
+		tok = strtok_r(NULL, ",", &saveptr);
+	}
+
+	free(tmp);
+
+	if (!dev->debug.device_node)
+		free(node);
+
+	return;
+}
+
 LIBEVDEV_EXPORT int
 libevdev_change_fd(struct libevdev *dev, int fd)
 {
@@ -242,7 +372,11 @@ libevdev_change_fd(struct libevdev *dev, int fd)
 		log_bug("device not initialized. call libevdev_set_fd() first\n");
 		return -1;
 	}
+
 	dev->fd = fd;
+
+	setup_event_logging(dev);
+
 	return 0;
 }
 
@@ -390,6 +524,8 @@ libevdev_set_fd(struct libevdev* dev, int fd)
 
 	dev->fd = fd;
 
+	setup_event_logging(dev);
+
 	/* devices with ABS_MT_SLOT - 1 aren't MT devices,
 	   see the documentation for multitouch-related
 	   functions for more details */
@@ -975,6 +1111,9 @@ libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event
 		   of the device too */
 		while (queue_shift(dev, &e) == 0) {
 			dev->queue_nsync--;
+
+			log_event(dev, 0, ev);
+
 			if (sanitize_event(dev, &e, dev->sync_state) != EVENT_FILTER_DISCARD)
 				update_state(dev, &e);
 		}
@@ -1006,6 +1145,8 @@ libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event
 		if (queue_shift(dev, ev) != 0)
 			return -EAGAIN;
 
+		log_event(dev, flags, ev);
+
 		filter_status = sanitize_event(dev, ev, dev->sync_state);
 		if (filter_status != EVENT_FILTER_DISCARD)
 			update_state(dev, ev);
diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
index ef142ea..66b9d06 100644
--- a/libevdev/libevdev.h
+++ b/libevdev/libevdev.h
@@ -152,6 +152,40 @@ extern "C" {
  * libevdev is licensed under the
  * [X11 license](http://cgit.freedesktop.org/libevdev/tree/COPYING).
  *
+ * Debugging events
+ * ================
+ * libevdev checks the LIBEVDEV_LOG_EVENTS environment variable during
+ * libevdev_set_fd() and libevdev_change_fd(). If set to "error", "info", or
+ * "debug", all events are logged with the respective priority before they
+ * are passed to the client. This is useful to check event sequences that
+ * happen before a specific bug in the higher layers of the stack.
+ *
+ * To debug specific devices only, the event node may be provided as a
+ * comma-separated list after the priority:
+ * LIBEVDEV_LOG_EVENTS="error:event0,event5,event3" logs all events from the
+ * devices with the given /dev/input/event nodes. This requires that the
+ * libevdev process is able to iterate and stat each file in the /dev/input
+ * directory. An invalid value of the variable or unsuccessful attempt to
+ * initialize logging for a device will be logged as error. Debugging is
+ * disabled (for this device, if appropriate) and libevdev continues
+ * normally.
+ *
+ * Events are logged through libevdev's logging handler. See
+ * See libevdev_set_log_function() for more information.
+ * The log format for the events is
+ *
+ *     device-node: EVENT(mode): type code value
+ *
+ * where "mode" is:
+ * * N for events read during LIBEVDEV_READ_FLAG_NORMAL
+ * * S for events read during LIBEVDEV_READ_FLAG_SYNC
+ * * I for events read in an internal sync, if the client skips the sync
+ *   process. See libevdev_next_event() for more information.
+ *
+ * Events logged by libevdev are likely to end up in public bug reports.
+ * To protect accidental leakage of private information. Key codes between
+ * KEY_ESC up to including KEY_COMPOSE are always logged as "<hidden>".
+ *
  * Reporting bugs
  * ==============
  * Please report bugs in the freedesktop.org bugzilla under the libevdev product:
diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
index 6ef0bf9..4f21af3 100644
--- a/test/test-libevdev-events.c
+++ b/test/test-libevdev-events.c
@@ -25,7 +25,9 @@
 #include <errno.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <stdarg.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <libevdev/libevdev-util.h>
 
 #include "test-common.h"
@@ -2059,6 +2061,360 @@ START_TEST(test_event_mt_value_setters_current_slot)
 }
 END_TEST
 
+struct debug_log_data {
+	enum libevdev_log_priority priority;
+	char *expect_token;
+	char *dont_expect_token;
+	int logged;
+};
+
+void debug_log_func(enum libevdev_log_priority priority,
+		    void *data,
+		    const char *file, int line,
+		    const char *func,
+		    const char *format, va_list args)
+{
+	struct debug_log_data *debug_data = data;
+	char msg[1024];
+
+	ck_assert_int_eq(debug_data->priority, priority);
+
+	vsnprintf(msg, sizeof(msg), format, args);
+
+	if (debug_data->expect_token)
+		ck_assert(strstr(msg, debug_data->expect_token) != NULL);
+
+	if (debug_data->dont_expect_token)
+		ck_assert(strstr(msg, debug_data->dont_expect_token) == NULL);
+
+	debug_data->logged++;
+}
+
+START_TEST(test_event_log)
+{
+	struct uinput_device* uidev;
+	struct libevdev *dev;
+	const char *oldenv;
+	struct input_event ev;
+
+	struct debug_log_data debug_log_data;
+
+	oldenv = getenv("LIBEVDEV_LOG_EVENTS");
+	setenv("LIBEVDEV_LOG_EVENTS", "error", 1);
+
+	debug_log_data.priority = LIBEVDEV_LOG_ERROR;
+	debug_log_data.dont_expect_token = NULL;
+	debug_log_data.logged = 0;
+	libevdev_set_log_function(debug_log_func, &debug_log_data);
+
+	test_create_device(&uidev, &dev,
+			   EV_REL, REL_X,
+			   EV_REL, REL_Y,
+			   EV_KEY, BTN_LEFT,
+			   EV_KEY, KEY_A,
+			   EV_KEY, KEY_STOP,
+			   -1);
+
+	uinput_device_event(uidev, EV_REL, REL_X, 1);
+	uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1);
+	uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+	debug_log_data.expect_token = "EV_REL";
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+	debug_log_data.expect_token = "EV_KEY";
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+	debug_log_data.expect_token = "EV_SYN";
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+	uinput_device_event(uidev, EV_KEY, KEY_A, 1);
+	uinput_device_event(uidev, EV_KEY, KEY_STOP, 1);
+	uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+	debug_log_data.expect_token = "<hidden>";
+	debug_log_data.dont_expect_token = "KEY_A";
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+	debug_log_data.expect_token = "KEY_STOP";
+	debug_log_data.dont_expect_token = "<hidden>";
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+	debug_log_data.expect_token = "EV_SYN";
+	debug_log_data.dont_expect_token = NULL;
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+	ck_assert_int_eq(debug_log_data.logged, 6);
+
+	libevdev_set_log_function(test_logfunc_abort_on_error, NULL);
+
+	uinput_device_free(uidev);
+	libevdev_free(dev);
+
+	if (oldenv)
+		setenv("LIBEVDEV_LOG_EVENTS", oldenv, 1);
+	else
+		unsetenv("LIBEVDEV_LOG_EVENTS");
+}
+END_TEST
+
+START_TEST(test_event_log_hidden_keys)
+{
+	struct uinput_device* uidev;
+	struct libevdev *dev;
+	const char *oldenv;
+	struct input_event ev;
+	int rc, i, fd;
+	struct debug_log_data debug_log_data;
+
+	oldenv = getenv("LIBEVDEV_LOG_EVENTS");
+	setenv("LIBEVDEV_LOG_EVENTS", "error", 1);
+
+	debug_log_data.priority = LIBEVDEV_LOG_ERROR;
+	debug_log_data.dont_expect_token = NULL;
+	debug_log_data.logged = 0;
+	libevdev_set_log_function(debug_log_func, &debug_log_data);
+
+	uidev = uinput_device_new("libevdev test device");
+	ck_assert(uidev != NULL);
+
+	for (i = 0; i <= KEY_MAX; i++) {
+		rc = uinput_device_set_event_bit(uidev, EV_KEY, i);
+		ck_assert_int_eq(rc, 0);
+	}
+
+	rc = uinput_device_create(uidev);
+	ck_assert_int_eq(rc, 0);
+
+	fd = uinput_device_get_fd(uidev);
+
+	rc = libevdev_new_from_fd(fd, &dev);
+	ck_assert_msg(rc == 0, "Failed to init device device: %s", strerror(-rc));
+	rc = fcntl(fd, F_SETFL, O_NONBLOCK);
+	ck_assert_msg(rc == 0, "fcntl failed: %s", strerror(errno));
+
+	for (i = 1; i <= KEY_MAX; i++) {
+		/* press */
+		uinput_device_event(uidev, EV_KEY, i, 1);
+		uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+		if (i <= KEY_COMPOSE) {
+			debug_log_data.expect_token = "<hidden>";
+			debug_log_data.dont_expect_token = "KEY_";
+		} else {
+			debug_log_data.expect_token =
+				(char*)libevdev_event_code_get_name(EV_KEY, i);
+			debug_log_data.dont_expect_token = "<hidden>";
+		}
+		libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+		debug_log_data.expect_token = "EV_SYN";
+		libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+		ck_assert_int_eq(debug_log_data.logged, (i - 1) * 4 + 2);
+
+		/* release */
+		uinput_device_event(uidev, EV_KEY, i, 0);
+		uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+		if (i <= KEY_COMPOSE) {
+			debug_log_data.expect_token = "<hidden>";
+			debug_log_data.dont_expect_token = "KEY_";
+		} else {
+			debug_log_data.expect_token =
+				(char*)libevdev_event_code_get_name(EV_KEY, i);
+			debug_log_data.dont_expect_token = "<hidden>";
+		}
+		libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+		debug_log_data.expect_token = "EV_SYN";
+		libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+		ck_assert_int_eq(debug_log_data.logged, i * 4);
+	}
+
+	libevdev_set_log_function(test_logfunc_abort_on_error, NULL);
+
+	uinput_device_free(uidev);
+	libevdev_free(dev);
+
+	if (oldenv)
+		setenv("LIBEVDEV_LOG_EVENTS", oldenv, 1);
+	else
+		unsetenv("LIBEVDEV_LOG_EVENTS");
+}
+END_TEST
+
+START_TEST(test_event_log_per_device)
+{
+	struct uinput_device *uidev1, *uidev2;
+	struct libevdev *dev1, *dev2;
+	const char *oldenv;
+	char env[1024];
+	struct input_event ev;
+	int rc;
+	int fd;
+	const char *node1, *node2;
+
+	struct debug_log_data debug_log_data;
+
+	oldenv = getenv("LIBEVDEV_LOG_EVENTS");
+
+	rc = uinput_device_new_with_events(&uidev1,
+					   TEST_DEVICE_NAME, DEFAULT_IDS,
+					   EV_SYN, SYN_REPORT,
+					   EV_REL, REL_X,
+					   EV_REL, REL_Y,
+					   EV_REL, REL_WHEEL,
+					   EV_KEY, BTN_LEFT,
+					   EV_KEY, BTN_MIDDLE,
+					   EV_KEY, BTN_RIGHT,
+					   -1);
+	ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc));
+	rc = uinput_device_new_with_events(&uidev2,
+					   TEST_DEVICE_NAME " 2", DEFAULT_IDS,
+					   EV_SYN, SYN_REPORT,
+					   EV_REL, REL_X,
+					   EV_REL, REL_Y,
+					   EV_REL, REL_WHEEL,
+					   EV_KEY, BTN_LEFT,
+					   EV_KEY, BTN_MIDDLE,
+					   EV_KEY, BTN_RIGHT,
+					   -1);
+	ck_assert_msg(rc == 0, "Failed to create uinput device: %s", strerror(-rc));
+
+	node1 = strrchr(uinput_device_get_devnode(uidev1), '/') + 1;
+	node2 = strrchr(uinput_device_get_devnode(uidev2), '/') + 1;
+
+	sprintf(env, "error:%s", node2);
+	setenv("LIBEVDEV_LOG_EVENTS", env, 1);
+
+	fd = uinput_device_get_fd(uidev1);
+	fcntl(fd, F_SETFL, O_NONBLOCK);
+	rc = libevdev_new_from_fd(fd, &dev1);
+	ck_assert_int_eq(rc, 0);
+
+	fd = uinput_device_get_fd(uidev2);
+	fcntl(fd, F_SETFL, O_NONBLOCK);
+	rc = libevdev_new_from_fd(fd, &dev2);
+	ck_assert_int_eq(rc, 0);
+
+	/* check with the trailing : otherwise we may find event1 in
+	 * event13, etc. */
+	debug_log_data.dont_expect_token = alloca(strlen(node1) + 2);
+	ck_assert(debug_log_data.dont_expect_token != NULL);
+	sprintf(debug_log_data.dont_expect_token, "%s:", node1);
+
+	debug_log_data.expect_token = alloca(strlen(node2) + 2);
+	ck_assert(debug_log_data.dont_expect_token != NULL);
+	sprintf(debug_log_data.expect_token, "%s:", node2);
+
+	debug_log_data.priority = LIBEVDEV_LOG_ERROR;
+	debug_log_data.logged = 0;
+	libevdev_set_log_function(debug_log_func, &debug_log_data);
+
+	uinput_device_event(uidev1, EV_REL, REL_X, 1);
+	uinput_device_event(uidev1, EV_KEY, BTN_LEFT, 1);
+	uinput_device_event(uidev1, EV_SYN, SYN_REPORT, 0);
+
+	uinput_device_event(uidev2, EV_REL, REL_X, 1);
+	uinput_device_event(uidev2, EV_KEY, BTN_LEFT, 1);
+	uinput_device_event(uidev2, EV_SYN, SYN_REPORT, 0);
+
+	while (libevdev_next_event(dev1, LIBEVDEV_READ_FLAG_NORMAL, &ev) != -EAGAIN &&
+	       libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev) != -EAGAIN)
+		;
+
+	ck_assert_int_eq(debug_log_data.logged, 3);
+
+	libevdev_set_log_function(test_logfunc_abort_on_error, NULL);
+
+	uinput_device_free(uidev1);
+	uinput_device_free(uidev2);
+	libevdev_free(dev1);
+	libevdev_free(dev2);
+
+	if (oldenv)
+		setenv("LIBEVDEV_LOG_EVENTS", oldenv, 1);
+	else
+		unsetenv("LIBEVDEV_LOG_EVENTS");
+}
+END_TEST
+
+void event_debug_log_error_func(enum libevdev_log_priority priority,
+				void *data,
+				const char *file, int line,
+				const char *func,
+				const char *format, va_list args)
+{
+	int *called = data;
+	(*called)++;
+}
+
+static void
+test_log_env_setting(const char *env, int is_error)
+{
+	struct uinput_device* uidev;
+	struct libevdev *dev;
+	struct input_event ev;
+	int error_func_called = 0;
+
+	setenv("LIBEVDEV_LOG_EVENTS", env, 1);
+
+	libevdev_set_log_function(event_debug_log_error_func, &error_func_called);
+
+	test_create_device(&uidev, &dev,
+			   EV_REL, REL_X,
+			   EV_REL, REL_Y,
+			   EV_KEY, BTN_LEFT,
+			   EV_KEY, KEY_A,
+			   EV_KEY, KEY_STOP,
+			   -1);
+
+	uinput_device_event(uidev, EV_REL, REL_X, 1);
+	uinput_device_event(uidev, EV_KEY, BTN_LEFT, 1);
+	uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+	libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+
+	ck_assert_int_eq(error_func_called, is_error);
+
+	libevdev_set_log_function(test_logfunc_abort_on_error, NULL);
+
+	uinput_device_free(uidev);
+	libevdev_free(dev);
+}
+
+START_TEST(test_event_log_invalid_setting)
+{
+	const char *oldenv = getenv("LIBEVDEV_LOG_EVENTS");
+	enum libevdev_log_priority old = libevdev_get_log_priority();
+
+	libevdev_set_log_priority(LIBEVDEV_LOG_ERROR);
+
+	test_log_env_setting("", 0);
+	test_log_env_setting("foo", 1);
+	test_log_env_setting("debug:foo", 1);
+	test_log_env_setting("debug:event0", 0);
+	test_log_env_setting("debug:eventbanana", 0);
+	test_log_env_setting("debug:event0,event1", 0);
+	test_log_env_setting("debug:event0,", 0);
+	test_log_env_setting("debug:,event0,", 0);
+	test_log_env_setting("debug:/event0,", 1);
+	test_log_env_setting("debug:,,", 0);
+	test_log_env_setting("debug,", 1);
+
+	if (oldenv)
+		setenv("LIBEVDEV_LOG_EVENTS", oldenv, 1);
+	else
+		unsetenv("LIBEVDEV_LOG_EVENTS");
+
+	libevdev_set_log_priority(old);
+}
+END_TEST
+
 Suite *
 libevdev_events(void)
 {
@@ -2110,6 +2466,13 @@ libevdev_events(void)
 	tcase_add_test(tc, test_event_mt_value_setters_current_slot);
 	suite_add_tcase(s, tc);
 
+	tc = tcase_create("event debug logging");
+	tcase_add_test(tc, test_event_log);
+	tcase_add_test(tc, test_event_log_hidden_keys);
+	tcase_add_test(tc, test_event_log_per_device);
+	tcase_add_test(tc, test_event_log_invalid_setting);
+	suite_add_tcase(s, tc);
+
 	return s;
 }
 
-- 
1.9.0



More information about the Input-tools mailing list