evdev: don't resume a removed device
authorPeter Hutterer <peter.hutterer@who-t.net>
Thu, 30 Jan 2014 06:33:24 +0000 (16:33 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Thu, 18 Sep 2014 01:31:23 +0000 (11:31 +1000)
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 <peter.hutterer@who-t.net>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
src/evdev.c
src/evdev.h
src/path.c
src/udev-seat.c

index 996ce32..3a5d874 100644 (file)
@@ -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);
 }
index 2af6828..bdd4a4e 100644 (file)
@@ -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);
index e9c0ee8..3752751 100644 (file)
@@ -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);
 
index ccff35c..f2be66e 100644 (file)
@@ -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) {