[PATCH libevdev 2/2] Add support for EVIOCREVOKE

Peter Hutterer peter.hutterer at who-t.net
Tue Jan 7 17:28:37 PST 2014


New in 3.12, EVIOCREVOKE revokes access to an evdev device.
The process calling revoke is unlikely to be the same process as the one
handling the device, so provide a convenience call to just wrap the
ioctl instead of requiring a full libevdev device.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
In two minds about this one: I suspect that the only users of EVIOCREVOKE
are low-level tools that probably don't use libevdev anyway, so even the
libevdev_revoke_fd() call won't make them drag in libevdev as dependency.

So I'm mostly sending this to the list for the archivse, we probably
shouldn't merge this until we have a real need for it.

 libevdev/libevdev.c       |  24 ++++++++++
 libevdev/libevdev.h       |  37 ++++++++++++++
 test/test-libevdev-init.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 181 insertions(+)

diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
index 8a37204..a99332c 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -1446,3 +1446,27 @@ libevdev_set_clock_id(struct libevdev *dev, int clockid)
 
 	return ioctl(dev->fd, EVIOCSCLOCKID, &clockid) ? -errno : 0;
 }
+
+LIBEVDEV_EXPORT int
+libevdev_revoke(struct libevdev *dev)
+{
+	if (!dev->initialized) {
+		log_bug("device not initialized. call libevdev_set_fd() first\n");
+		return -EBADF;
+	} else if (dev->fd < 0)
+		return -EBADF;
+
+	return libevdev_revoke_fd(dev->fd);
+}
+
+LIBEVDEV_EXPORT int
+libevdev_revoke_fd(int fd)
+{
+	int rc;
+
+	if (fd < 0)
+		return -EBADF;
+
+	rc = ioctl(fd, EVIOCREVOKE, NULL);
+	return rc ? -errno : 0;
+}
diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
index f5ceda0..08ab8e6 100644
--- a/libevdev/libevdev.h
+++ b/libevdev/libevdev.h
@@ -310,6 +310,8 @@ extern "C" {
  * <dd>supported, see libevdev_grab()</dd>
  * <dt>EVIOCSCLOCKID:</dt>
  * <dd>supported, see libevdev_set_clock_id()</dd>
+ * <dt>EVIOCREVOKE:</dt>
+ * <dd>supported, see libevdev_revoke(), libevdev_revoke_fd()</dd>
  * </dl>
  *
  */
@@ -1635,6 +1637,41 @@ int libevdev_event_code_from_name_n(unsigned int type, const char *name,
  */
 int libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period);
 
+/**
+ * @ingroup kernel
+ *
+ * Revoke access to the file descriptor by calling EVIOCREVOKE on it.
+ * Once issued, this file descriptor becomes mute and will not send
+ * any more events. Reading or writing to the fd will fail.
+ *
+ * @param dev The evdev device, already initialized with libevdev_set_fd()
+ * @return 0 on success or a negative errno on failure
+ * @retval -EINVAL The kernel does not support the EVIOCREVOKE ioctl.
+ *
+ * @see libevdev_revoke_fd
+ */
+int libevdev_revoke(struct libevdev *dev);
+
+/**
+ * @ingroup kernel
+ *
+ * Revoke access to the file descriptor by calling EVIOCREVOKE on it.
+ * Once issued, this file descriptor becomes mute and will not send
+ * any more events. Reading or writing to the fd will fail.
+ *
+ * This call is a convenience call for clients that do not want to handle
+ * ioctls manually but do not need or want to open a full libevdev device.
+ *
+ * If you have a libevdev device anyway, use libevdev_revoke() instead.
+ *
+ * @param fd A file descriptor to an evdev device
+ * @return 0 on success or a negative errno on failure
+ * @retval -EINVAL The kernel does not support the EVIOCREVOKE ioctl.
+ *
+ * @see libevdev_revoke
+ */
+int libevdev_revoke_fd(int fd);
+
 
 /********* DEPRECATED SECTION *********/
 #if defined(__GNUC__) && __GNUC__ >= 4
diff --git a/test/test-libevdev-init.c b/test/test-libevdev-init.c
index bf0b874..eb4916f 100644
--- a/test/test-libevdev-init.c
+++ b/test/test-libevdev-init.c
@@ -28,6 +28,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <stdio.h>
 
 #include <libevdev/libevdev-uinput.h>
 #include "test-common.h"
@@ -366,6 +367,120 @@ START_TEST(test_clock_id_events)
 END_TEST
 
 
+START_TEST(test_revoke)
+{
+	struct uinput_device* uidev;
+	struct libevdev *dev, *dev2;
+	int rc, fd;
+	struct input_event ev1, ev2;
+
+	rc = test_create_device(&uidev, &dev,
+				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 device: %s", strerror(-rc));
+
+	fd = open(uinput_device_get_devnode(uidev), O_RDONLY|O_NONBLOCK);
+	ck_assert_int_gt(fd, -1);
+	rc = libevdev_new_from_fd(fd, &dev2);
+	ck_assert_msg(rc == 0, "Failed to create second device: %s", strerror(-rc));
+
+	uinput_device_event(uidev, EV_REL, REL_X, 1);
+	uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+	rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev1);
+	ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+
+	rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev2);
+	ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+
+	ck_assert_int_eq(ev1.type, ev2.type);
+	ck_assert_int_eq(ev1.code, ev2.code);
+	ck_assert_int_eq(ev1.value, ev2.value);
+
+	/* revoke first device, expect it closed, second device still open */
+	rc = libevdev_revoke(dev);
+	if (rc == -EINVAL) {
+		fprintf(stderr, "WARNING: skipping libevdev_revoke test, not suported by current kernel\n");
+		goto out;
+	}
+
+	rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev1);
+	ck_assert_int_eq(rc, -ENODEV);
+
+	rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev2);
+	ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+
+out:
+	uinput_device_free(uidev);
+	libevdev_free(dev);
+	libevdev_free(dev2);
+	close(fd);
+}
+END_TEST
+
+START_TEST(test_revoke_fd)
+{
+	struct uinput_device* uidev;
+	struct libevdev *dev, *dev2;
+	int rc, fd;
+	struct input_event ev1, ev2;
+
+	rc = test_create_device(&uidev, &dev,
+				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 device: %s", strerror(-rc));
+
+	fd = open(uinput_device_get_devnode(uidev), O_RDONLY|O_NONBLOCK);
+	ck_assert_int_gt(fd, -1);
+	rc = libevdev_new_from_fd(fd, &dev2);
+	ck_assert_msg(rc == 0, "Failed to create second device: %s", strerror(-rc));
+
+	uinput_device_event(uidev, EV_REL, REL_X, 1);
+	uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+	rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev1);
+	ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+
+	rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev2);
+	ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+
+	ck_assert_int_eq(ev1.type, ev2.type);
+	ck_assert_int_eq(ev1.code, ev2.code);
+	ck_assert_int_eq(ev1.value, ev2.value);
+
+	/* revoke second device based on fd */
+	rc = libevdev_revoke_fd(fd);
+	if (rc == -EINVAL) {
+		fprintf(stderr, "::: WARNING: skipping libevdev_revoke_fd test, not suported by current kernel\n");
+		goto out;
+	}
+
+	rc = libevdev_next_event(dev2, LIBEVDEV_READ_FLAG_NORMAL, &ev2);
+	ck_assert_int_eq(rc, -ENODEV);
+
+	rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev1);
+	ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SUCCESS);
+
+out:
+	uinput_device_free(uidev);
+	libevdev_free(dev);
+	libevdev_free(dev2);
+	close(fd);
+}
+END_TEST
+
 Suite *
 libevdev_init_test(void)
 {
@@ -397,5 +512,10 @@ libevdev_init_test(void)
 	tcase_add_test(tc, test_clock_id_events);
 	suite_add_tcase(s, tc);
 
+	tc = tcase_create("revoke");
+	tcase_add_test(tc, test_revoke);
+	tcase_add_test(tc, test_revoke_fd);
+	suite_add_tcase(s, tc);
+
 	return s;
 }
-- 
1.8.4.2



More information about the Input-tools mailing list