]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
logind: make use of EVIOCSMASK input ioctl to mask out events we aren't interested in
authorLennart Poettering <lennart@poettering.net>
Mon, 26 Jun 2017 19:35:12 +0000 (21:35 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 27 Jun 2017 17:25:46 +0000 (19:25 +0200)
This way logind will get woken up only when an actual event took place,
and not for every key press on the system.

The ioctl EVIOCSMASK was added by @dvdhrm already in October 2015, for
the use in logind, among others, hence let's actually make use of it
now.

While we are at it, also fix usage of the EVIOCGSW ioctl, where we
assumed a byte array, even though a unsigned long native endian array is
returned.

src/basic/missing.h
src/login/logind-button.c

index 55028754cdc0deb75f9bcccf3de183be0b1191a5..7c323c61cfab849b7b87e78a583faa5fffc0a318 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <linux/audit.h>
 #include <linux/capability.h>
 #include <linux/if_link.h>
@@ -569,6 +570,17 @@ struct btrfs_ioctl_quota_ctl_args {
 #  define EVIOCREVOKE _IOW('E', 0x91, int)
 #endif
 
+#ifndef EVIOCSMASK
+
+struct input_mask {
+        uint32_t type;
+        uint32_t codes_size;
+        uint64_t codes_ptr;
+};
+
+#define EVIOCSMASK _IOW('E', 0x93, struct input_mask)
+#endif
+
 #ifndef DRM_IOCTL_SET_MASTER
 #  define DRM_IOCTL_SET_MASTER _IO('d', 0x1e)
 #endif
index d739af8ea2c3f7c023a9455bbca318b9afa99384..c47c415e6076fa8f17c068c2dbcc9e642957c9b0 100644 (file)
 #include "string-util.h"
 #include "util.h"
 
+#define CONST_MAX4(a, b, c, d) CONST_MAX(CONST_MAX(a, b), CONST_MAX(c, d))
+
+#define ULONG_BITS (sizeof(unsigned long)*8)
+
+static bool bitset_get(const unsigned long *bits, unsigned i) {
+        return (bits[i / ULONG_BITS] >> (i % ULONG_BITS)) & 1UL;
+}
+
+static void bitset_put(unsigned long *bits, unsigned i) {
+        bits[i / ULONG_BITS] |= (unsigned long) 1 << (i % ULONG_BITS);
+}
+
 Button* button_new(Manager *m, const char *name) {
         Button *b;
 
@@ -231,6 +243,59 @@ static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *u
         return 0;
 }
 
+static int button_set_mask(Button *b) {
+        unsigned long
+                types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1] = {},
+                keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1] = {},
+                switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1] = {};
+        struct input_mask mask;
+
+        assert(b);
+        assert(b->fd >= 0);
+
+        bitset_put(types, EV_KEY);
+        bitset_put(types, EV_SW);
+
+        mask = (struct input_mask) {
+                .type = EV_SYN,
+                .codes_size = sizeof(types),
+                .codes_ptr = PTR_TO_UINT64(types),
+        };
+
+        if (ioctl(b->fd, EVIOCSMASK, &mask) < 0)
+                /* Log only at debug level if the kernel doesn't do EVIOCSMASK yet */
+                return log_full_errno(IN_SET(errno, ENOTTY, EOPNOTSUPP, EINVAL) ? LOG_DEBUG : LOG_WARNING,
+                                      errno, "Failed to set EV_SYN event mask on /dev/input/%s: %m", b->name);
+
+        bitset_put(keys, KEY_POWER);
+        bitset_put(keys, KEY_POWER2);
+        bitset_put(keys, KEY_SLEEP);
+        bitset_put(keys, KEY_SUSPEND);
+
+        mask = (struct input_mask) {
+                .type = EV_KEY,
+                .codes_size = sizeof(keys),
+                .codes_ptr = PTR_TO_UINT64(keys),
+        };
+
+        if (ioctl(b->fd, EVIOCSMASK, &mask) < 0)
+                return log_warning_errno(errno, "Failed to set EV_KEY event mask on /dev/input/%s: %m", b->name);
+
+        bitset_put(switches, SW_LID);
+        bitset_put(switches, SW_DOCK);
+
+        mask = (struct input_mask) {
+                .type = EV_SW,
+                .codes_size = sizeof(switches),
+                .codes_ptr = PTR_TO_UINT64(switches),
+        };
+
+        if (ioctl(b->fd, EVIOCSMASK, &mask) < 0)
+                return log_warning_errno(errno, "Failed to set EV_SW event mask on /dev/input/%s: %m", b->name);
+
+        return 0;
+}
+
 int button_open(Button *b) {
         char *p, name[256];
         int r;
@@ -250,6 +315,8 @@ int button_open(Button *b) {
                 goto fail;
         }
 
+        (void) button_set_mask(b);
+
         r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
         if (r < 0) {
                 log_error_errno(r, "Failed to add button event: %m");
@@ -266,7 +333,7 @@ fail:
 }
 
 int button_check_switches(Button *b) {
-        uint8_t switches[SW_MAX/8+1] = {};
+        unsigned long switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1] = {};
         assert(b);
 
         if (b->fd < 0)
@@ -275,8 +342,8 @@ int button_check_switches(Button *b) {
         if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
                 return -errno;
 
-        b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
-        b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1;
+        b->lid_closed = bitset_get(switches, SW_LID);
+        b->docked = bitset_get(switches, SW_DOCK);
 
         if (b->lid_closed)
                 button_install_check_event_source(b);