Zbigniew Jędrzejewski-Szmek 62fe94
From c93e5a62ff599528c3bf2a8656825403aaebe093 Mon Sep 17 00:00:00 2001
Zbigniew Jędrzejewski-Szmek 62fe94
From: David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
Date: Wed, 27 Aug 2014 18:31:34 +0200
Zbigniew Jędrzejewski-Szmek 62fe94
Subject: [PATCH] terminal: add evdev elements to idev
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
The evdev-element provides linux evdev interfaces as idev-elements. This
Zbigniew Jędrzejewski-Szmek 62fe94
way, all real input hardware devices on linux can be used with the idev
Zbigniew Jędrzejewski-Szmek 62fe94
interface.
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
We use libevdev to interface with the kernel. It's a simple wrapper
Zbigniew Jędrzejewski-Szmek 62fe94
library around the kernel evdev API that takes care to resync devices
Zbigniew Jędrzejewski-Szmek 62fe94
after kernel-queue overflows, which is a rather non-trivial task.
Zbigniew Jędrzejewski-Szmek 62fe94
Furthermore, it's a well tested interface used by all other major input
Zbigniew Jędrzejewski-Szmek 62fe94
users (Xorg, weston, libinput, ...).
Zbigniew Jędrzejewski-Szmek 62fe94
Last but not least, it provides nice keycode to keyname lookup tables (and
Zbigniew Jędrzejewski-Szmek 62fe94
vice versa), which is really nice for debugging input problems.
Zbigniew Jędrzejewski-Szmek 62fe94
---
Zbigniew Jędrzejewski-Szmek 62fe94
 Makefile.am                             |   7 +-
Zbigniew Jędrzejewski-Szmek 62fe94
 configure.ac                            |   7 +-
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/idev-evdev.c    | 938 ++++++++++++++++++++++++++++++++
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/idev-internal.h |  10 +
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/idev.c          |  48 ++
Zbigniew Jędrzejewski-Szmek 62fe94
 src/libsystemd-terminal/idev.h          |  20 +
Zbigniew Jędrzejewski-Szmek 62fe94
 6 files changed, 1026 insertions(+), 4 deletions(-)
Zbigniew Jędrzejewski-Szmek 62fe94
 create mode 100644 src/libsystemd-terminal/idev-evdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/Makefile.am b/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
index 82f474e20e..b51c522443 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/Makefile.am
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -2968,12 +2968,14 @@ tests += \
Zbigniew Jędrzejewski-Szmek 62fe94
 endif
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 libsystemd_terminal_la_CFLAGS = \
Zbigniew Jędrzejewski-Szmek 62fe94
-	$(AM_CFLAGS)
Zbigniew Jędrzejewski-Szmek 62fe94
+	$(AM_CFLAGS) \
Zbigniew Jędrzejewski-Szmek 62fe94
+	$(TERMINAL_CFLAGS)
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 libsystemd_terminal_la_SOURCES = \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev-internal.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/idev.c \
Zbigniew Jędrzejewski-Szmek 62fe94
+	src/libsystemd-terminal/idev-evdev.c \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/sysview.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/sysview-internal.h \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/sysview.c \
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -2989,7 +2991,8 @@ libsystemd_terminal_la_SOURCES = \
Zbigniew Jędrzejewski-Szmek 62fe94
 libsystemd_terminal_la_LIBADD = \
Zbigniew Jędrzejewski-Szmek 62fe94
 	libudev-internal.la \
Zbigniew Jędrzejewski-Szmek 62fe94
 	libsystemd-internal.la \
Zbigniew Jędrzejewski-Szmek 62fe94
-	libsystemd-shared.la
Zbigniew Jędrzejewski-Szmek 62fe94
+	libsystemd-shared.la \
Zbigniew Jędrzejewski-Szmek 62fe94
+	$(TERMINAL_LIBS)
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 systemd_subterm_SOURCES = \
Zbigniew Jędrzejewski-Szmek 62fe94
 	src/libsystemd-terminal/subterm.c
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/configure.ac b/configure.ac
Zbigniew Jędrzejewski-Szmek 62fe94
index 08a8a105f8..3900c4065b 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/configure.ac
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/configure.ac
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -1066,8 +1066,11 @@ AM_CONDITIONAL(ENABLE_MULTI_SEAT_X, [test "$have_multi_seat_x" = "yes"])
Zbigniew Jędrzejewski-Szmek 62fe94
 have_terminal=no
Zbigniew Jędrzejewski-Szmek 62fe94
 AC_ARG_ENABLE(terminal, AS_HELP_STRING([--enable-terminal], [enable terminal support]))
Zbigniew Jędrzejewski-Szmek 62fe94
 if test "x$enable_terminal" = "xyes"; then
Zbigniew Jędrzejewski-Szmek 62fe94
-        AC_DEFINE(ENABLE_TERMINAL, 1, [Define if terminal support is to be enabled])
Zbigniew Jędrzejewski-Szmek 62fe94
-        have_terminal=yes
Zbigniew Jędrzejewski-Szmek 62fe94
+        PKG_CHECK_MODULES([TERMINAL], [ libevdev >= 1.2 ], [have_terminal=yes])
Zbigniew Jędrzejewski-Szmek 62fe94
+        AS_IF([test "x$have_terminal" != xyes -a "x$enable_terminal" = xyes],
Zbigniew Jędrzejewski-Szmek 62fe94
+              [AC_MSG_ERROR([*** terminal support requested but required dependencies not available])],
Zbigniew Jędrzejewski-Szmek 62fe94
+              [test "x$have_terminal" = xyes],
Zbigniew Jędrzejewski-Szmek 62fe94
+              [AC_DEFINE(ENABLE_TERMINAL, 1, [Define if terminal support is to be enabled])])
Zbigniew Jędrzejewski-Szmek 62fe94
 fi
