From 8be1abf58ad855ba25f48db5a7196c85dda1ff25 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Thu, 30 Jan 2014 16:33:24 +1000 Subject: [PATCH] evdev: don't resume a removed device A device may disappear and a new device may re-appear with the same device node while the original device is suspended. Prevent a device resume to open the wrong device. In a path context, a changing syspath is the only indicator we get of the device changing. In a udev context, if the device was removed and libinput_dispatch() was called, we can short-cut the syspath comparison by setting it to NULL. Signed-off-by: Peter Hutterer Reviewed-by: Hans de Goede --- src/evdev.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- src/evdev.h | 4 +++- src/path.c | 9 ++++++--- src/udev-seat.c | 4 +++- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src/evdev.c b/src/evdev.c index 996ce32..3a5d874 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -971,7 +971,8 @@ evdev_configure_device(struct evdev_device *device) struct evdev_device * evdev_device_create(struct libinput_seat *seat, const char *devnode, - const char *sysname) + const char *sysname, + const char *syspath) { struct libinput *libinput = seat->libinput; struct evdev_device *device; @@ -1008,6 +1009,7 @@ evdev_device_create(struct libinput_seat *seat, device->mtdev = NULL; device->devnode = strdup(devnode); device->sysname = strdup(sysname); + device->syspath = strdup(syspath); device->rel.dx = 0; device->rel.dy = 0; device->abs.seat_slot = -1; @@ -1255,6 +1257,36 @@ evdev_device_suspend(struct evdev_device *device) return 0; } +static int +evdev_device_compare_syspath(struct evdev_device *device, int fd) +{ + struct udev *udev = NULL; + struct udev_device *udev_device = NULL; + const char *syspath; + struct stat st; + int rc = 1; + + udev = udev_new(); + if (!udev) + goto out; + + if (fstat(fd, &st) < 0) + goto out; + + udev_device = udev_device_new_from_devnum(udev, 'c', st.st_rdev); + if (!device) + goto out; + + syspath = udev_device_get_syspath(udev_device); + rc = strcmp(syspath, device->syspath); +out: + if (udev_device) + udev_device_unref(udev_device); + if (udev) + udev_unref(udev); + return rc; +} + int evdev_device_resume(struct evdev_device *device) { @@ -1264,11 +1296,19 @@ evdev_device_resume(struct evdev_device *device) if (device->fd != -1) return 0; + if (device->syspath == NULL) + return -ENODEV; + fd = open_restricted(libinput, device->devnode, O_RDWR | O_NONBLOCK); if (fd < 0) return -errno; + if (evdev_device_compare_syspath(device, fd)) { + close_restricted(libinput, fd); + return -ENODEV; + } + device->fd = fd; if (evdev_need_mtdev(device)) { @@ -1294,6 +1334,11 @@ evdev_device_remove(struct evdev_device *device) { evdev_device_suspend(device); + /* A device may be removed while suspended. Free the syspath to + * skip re-opening a different device with the same node */ + free(device->syspath); + device->syspath = NULL; + list_remove(&device->base.link); notify_removed_device(&device->base); @@ -1315,5 +1360,6 @@ evdev_device_destroy(struct evdev_device *device) free(device->mt.slots); free(device->devnode); free(device->sysname); + free(device->syspath); free(device); } diff --git a/src/evdev.h b/src/evdev.h index 2af6828..bdd4a4e 100644 --- a/src/evdev.h +++ b/src/evdev.h @@ -63,6 +63,7 @@ struct evdev_device { char *output_name; char *devnode; char *sysname; + char *syspath; const char *devname; int fd; struct { @@ -134,7 +135,8 @@ struct evdev_dispatch { struct evdev_device * evdev_device_create(struct libinput_seat *seat, const char *devnode, - const char *sysname); + const char *sysname, + const char *syspath); struct evdev_dispatch * evdev_touchpad_create(struct evdev_device *device); diff --git a/src/path.c b/src/path.c index e9c0ee8..3752751 100644 --- a/src/path.c +++ b/src/path.c @@ -112,6 +112,7 @@ path_seat_get_named(struct path_input *input, static int path_get_udev_properties(const char *path, char **sysname, + char **syspath, char **seat_name, char **seat_logical_name) { @@ -133,6 +134,7 @@ path_get_udev_properties(const char *path, goto out; *sysname = strdup(udev_device_get_sysname(device)); + *syspath = strdup(udev_device_get_syspath(device)); seat = udev_device_get_property_value(device, "ID_SEAT"); *seat_name = strdup(seat ? seat : default_seat); @@ -155,10 +157,10 @@ path_device_enable(struct path_input *input, const char *devnode) { struct path_seat *seat; struct evdev_device *device = NULL; - char *sysname = NULL; + char *sysname = NULL, *syspath = NULL; char *seat_name = NULL, *seat_logical_name = NULL; - if (path_get_udev_properties(devnode, &sysname, + if (path_get_udev_properties(devnode, &sysname, &syspath, &seat_name, &seat_logical_name) == -1) { log_info(&input->base, "failed to obtain sysname for device '%s'.\n", @@ -180,7 +182,7 @@ path_device_enable(struct path_input *input, const char *devnode) } } - device = evdev_device_create(&seat->base, devnode, sysname); + device = evdev_device_create(&seat->base, devnode, sysname, syspath); libinput_seat_unref(&seat->base); if (device == EVDEV_UNHANDLED_DEVICE) { @@ -198,6 +200,7 @@ path_device_enable(struct path_input *input, const char *devnode) out: free(sysname); + free(syspath); free(seat_name); free(seat_logical_name); diff --git a/src/udev-seat.c b/src/udev-seat.c index ccff35c..f2be66e 100644 --- a/src/udev-seat.c +++ b/src/udev-seat.c @@ -47,6 +47,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) struct evdev_device *device; const char *devnode; const char *sysname; + const char *syspath; const char *device_seat, *seat_name, *output_name; const char *calibration_values; float calibration[6]; @@ -61,6 +62,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) devnode = udev_device_get_devnode(udev_device); sysname = udev_device_get_sysname(udev_device); + syspath = udev_device_get_syspath(udev_device); /* Search for matching logical seat */ seat_name = udev_device_get_property_value(udev_device, "WL_SEAT"); @@ -77,7 +79,7 @@ device_added(struct udev_device *udev_device, struct udev_input *input) return -1; } - device = evdev_device_create(&seat->base, devnode, sysname); + device = evdev_device_create(&seat->base, devnode, sysname, syspath); libinput_seat_unref(&seat->base); if (device == EVDEV_UNHANDLED_DEVICE) { -- 2.7.4