[PATCH v2 libevdev 2/4] Let libevdev manage the /dev/uinput node if required
Peter Hutterer
peter.hutterer at who-t.net
Thu Aug 15 23:50:47 PDT 2013
Same API as before, but if the fd is LIBEVDEV_UINPUT_OPEN_MANAGED, libevdev
opens the uinput node itself. That value is intentionally different to -1, to avoid
code like the below from accidentally working:
fd = open("/dev/uinput", O_RDWR); /* fails, fd is -1 */
libevdev_uinput_create_from_device(dev, fd, &uidev); /* may hide the error */
Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
Changes to v1:
- O_CLOEXEC
- actually do what the commit message says, and return -EBADF for neg file
descriptors
- add a comment to explain -2
- add a test for invalid create
libevdev/libevdev-uinput-int.h | 1 +
libevdev/libevdev-uinput.c | 21 +++++++++++
libevdev/libevdev-uinput.h | 20 ++++++++---
test/test-uinput.c | 80 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 118 insertions(+), 4 deletions(-)
diff --git a/libevdev/libevdev-uinput-int.h b/libevdev/libevdev-uinput-int.h
index 70fa1e1..f443984 100644
--- a/libevdev/libevdev-uinput-int.h
+++ b/libevdev/libevdev-uinput-int.h
@@ -23,6 +23,7 @@
struct libevdev_uinput {
int fd; /**< file descriptor to uinput */
+ int fd_is_managed; /**< do we need to close it? */
char *name; /**< device name */
char *syspath; /**< /sys path */
time_t ctime[2]; /**< before/after UI_DEV_CREATE */
diff --git a/libevdev/libevdev-uinput.c b/libevdev/libevdev-uinput.c
index 9fd36b4..45330e1 100644
--- a/libevdev/libevdev-uinput.c
+++ b/libevdev/libevdev-uinput.c
@@ -139,6 +139,16 @@ set_props(const struct libevdev *dev, int fd, struct uinput_user_dev *uidev)
return rc;
}
+static int
+open_uinput()
+{
+ int fd = open("/dev/uinput", O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
int
libevdev_uinput_create_from_device(const struct libevdev *dev, int fd, struct libevdev_uinput** uinput_dev)
{
@@ -150,6 +160,15 @@ libevdev_uinput_create_from_device(const struct libevdev *dev, int fd, struct li
if (!new_device)
return -ENOMEM;
+ if (fd == LIBEVDEV_UINPUT_OPEN_MANAGED) {
+ fd = open_uinput();
+ if (fd < 0)
+ return fd;
+
+ new_device->fd_is_managed = 1;
+ } else if (fd < 0)
+ return -EBADF;
+
memset(&uidev, 0, sizeof(uidev));
strncpy(uidev.name, libevdev_get_name(dev), UINPUT_MAX_NAME_SIZE - 1);
@@ -199,6 +218,8 @@ error:
void libevdev_uinput_destroy(struct libevdev_uinput *uinput_dev)
{
ioctl(uinput_dev->fd, UI_DEV_DESTROY, NULL);
+ if (uinput_dev->fd_is_managed)
+ close(uinput_dev->fd);
free(uinput_dev->syspath);
free(uinput_dev->name);
free(uinput_dev);
diff --git a/libevdev/libevdev-uinput.h b/libevdev/libevdev-uinput.h
index 9e19ad2..a778845 100644
--- a/libevdev/libevdev-uinput.h
+++ b/libevdev/libevdev-uinput.h
@@ -99,6 +99,13 @@ struct libevdev_uinput;
* @endcode
*/
+enum EvdevUInputOpenMode {
+ /* intentionally -2 to avoid to avoid code like the below from accidentally working:
+ fd = open("/dev/uinput", O_RDWR); // fails, fd is -1
+ libevdev_uinput_create_from_device(dev, fd, &uidev); // may hide the error */
+ LIBEVDEV_UINPUT_OPEN_MANAGED = -2,
+};
+
/**
* @ingroup uinput
*
@@ -106,6 +113,11 @@ struct libevdev_uinput;
* will be an exact copy of the libevdev device, minus the bits that uinput doesn't
* allow to be set.
*
+ * If uinput_fd is LIBEVDEV_UINPUT_OPEN_MANAGED, libevdev_uinput_create_from_device()
+ * will open @c /dev/uinput in read/write mode and manage the file descriptor.
+ * Otherwise, uinput_fd must be opened by the caller and opened with the
+ * appropriate permissions.
+ *
* The device's lifetime is tied to the uinput file descriptor, closing it will
* destroy the uinput device. You should call libevdev_uinput_destroy() before
* closing the file descriptor to free allocated resources.
@@ -120,9 +132,7 @@ struct libevdev_uinput;
* source device.
*
* @param dev The device to duplicate
- * @param uinput_fd A file descriptor to @c /dev/uinput, opened with the required
- * permissions to create a device. This fd may only be used once to create a
- * uinput device.
+ * @param uinput_fd LIBEVDEV_UINPUT_OPEN_MANAGED or a file descriptor to @c /dev/uinput,
* @param[out] uinput_dev The newly created libevdev device.
*
* @return 0 on success or a negative errno on failure. On failure, the value of
@@ -139,7 +149,9 @@ int libevdev_uinput_create_from_device(const struct libevdev *dev,
*
* Destroy a previously created uinput device and free associated memory.
*
- * @note libevdev_uinput_destroy() does not close the fd.
+ * If the device was opened with LIBEVDEV_UINPUT_OPEN_MANAGED, libevdev_uinput_destroy()
+ * also closes the file descriptor. Otherwise, the fd is left as-is and
+ * must be closed by the caller.
*
* @param uinput_dev A previously created uinput device.
*
diff --git a/test/test-uinput.c b/test/test-uinput.c
index 9d7fa85..e7f6266 100644
--- a/test/test-uinput.c
+++ b/test/test-uinput.c
@@ -36,6 +36,84 @@ START_TEST(test_uinput_create_device)
{
struct libevdev *dev, *dev2;
struct libevdev_uinput *uidev;
+ int fd, uinput_fd;
+ unsigned int type, code;
+ int rc;
+ char *devnode;
+
+ dev = libevdev_new();
+ ck_assert(dev != NULL);
+ libevdev_set_name(dev, TEST_DEVICE_NAME);
+ libevdev_enable_event_type(dev, EV_SYN);
+ libevdev_enable_event_type(dev, EV_REL);
+ libevdev_enable_event_code(dev, EV_REL, REL_X, NULL);
+ libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL);
+
+ rc = libevdev_uinput_create_from_device(dev, LIBEVDEV_UINPUT_OPEN_MANAGED, &uidev);
+ ck_assert_int_eq(rc, 0);
+ ck_assert(uidev != NULL);
+
+ uinput_fd = libevdev_uinput_get_fd(uidev);
+ ck_assert_int_gt(uinput_fd, -1);
+
+ devnode = uinput_devnode_from_syspath(libevdev_uinput_get_syspath(uidev));
+ ck_assert(devnode != NULL);
+
+ fd = open(devnode, O_RDONLY);
+ ck_assert_int_gt(fd, -1);
+ rc = libevdev_new_from_fd(fd, &dev2);
+ ck_assert_int_eq(rc, 0);
+
+ for (type = 0; type < EV_MAX; type++) {
+ int max = libevdev_get_event_type_max(type);
+ if (max == -1)
+ continue;
+
+ for (code = 0; code < max; code++) {
+ ck_assert_int_eq(libevdev_has_event_code(dev, type, code),
+ libevdev_has_event_code(dev2, type, code));
+ }
+ }
+
+ libevdev_free(dev);
+ libevdev_free(dev2);
+ libevdev_uinput_destroy(uidev);
+ close(fd);
+ free(devnode);
+
+ /* uinput fd is managed, so make sure it did get closed */
+ ck_assert_int_eq(close(uinput_fd), -1);
+ ck_assert_int_eq(errno, EBADF);
+
+}
+END_TEST
+
+START_TEST(test_uinput_create_device_invalid)
+{
+ struct libevdev *dev;
+ struct libevdev_uinput *uidev = NULL;
+ int rc;
+
+ dev = libevdev_new();
+ ck_assert(dev != NULL);
+ libevdev_set_name(dev, TEST_DEVICE_NAME);
+ libevdev_enable_event_type(dev, EV_SYN);
+ libevdev_enable_event_type(dev, EV_REL);
+ libevdev_enable_event_code(dev, EV_REL, REL_X, NULL);
+ libevdev_enable_event_code(dev, EV_REL, REL_Y, NULL);
+
+ rc = libevdev_uinput_create_from_device(dev, -1, &uidev);
+ ck_assert_int_eq(rc, -EBADF);
+ ck_assert(uidev == NULL);
+
+ libevdev_free(dev);
+}
+END_TEST
+
+START_TEST(test_uinput_create_device_from_fd)
+{
+ struct libevdev *dev, *dev2;
+ struct libevdev_uinput *uidev;
int fd, fd2;
unsigned int type, code;
int rc;
@@ -257,6 +335,8 @@ uinput_suite(void)
TCase *tc = tcase_create("device creation");
tcase_add_test(tc, test_uinput_create_device);
+ tcase_add_test(tc, test_uinput_create_device_invalid);
+ tcase_add_test(tc, test_uinput_create_device_from_fd);
tcase_add_test(tc, test_uinput_check_syspath_time);
tcase_add_test(tc, test_uinput_check_syspath_name);
suite_add_tcase(s, tc);
--
1.8.2.1
More information about the Input-tools
mailing list