Zbigniew Jędrzejewski-Szmek 62fe94
 AM_CONDITIONAL(ENABLE_TERMINAL, [test "x$have_terminal" = "xyes"])
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/idev-evdev.c b/src/libsystemd-terminal/idev-evdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
new file mode 100644
Zbigniew Jędrzejewski-Szmek 62fe94
index 0000000000..c93ede8dc9
Zbigniew Jędrzejewski-Szmek 62fe94
--- /dev/null
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/idev-evdev.c
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -0,0 +1,938 @@
Zbigniew Jędrzejewski-Szmek 62fe94
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/***
Zbigniew Jędrzejewski-Szmek 62fe94
+  This file is part of systemd.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  Copyright (C) 2014 David Herrmann <dh.herrmann@gmail.com>
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is free software; you can redistribute it and/or modify it
Zbigniew Jędrzejewski-Szmek 62fe94
+  under the terms of the GNU Lesser General Public License as published by
Zbigniew Jędrzejewski-Szmek 62fe94
+  the Free Software Foundation; either version 2.1 of the License, or
Zbigniew Jędrzejewski-Szmek 62fe94
+  (at your option) any later version.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  systemd is distributed in the hope that it will be useful, but
Zbigniew Jędrzejewski-Szmek 62fe94
+  WITHOUT ANY WARRANTY; without even the implied warranty of
Zbigniew Jędrzejewski-Szmek 62fe94
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Zbigniew Jędrzejewski-Szmek 62fe94
+  Lesser General Public License for more details.
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+  You should have received a copy of the GNU Lesser General Public License
Zbigniew Jędrzejewski-Szmek 62fe94
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
Zbigniew Jędrzejewski-Szmek 62fe94
+***/
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <fcntl.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libevdev/libevdev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libudev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <systemd/sd-event.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <unistd.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "bus-util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "hashmap.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "idev.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "idev-internal.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "macro.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "udev-util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct idev_evdev idev_evdev;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct unmanaged_evdev unmanaged_evdev;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct managed_evdev managed_evdev;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct idev_evdev {
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct idev_element element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct libevdev *evdev;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source *fd_src;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source *idle_src;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool unsync : 1;                /* not in-sync with kernel */
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool resync : 1;                /* re-syncing with kernel */
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct unmanaged_evdev {
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct idev_evdev evdev;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char *devnode;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct managed_evdev {
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct idev_evdev evdev;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_bus_slot *slot_pause_device;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_bus_slot *slot_resume_device;
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_bus_slot *slot_take_device;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool requested : 1;             /* TakeDevice() was sent */
Zbigniew Jędrzejewski-Szmek 62fe94
+        bool acquired : 1;              /* TakeDevice() was successful */
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define idev_evdev_from_element(_e) container_of((_e), idev_evdev, element)
Zbigniew Jędrzejewski-Szmek 62fe94
+#define unmanaged_evdev_from_element(_e) \
Zbigniew Jędrzejewski-Szmek 62fe94
+        container_of(idev_evdev_from_element(_e), unmanaged_evdev, evdev)
Zbigniew Jędrzejewski-Szmek 62fe94
+#define managed_evdev_from_element(_e) \
Zbigniew Jędrzejewski-Szmek 62fe94
+        container_of(idev_evdev_from_element(_e), managed_evdev, evdev)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define IDEV_EVDEV_INIT(_vtable, _session) ((idev_evdev){ \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .element = IDEV_ELEMENT_INIT((_vtable), (_session)), \
Zbigniew Jędrzejewski-Szmek 62fe94
+                .fd = -1, \
Zbigniew Jędrzejewski-Szmek 62fe94
+        })
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+#define IDEV_EVDEV_NAME_MAX (8 + DECIMAL_STR_MAX(unsigned) * 2)
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static const idev_element_vtable unmanaged_evdev_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+static const idev_element_vtable managed_evdev_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int idev_evdev_resume(idev_evdev *evdev, int dev_fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+static void idev_evdev_pause(idev_evdev *evdev, bool release);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Virtual Evdev Element
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The virtual evdev element is the base class of all other evdev elements. It
Zbigniew Jędrzejewski-Szmek 62fe94
+ * uses libevdev to access the kernel evdev API. It supports asynchronous
Zbigniew Jędrzejewski-Szmek 62fe94
+ * access revocation, re-syncing if events got dropped and more.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * This element cannot be used by itself. There must be a wrapper around it
Zbigniew Jędrzejewski-Szmek 62fe94
+ * which opens a file-descriptor and passes it to the virtual evdev element.
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void idev_evdev_name(char *out, dev_t devnum) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* @out must be at least of size IDEV_EVDEV_NAME_MAX */
Zbigniew Jędrzejewski-Szmek 62fe94
+        sprintf(out, "evdev/%u:%u", major(devnum), minor(devnum));
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int idev_evdev_raise(idev_evdev *evdev, struct input_event *event) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_data data = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                .type = IDEV_DATA_EVDEV,
Zbigniew Jędrzejewski-Szmek 62fe94
+                .resync = evdev->resync,
Zbigniew Jędrzejewski-Szmek 62fe94
+                .evdev = {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        .event = *event,
Zbigniew Jędrzejewski-Szmek 62fe94
+                },
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return idev_element_feed(&evdev->element, &data);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void idev_evdev_hup(idev_evdev *evdev) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * On HUP, we close the current fd via idev_evdev_pause(). This drops
Zbigniew Jędrzejewski-Szmek 62fe94
+         * the event-sources from the main-loop and effectively puts the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * element asleep. If the HUP is part of a hotplug-event, a following
Zbigniew Jędrzejewski-Szmek 62fe94
+         * udev-notification will destroy the element. Otherwise, the HUP is
Zbigniew Jędrzejewski-Szmek 62fe94
+         * either result of access-revokation or a serious error.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * For unmanaged devices, we should never receive HUP (except for
Zbigniew Jędrzejewski-Szmek 62fe94
+         * unplug-events). But if we do, something went seriously wrong and we
Zbigniew Jędrzejewski-Szmek 62fe94
+         * shouldn't try to be clever.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Instead, we simply stay asleep and wait for the device to be
Zbigniew Jędrzejewski-Szmek 62fe94
+         * disabled and then re-enabled (or closed and re-opened). This will
Zbigniew Jędrzejewski-Szmek 62fe94
+         * re-open the device node and restart the device.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * For managed devices, a HUP usually means our device-access was
Zbigniew Jędrzejewski-Szmek 62fe94
+         * revoked. In that case, we simply put the device asleep and wait for
Zbigniew Jędrzejewski-Szmek 62fe94
+         * logind to notify us once the device is alive again. logind also
Zbigniew Jędrzejewski-Szmek 62fe94
+         * passes us a new fd. Hence, we don't have to re-enable the device.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Long story short: The only thing we have to do here, is close() the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * file-descriptor and remove it from the main-loop. Everything else is
Zbigniew Jędrzejewski-Szmek 62fe94
+         * handled via additional events we receive.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_pause(evdev, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int idev_evdev_io(idev_evdev *evdev) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e = &evdev->element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct input_event ev;
Zbigniew Jędrzejewski-Szmek 62fe94
+        unsigned int flags;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, error = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Read input-events via libevdev until the input-queue is drained. In
Zbigniew Jędrzejewski-Szmek 62fe94
+         * case we're disabled, don't do anything. The input-queue might
Zbigniew Jędrzejewski-Szmek 62fe94
+         * overflow, but we don't care as we have to resync after wake-up,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * anyway.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * TODO: libevdev should give us a hint how many events to read. We
Zbigniew Jędrzejewski-Szmek 62fe94
+         * really want to avoid starvation, so we shouldn't read forever in
Zbigniew Jędrzejewski-Szmek 62fe94
+         * case we cannot keep up with the kernel.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * TODO: Make sure libevdev always reports SYN_DROPPED to us, regardless
Zbigniew Jędrzejewski-Szmek 62fe94
+         * whether any event was synced afterwards.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * TODO: Forward SYN_DROPPED to attached devices.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        flags = LIBEVDEV_READ_FLAG_NORMAL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        while (e->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (evdev->unsync) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* immediately resync, even if in sync right now */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        evdev->unsync = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        evdev->resync = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        flags = LIBEVDEV_READ_FLAG_NORMAL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = libevdev_next_event(evdev->evdev, flags | LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev;;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r < 0 && r != -EAGAIN) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        } else if (r != LIBEVDEV_READ_STATUS_SYNC) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("idev-evdev: %s/%s: cannot force resync: %d",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          e->session->name, e->name, r);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = libevdev_next_event(evdev->evdev, flags, &ev;;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (evdev->resync && r == -EAGAIN) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* end of re-sync */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        evdev->resync = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        flags = LIBEVDEV_READ_FLAG_NORMAL;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else if (r == -EAGAIN) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* no data available */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* read error */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else if (r == LIBEVDEV_READ_STATUS_SYNC) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (evdev->resync) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                /* sync-event */
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = idev_evdev_raise(evdev, &ev;;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                if (r != 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        error = r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                }
Zbigniew Jędrzejewski-Szmek 62fe94
+                        } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                /* start of sync */
Zbigniew Jędrzejewski-Szmek 62fe94
+                                evdev->resync = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                flags = LIBEVDEV_READ_FLAG_SYNC;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        /* normal event */
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = idev_evdev_raise(evdev, &ev;;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r != 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                error = r;
Zbigniew Jędrzejewski-Szmek 62fe94
+                                break;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (error < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: error on data event: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          e->session->name, e->name, strerror(-error));
Zbigniew Jędrzejewski-Szmek 62fe94
+        return error;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+error:
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_hup(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int idev_evdev_event_fn(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev *evdev = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* fetch data as long as EPOLLIN is signalled */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (revents & EPOLLIN)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return idev_evdev_io(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (revents & (EPOLLHUP | EPOLLERR))
Zbigniew Jędrzejewski-Szmek 62fe94
+                idev_evdev_hup(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int idev_evdev_idle_fn(sd_event_source *s, void *userdata) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev *evdev = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * The idle-event is raised whenever we have to re-sync the libevdev
Zbigniew Jędrzejewski-Szmek 62fe94
+         * state from the kernel. We simply call into idev_evdev_io() which
Zbigniew Jędrzejewski-Szmek 62fe94
+         * flushes the state and re-syncs it if @unsync is set.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * State has to be synced whenever our view of the kernel device is
Zbigniew Jędrzejewski-Szmek 62fe94
+         * out of date. This is the case when we open the device, if the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * kernel's receive buffer overflows, or on other exceptional
Zbigniew Jędrzejewski-Szmek 62fe94
+         * situations. Events during re-syncs must be forwarded to the upper
Zbigniew Jędrzejewski-Szmek 62fe94
+         * layers so they can update their view of the device. However, such
Zbigniew Jędrzejewski-Szmek 62fe94
+         * events must only be handled passively, as they might be out-of-order
Zbigniew Jędrzejewski-Szmek 62fe94
+         * and/or re-ordered. Therefore, we mark them as 'sync' events.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!evdev->unsync)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return idev_evdev_io(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void idev_evdev_destroy(idev_evdev *evdev) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(evdev->fd < 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        libevdev_free(evdev->evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        evdev->evdev = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void idev_evdev_enable(idev_evdev *evdev) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(evdev->fd_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(evdev->idle_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_ON);
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_ONESHOT);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void idev_evdev_disable(idev_evdev *evdev) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(evdev->fd_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(evdev->idle_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
Zbigniew Jędrzejewski-Szmek 62fe94
+        sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int idev_evdev_resume(idev_evdev *evdev, int dev_fd) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e = &evdev->element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_close_ int fd = dev_fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, flags;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (fd < 0 || evdev->fd == fd) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                fd = -1;
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (evdev->fd >= 0 && e->n_open > 0 && e->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        idev_evdev_enable(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_pause(evdev, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("idev-evdev: %s/%s: resume", e->session->name, e->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = fd_nonblock(fd, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = fd_cloexec(fd, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        flags = fcntl(fd, F_GETFL, 0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (flags < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        flags &= O_ACCMODE;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (flags == O_WRONLY)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -EACCES;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        evdev->element.readable = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        evdev->element.writable = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (flags == O_RDONLY)
Zbigniew Jędrzejewski-Szmek 62fe94
+                evdev->element.writable = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+        else if (flags == O_WRONLY)
Zbigniew Jędrzejewski-Szmek 62fe94
+                evdev->element.readable = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * TODO: We *MUST* re-sync the device so we get a delta of the changed
Zbigniew Jędrzejewski-Szmek 62fe94
+         * state while we didn't read events from the device. This works just
Zbigniew Jędrzejewski-Szmek 62fe94
+         * fine with libevdev_change_fd(), however, libevdev_new_from_fd() (or
Zbigniew Jędrzejewski-Szmek 62fe94
+         * libevdev_set_fd()) don't pass us events for the initial device
Zbigniew Jędrzejewski-Szmek 62fe94
+         * state. So even if we force a re-sync, we will not get the delta for
Zbigniew Jędrzejewski-Szmek 62fe94
+         * the initial device state.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * We really need to fix libevdev to support that!
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (evdev->evdev)
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = libevdev_change_fd(evdev->evdev, fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+        else
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = libevdev_new_from_fd(fd, &evdev->evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_add_io(e->session->context->event,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            &evdev->fd_src,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            fd,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            EPOLLHUP | EPOLLERR | EPOLLIN,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            idev_evdev_event_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                            evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_event_add_defer(e->session->context->event,
Zbigniew Jędrzejewski-Szmek 62fe94
+                               &evdev->idle_src,
Zbigniew Jędrzejewski-Szmek 62fe94
+                               idev_evdev_idle_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                               evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                evdev->fd_src = sd_event_source_unref(evdev->fd_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (e->n_open < 1 || !e->enabled) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                sd_event_source_set_enabled(evdev->fd_src, SD_EVENT_OFF);
Zbigniew Jędrzejewski-Szmek 62fe94
+                sd_event_source_set_enabled(evdev->idle_src, SD_EVENT_OFF);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        evdev->unsync = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        evdev->fd = fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fd = -1;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void idev_evdev_pause(idev_evdev *evdev, bool release) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e = &evdev->element;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (evdev->fd < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("idev-evdev: %s/%s: pause", e->session->name, e->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (release) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                evdev->idle_src = sd_event_source_unref(evdev->idle_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+                evdev->fd_src = sd_event_source_unref(evdev->fd_src);
Zbigniew Jędrzejewski-Szmek 62fe94
+                evdev->fd = safe_close(evdev->fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+        } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                idev_evdev_disable(evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Unmanaged Evdev Element
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The unmanaged evdev element opens the evdev node for a given input device
Zbigniew Jędrzejewski-Szmek 62fe94
+ * directly (/dev/input/eventX) and thus needs sufficient privileges. It opens
Zbigniew Jędrzejewski-Szmek 62fe94
+ * the device only if we really require it and releases it as soon as we're
Zbigniew Jędrzejewski-Szmek 62fe94
+ * disabled or closed.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The unmanaged element can be used in all situations where you have direct
Zbigniew Jędrzejewski-Szmek 62fe94
+ * access to input device nodes. Unlike managed evdev elements, it can be used
Zbigniew Jędrzejewski-Szmek 62fe94
+ * outside of user sessions and in emergency situations where logind is not
Zbigniew Jędrzejewski-Szmek 62fe94
+ * available.
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void unmanaged_evdev_resume(idev_element *e) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Unmanaged devices can be acquired on-demand. Therefore, don't
Zbigniew Jędrzejewski-Szmek 62fe94
+         * acquire it unless someone opened the device *and* we're enabled.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (e->n_open < 1 || !e->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fd = eu->evdev.fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                fd = open(eu->devnode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (errno != EACCES && errno != EPERM) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("idev-evdev: %s/%s: cannot open node %s: %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          e->session->name, e->name, eu->devnode);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        fd = open(eu->devnode, O_RDONLY | O_CLOEXEC | O_NOCTTY | O_NONBLOCK);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                                log_debug("idev-evdev: %s/%s: cannot open node %s: %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          e->session->name, e->name, eu->devnode);
Zbigniew Jędrzejewski-Szmek 62fe94
+                                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                        e->readable = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        e->writable = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+                } else {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        e->readable = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                        e->writable = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = idev_evdev_resume(&eu->evdev, fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: cannot resume: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          e->session->name, e->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void unmanaged_evdev_pause(idev_element *e) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Release the device if the device is disabled or there is no-one who
Zbigniew Jędrzejewski-Szmek 62fe94
+         * opened it. This guarantees we stay only available if we're opened
Zbigniew Jędrzejewski-Szmek 62fe94
+         * *and* enabled.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_pause(&eu->evdev, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int unmanaged_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(idev_element_freep) idev_element *e = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char name[IDEV_EVDEV_NAME_MAX];
Zbigniew Jędrzejewski-Szmek 62fe94
+        unmanaged_evdev *eu;
Zbigniew Jędrzejewski-Szmek 62fe94
+        const char *devnode;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(s, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(ud, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnode = udev_device_get_devnode(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!devnode || devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENODEV;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_name(name, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        eu = new0(unmanaged_evdev, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!eu)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        e = &eu->evdev.element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        eu->evdev = IDEV_EVDEV_INIT(&unmanaged_evdev_vtable, s);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        eu->devnode = strdup(devnode);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!eu->devnode)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = idev_element_add(e, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = e;
Zbigniew Jędrzejewski-Szmek 62fe94
+        e = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void unmanaged_evdev_free(idev_element *e) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        unmanaged_evdev *eu = unmanaged_evdev_from_element(e);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_destroy(&eu->evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(eu->devnode);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(eu);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static const idev_element_vtable unmanaged_evdev_vtable = {
Zbigniew Jędrzejewski-Szmek 62fe94
+        .free                   = unmanaged_evdev_free,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .enable                 = unmanaged_evdev_resume,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .disable                = unmanaged_evdev_pause,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .open                   = unmanaged_evdev_resume,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .close                  = unmanaged_evdev_pause,
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Managed Evdev Element
Zbigniew Jędrzejewski-Szmek 62fe94
+ * The managed evdev element uses systemd-logind to acquire evdev devices. This
Zbigniew Jędrzejewski-Szmek 62fe94
+ * means, we do not open the device node /dev/input/eventX directly. Instead,
Zbigniew Jędrzejewski-Szmek 62fe94
+ * logind passes us a file-descriptor whenever our session is activated. Thus,
Zbigniew Jędrzejewski-Szmek 62fe94
+ * we don't need access to the device node directly.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Furthermore, whenever the session is put asleep, logind revokes the
Zbigniew Jędrzejewski-Szmek 62fe94
+ * file-descriptor so we loose access to the device.
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Managed evdev elements should be preferred over unmanaged elements whenever
Zbigniew Jędrzejewski-Szmek 62fe94
+ * you run inside a user session with exclusive device access.
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_evdev_take_device_fn(sd_bus *bus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        sd_bus_message *reply,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        void *userdata,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                        sd_bus_error *ret_error) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_evdev *em = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e = &em->evdev.element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_session *s = e->session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, paused, fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->slot_take_device = sd_bus_slot_unref(em->slot_take_device);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (sd_bus_message_is_method_error(reply, NULL)) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                const sd_bus_error *error = sd_bus_message_get_error(reply);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: TakeDevice failed: %s: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          s->name, e->name, error->name, error->message);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->acquired = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_read(reply, "hb", &fd, &paused);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: erroneous TakeDevice reply", s->name, e->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* If the device is paused, ignore it; we will get the next fd via
Zbigniew Jędrzejewski-Szmek 62fe94
+         * ResumeDevice signals. */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (paused)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: cannot duplicate evdev fd: %m", s->name, e->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = idev_evdev_resume(&em->evdev, fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: cannot resume: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          s->name, e->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void managed_evdev_resume(idev_element *e) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_evdev *em = managed_evdev_from_element(e);
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_session *s = e->session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_context *c = s->context;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Acquiring managed devices is heavy, so do it only once we're
Zbigniew Jędrzejewski-Szmek 62fe94
+         * enabled *and* opened by someone.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (e->n_open < 1 || !e->enabled)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* bail out if already pending */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (em->requested)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_new_method_call(c->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           &m,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "org.freedesktop.login1",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           s->path,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "org.freedesktop.login1.Session",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "TakeDevice");
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_call_async(c->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              &em->slot_take_device,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              m,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              managed_evdev_take_device_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              em,
Zbigniew Jędrzejewski-Szmek 62fe94
+                              0);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                goto error;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->requested = true;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+error:
Zbigniew Jędrzejewski-Szmek 62fe94
+        log_debug("idev-evdev: %s/%s: cannot send TakeDevice request: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                  s->name, e->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void managed_evdev_pause(idev_element *e) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_evdev *em = managed_evdev_from_element(e);
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_session *s = e->session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_context *c = s->context;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Releasing managed devices is heavy. Once acquired, we get
Zbigniew Jędrzejewski-Szmek 62fe94
+         * notifications for sleep/wake-up events, so there's no reason to
Zbigniew Jędrzejewski-Szmek 62fe94
+         * release it if disabled but opened. However, if a device is closed,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * we release it immediately as we don't care for sleep/wake-up events
Zbigniew Jędrzejewski-Szmek 62fe94
+         * then (even if we're actually enabled).
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_pause(&em->evdev, false);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (e->n_open > 0 || !em->requested)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * If TakeDevice() is pending or was successful, make sure to
Zbigniew Jędrzejewski-Szmek 62fe94
+         * release the device again. We don't care for return-values,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * so send it without waiting or callbacks.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * If a failed TakeDevice() is pending, but someone else took
Zbigniew Jędrzejewski-Szmek 62fe94
+         * the device on the same bus-connection, we might incorrectly
Zbigniew Jędrzejewski-Szmek 62fe94
+         * release their device. This is an unlikely race, though.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Furthermore, you really shouldn't have two users of the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * controller-API on the same session, on the same devices, *AND* on
Zbigniew Jędrzejewski-Szmek 62fe94
+         * the same bus-connection. So we don't care for that race..
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_pause(&em->evdev, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->requested = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!em->acquired && !em->slot_take_device)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->slot_take_device = sd_bus_slot_unref(em->slot_take_device);
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->acquired = false;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_new_method_call(c->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           &m,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "org.freedesktop.login1",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           s->path,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "org.freedesktop.login1.Session",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                           "ReleaseDevice");
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r >= 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = sd_bus_message_append(m, "uu", major(em->devnum), minor(em->devnum));
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r >= 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = sd_bus_send(c->sysbus, m, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0 && r != -ENOTCONN)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: cannot send ReleaseDevice: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          s->name, e->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_evdev_pause_device_fn(sd_bus *bus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                         sd_bus_message *signal,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                         void *userdata,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                         sd_bus_error *ret_error) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_evdev *em = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e = &em->evdev.element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_session *s = e->session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_context *c = s->context;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t major, minor;
Zbigniew Jędrzejewski-Szmek 62fe94
+        const char *mode;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * We get PauseDevice() signals from logind whenever a device we
Zbigniew Jędrzejewski-Szmek 62fe94
+         * requested was, or is about to be, paused. Arguments are major/minor
Zbigniew Jędrzejewski-Szmek 62fe94
+         * number of the device and the mode of the operation.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * In case the event is not about our device, we ignore it. Otherwise,
Zbigniew Jędrzejewski-Szmek 62fe94
+         * we treat it as asynchronous access-revocation (as if we got HUP on
Zbigniew Jędrzejewski-Szmek 62fe94
+         * the device fd). Note that we might have already treated the HUP
Zbigniew Jędrzejewski-Szmek 62fe94
+         * event via EPOLLHUP, whichever comes first.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         * @mode can be one of the following:
Zbigniew Jędrzejewski-Szmek 62fe94
+         *   "pause": The device is about to be paused. We must react
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            immediately and respond with PauseDeviceComplete(). Once
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            we replied, logind will pause the device. Note that
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            logind might apply any kind of timeout and force pause
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            the device if we don't respond in a timely manner. In
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            this case, we will receive a second PauseDevice event
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            with @mode set to "force" (or similar).
Zbigniew Jędrzejewski-Szmek 62fe94
+         *   "force": The device was disabled forecfully by logind. Access is
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            already revoked. This is just an asynchronous
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            notification so we can put the device asleep (in case
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            we didn't already notice the access revocation).
Zbigniew Jędrzejewski-Szmek 62fe94
+         *    "gone": This is like "force" but is sent if the device was
Zbigniew Jędrzejewski-Szmek 62fe94
+         *            paused due to a device-removal event.
Zbigniew Jędrzejewski-Szmek 62fe94
+         *
Zbigniew Jędrzejewski-Szmek 62fe94
+         * We always handle PauseDevice signals as "force" as we properly
Zbigniew Jędrzejewski-Szmek 62fe94
+         * support asynchronous access revocation, anyway. But in case logind
Zbigniew Jędrzejewski-Szmek 62fe94
+         * sent mode "pause", we also call PauseDeviceComplete() to immediately
Zbigniew Jędrzejewski-Szmek 62fe94
+         * acknowledge the request.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_read(signal, "uus", &major, &minor, &mode);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: erroneous PauseDevice signal",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          s->name, e->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* not our device? */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (makedev(major, minor) != em->devnum)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_pause(&em->evdev, true);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (streq(mode, "pause")) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                /*
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * Sending PauseDeviceComplete() is racy if logind triggers the
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * timeout. That is, if we take too long and logind pauses the
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * device by sending a forced PauseDevice, our
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * PauseDeviceComplete call will be stray. That's fine, though.
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * logind ignores such stray calls. Only if logind also sent a
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * further PauseDevice() signal, it might match our call
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * incorrectly to the newer PauseDevice(). That's fine, too, as
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * we handle that event asynchronously, anyway. Therefore,
Zbigniew Jędrzejewski-Szmek 62fe94
+                 * whatever happens, we're fine. Yay!
Zbigniew Jędrzejewski-Szmek 62fe94
+                 */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                r = sd_bus_message_new_method_call(c->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   &m,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   "org.freedesktop.login1",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   s->path,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   "org.freedesktop.login1.Session",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                                   "PauseDeviceComplete");
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r >= 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                        r = sd_bus_message_append(m, "uu", major, minor);
Zbigniew Jędrzejewski-Szmek 62fe94
+                        if (r >= 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                                r = sd_bus_send(c->sysbus, m, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+                }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+                if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                        log_debug("idev-evdev: %s/%s: cannot send PauseDeviceComplete: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                                  s->name, e->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_evdev_resume_device_fn(sd_bus *bus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          sd_bus_message *signal,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          void *userdata,
Zbigniew Jędrzejewski-Szmek 62fe94
+                                          sd_bus_error *ret_error) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_evdev *em = userdata;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e = &em->evdev.element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_session *s = e->session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        uint32_t major, minor;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r, fd;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /*
Zbigniew Jędrzejewski-Szmek 62fe94
+         * We get ResumeDevice signals whenever logind resumed a previously
Zbigniew Jędrzejewski-Szmek 62fe94
+         * paused device. The arguments contain the major/minor number of the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * related device and a new file-descriptor for the freshly opened
Zbigniew Jędrzejewski-Szmek 62fe94
+         * device-node.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * If the signal is not about our device, we simply ignore it.
Zbigniew Jędrzejewski-Szmek 62fe94
+         * Otherwise, we take the file-descriptor and immediately resume the
Zbigniew Jędrzejewski-Szmek 62fe94
+         * device.
Zbigniew Jędrzejewski-Szmek 62fe94
+         */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_message_read(signal, "uuh", &major, &minor, &fd;;
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: erroneous ResumeDevice signal",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          s->name, e->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        /* not our device? */
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (makedev(major, minor) != em->devnum)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (fd < 0) {
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: cannot duplicate evdev fd: %m",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          s->name, e->name);
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+        }
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = idev_evdev_resume(&em->evdev, fd);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                log_debug("idev-evdev: %s/%s: cannot resume: %s",
Zbigniew Jędrzejewski-Szmek 62fe94
+                          s->name, e->name, strerror(-r));
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_evdev_setup_bus(managed_evdev *em) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e = &em->evdev.element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_session *s = e->session;
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_context *c = s->context;
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_free_ char *match = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        match = strjoin("type='signal',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "sender='org.freedesktop.login1',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "interface='org.freedesktop.login1.Session',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "member='PauseDevice',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "path='", s->path, "'",
Zbigniew Jędrzejewski-Szmek 62fe94
+                        NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!match)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_add_match(c->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             &em->slot_pause_device,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             match,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             managed_evdev_pause_device_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             em);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(match);
Zbigniew Jędrzejewski-Szmek 62fe94
+        match = strjoin("type='signal',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "sender='org.freedesktop.login1',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "interface='org.freedesktop.login1.Session',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "member='ResumeDevice',"
Zbigniew Jędrzejewski-Szmek 62fe94
+                        "path='", s->path, "'",
Zbigniew Jędrzejewski-Szmek 62fe94
+                        NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!match)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = sd_bus_add_match(c->sysbus,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             &em->slot_resume_device,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             match,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             managed_evdev_resume_device_fn,
Zbigniew Jędrzejewski-Szmek 62fe94
+                             em);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static int managed_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        _cleanup_(idev_element_freep) idev_element *e = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        char name[IDEV_EVDEV_NAME_MAX];
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_evdev *em;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(s, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(s->context->sysbus, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(s->managed, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(s->context->sysbus, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(ud, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENODEV;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_name(name, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        em = new0(managed_evdev, 1);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!em)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return -ENOMEM;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        e = &em->evdev.element;
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->evdev = IDEV_EVDEV_INIT(&managed_evdev_vtable, s);
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->devnum = devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = managed_evdev_setup_bus(em);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = idev_element_add(e, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (out)
Zbigniew Jędrzejewski-Szmek 62fe94
+                *out = e;
Zbigniew Jędrzejewski-Szmek 62fe94
+        e = NULL;
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static void managed_evdev_free(idev_element *e) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        managed_evdev *em = managed_evdev_from_element(e);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->slot_resume_device = sd_bus_slot_unref(em->slot_resume_device);
Zbigniew Jędrzejewski-Szmek 62fe94
+        em->slot_pause_device = sd_bus_slot_unref(em->slot_pause_device);
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_destroy(&em->evdev);
Zbigniew Jędrzejewski-Szmek 62fe94
+        free(em);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+static const idev_element_vtable managed_evdev_vtable = {
Zbigniew Jędrzejewski-Szmek 62fe94
+        .free                   = managed_evdev_free,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .enable                 = managed_evdev_resume,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .disable                = managed_evdev_pause,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .open                   = managed_evdev_resume,
Zbigniew Jędrzejewski-Szmek 62fe94
+        .close                  = managed_evdev_pause,
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Generic Constructor
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Instead of relying on the caller to choose between managed and unmanaged
Zbigniew Jędrzejewski-Szmek 62fe94
+ * evdev devices, the idev_evdev_new() constructor does that for you (by
Zbigniew Jędrzejewski-Szmek 62fe94
+ * looking at s->managed).
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+bool idev_is_evdev(idev_element *e) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        return e && (e->vtable == &unmanaged_evdev_vtable ||
Zbigniew Jędrzejewski-Szmek 62fe94
+                     e->vtable == &managed_evdev_vtable);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+idev_element *idev_find_evdev(idev_session *s, dev_t devnum) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        char name[IDEV_EVDEV_NAME_MAX];
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(s, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(devnum != 0, NULL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_evdev_name(name, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+        return idev_find_element(s, name);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int idev_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(s, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(ud, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return s->managed ? managed_evdev_new(out, s, ud) : unmanaged_evdev_new(out, s, ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/idev-internal.h b/src/libsystemd-terminal/idev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
index bffefbb9c1..3301ebf6e4 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/src/libsystemd-terminal/idev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/idev-internal.h
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -22,6 +22,8 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 #pragma once
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libudev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <linux/input.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -37,6 +39,14 @@ typedef struct idev_element             idev_element;
Zbigniew Jędrzejewski-Szmek 62fe94
 typedef struct idev_element_vtable      idev_element_vtable;
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 /*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Evdev Elements
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+bool idev_is_evdev(idev_element *e);
Zbigniew Jędrzejewski-Szmek 62fe94
+idev_element *idev_find_evdev(idev_session *s, dev_t devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+int idev_evdev_new(idev_element **out, idev_session *s, struct udev_device *ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
  * Element Links
Zbigniew Jędrzejewski-Szmek 62fe94
  */
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/idev.c b/src/libsystemd-terminal/idev.c
Zbigniew Jędrzejewski-Szmek 62fe94
index 5e3080797a..2316a66529 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/src/libsystemd-terminal/idev.c
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/idev.c
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -20,6 +20,8 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 ***/
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libudev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <linux/input.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -31,6 +33,7 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "login-shared.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "macro.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "set.h"
Zbigniew Jędrzejewski-Szmek 62fe94
+#include "udev-util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 static void element_open(idev_element *e);
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -522,6 +525,51 @@ void idev_session_disable(idev_session *s) {
Zbigniew Jędrzejewski-Szmek 62fe94
         }
Zbigniew Jędrzejewski-Szmek 62fe94
 }
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
+int idev_session_add_evdev(idev_session *s, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+        int r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(s, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert_return(ud, -EINVAL);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        e = idev_find_evdev(s, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (e)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = idev_evdev_new(&e, s, ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r < 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        r = session_add_element(s, e);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (r != 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return r;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+int idev_session_remove_evdev(idev_session *s, struct udev_device *ud) {
Zbigniew Jędrzejewski-Szmek 62fe94
+        idev_element *e;
Zbigniew Jędrzejewski-Szmek 62fe94
+        dev_t devnum;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(s);
Zbigniew Jędrzejewski-Szmek 62fe94
+        assert(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        devnum = udev_device_get_devnum(ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (devnum == 0)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        e = idev_find_evdev(s, devnum);
Zbigniew Jędrzejewski-Szmek 62fe94
+        if (!e)
Zbigniew Jędrzejewski-Szmek 62fe94
+                return 0;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        return session_remove_element(s, e);
Zbigniew Jędrzejewski-Szmek 62fe94
+}
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
 /*
Zbigniew Jędrzejewski-Szmek 62fe94
  * Contexts
Zbigniew Jędrzejewski-Szmek 62fe94
  */
Zbigniew Jędrzejewski-Szmek 62fe94
diff --git a/src/libsystemd-terminal/idev.h b/src/libsystemd-terminal/idev.h
Zbigniew Jędrzejewski-Szmek 62fe94
index 6f618f37af..c98fb1bfb0 100644
Zbigniew Jędrzejewski-Szmek 62fe94
--- a/src/libsystemd-terminal/idev.h
Zbigniew Jędrzejewski-Szmek 62fe94
+++ b/src/libsystemd-terminal/idev.h
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -26,6 +26,8 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 #pragma once
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <inttypes.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <libudev.h>
Zbigniew Jędrzejewski-Szmek 62fe94
+#include <linux/input.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdbool.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <stdlib.h>
Zbigniew Jędrzejewski-Szmek 62fe94
 #include <systemd/sd-bus.h>
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -33,6 +35,7 @@
Zbigniew Jędrzejewski-Szmek 62fe94
 #include "util.h"
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 typedef struct idev_data                idev_data;
Zbigniew Jędrzejewski-Szmek 62fe94
+typedef struct idev_data_evdev          idev_data_evdev;
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 typedef struct idev_event               idev_event;
Zbigniew Jędrzejewski-Szmek 62fe94
 typedef struct idev_device              idev_device;
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -44,6 +47,7 @@ typedef struct idev_context             idev_context;
Zbigniew Jędrzejewski-Szmek 62fe94
  */
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 enum {
Zbigniew Jędrzejewski-Szmek 62fe94
+        IDEV_ELEMENT_EVDEV,
Zbigniew Jędrzejewski-Szmek 62fe94
         IDEV_ELEMENT_CNT
Zbigniew Jędrzejewski-Szmek 62fe94
 };
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -52,17 +56,30 @@ enum {
Zbigniew Jędrzejewski-Szmek 62fe94
 };
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 /*
Zbigniew Jędrzejewski-Szmek 62fe94
+ * Evdev Elements
Zbigniew Jędrzejewski-Szmek 62fe94
+ */
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+struct idev_data_evdev {
Zbigniew Jędrzejewski-Szmek 62fe94
+        struct input_event event;
Zbigniew Jędrzejewski-Szmek 62fe94
+};
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+/*
Zbigniew Jędrzejewski-Szmek 62fe94
  * Data Packets
Zbigniew Jędrzejewski-Szmek 62fe94
  */
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 enum {
Zbigniew Jędrzejewski-Szmek 62fe94
         IDEV_DATA_RESYNC,
Zbigniew Jędrzejewski-Szmek 62fe94
+        IDEV_DATA_EVDEV,
Zbigniew Jędrzejewski-Szmek 62fe94
         IDEV_DATA_CNT
Zbigniew Jędrzejewski-Szmek 62fe94
 };
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 struct idev_data {
Zbigniew Jędrzejewski-Szmek 62fe94
         unsigned int type;
Zbigniew Jędrzejewski-Szmek 62fe94
         bool resync : 1;
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
+        union {
Zbigniew Jędrzejewski-Szmek 62fe94
+                idev_data_evdev evdev;
Zbigniew Jędrzejewski-Szmek 62fe94
+        };
Zbigniew Jędrzejewski-Szmek 62fe94
 };
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
 /*
Zbigniew Jędrzejewski-Szmek 62fe94
@@ -122,6 +139,9 @@ bool idev_session_is_enabled(idev_session *s);
Zbigniew Jędrzejewski-Szmek 62fe94
 void idev_session_enable(idev_session *s);
Zbigniew Jędrzejewski-Szmek 62fe94
 void idev_session_disable(idev_session *s);
Zbigniew Jędrzejewski-Szmek 62fe94
 
Zbigniew Jędrzejewski-Szmek 62fe94
+int idev_session_add_evdev(idev_session *s, struct udev_device *ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+int idev_session_remove_evdev(idev_session *s, struct udev_device *ud);
Zbigniew Jędrzejewski-Szmek 62fe94
+
Zbigniew Jędrzejewski-Szmek 62fe94
 /*
Zbigniew Jędrzejewski-Szmek 62fe94
  * Contexts
Zbigniew Jędrzejewski-Szmek 62fe94
  */