LCOV - code coverage report
Current view: top level - login - logind-button.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 187 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 13 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 162 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <errno.h>
       4                 :            : #include <fcntl.h>
       5                 :            : #include <string.h>
       6                 :            : #include <sys/ioctl.h>
       7                 :            : #include <unistd.h>
       8                 :            : #include <linux/input.h>
       9                 :            : 
      10                 :            : #include "sd-messages.h"
      11                 :            : 
      12                 :            : #include "alloc-util.h"
      13                 :            : #include "fd-util.h"
      14                 :            : #include "logind-button.h"
      15                 :            : #include "missing_input.h"
      16                 :            : #include "string-util.h"
      17                 :            : #include "util.h"
      18                 :            : 
      19                 :            : #define CONST_MAX4(a, b, c, d) CONST_MAX(CONST_MAX(a, b), CONST_MAX(c, d))
      20                 :            : 
      21                 :            : #define ULONG_BITS (sizeof(unsigned long)*8)
      22                 :            : 
      23                 :          0 : static bool bitset_get(const unsigned long *bits, unsigned i) {
      24                 :          0 :         return (bits[i / ULONG_BITS] >> (i % ULONG_BITS)) & 1UL;
      25                 :            : }
      26                 :            : 
      27                 :          0 : static void bitset_put(unsigned long *bits, unsigned i) {
      28                 :          0 :         bits[i / ULONG_BITS] |= (unsigned long) 1 << (i % ULONG_BITS);
      29                 :          0 : }
      30                 :            : 
      31                 :          0 : Button* button_new(Manager *m, const char *name) {
      32                 :            :         Button *b;
      33                 :            : 
      34         [ #  # ]:          0 :         assert(m);
      35         [ #  # ]:          0 :         assert(name);
      36                 :            : 
      37                 :          0 :         b = new0(Button, 1);
      38         [ #  # ]:          0 :         if (!b)
      39                 :          0 :                 return NULL;
      40                 :            : 
      41                 :          0 :         b->name = strdup(name);
      42         [ #  # ]:          0 :         if (!b->name)
      43                 :          0 :                 return mfree(b);
      44                 :            : 
      45         [ #  # ]:          0 :         if (hashmap_put(m->buttons, b->name, b) < 0) {
      46                 :          0 :                 free(b->name);
      47                 :          0 :                 return mfree(b);
      48                 :            :         }
      49                 :            : 
      50                 :          0 :         b->manager = m;
      51                 :          0 :         b->fd = -1;
      52                 :            : 
      53                 :          0 :         return b;
      54                 :            : }
      55                 :            : 
      56                 :          0 : void button_free(Button *b) {
      57         [ #  # ]:          0 :         assert(b);
      58                 :            : 
      59                 :          0 :         hashmap_remove(b->manager->buttons, b->name);
      60                 :            : 
      61                 :          0 :         sd_event_source_unref(b->io_event_source);
      62                 :          0 :         sd_event_source_unref(b->check_event_source);
      63                 :            : 
      64         [ #  # ]:          0 :         if (b->fd >= 0)
      65                 :            :                 /* If the device has been unplugged close() returns
      66                 :            :                  * ENODEV, let's ignore this, hence we don't use
      67                 :            :                  * safe_close() */
      68                 :          0 :                 (void) close(b->fd);
      69                 :            : 
      70                 :          0 :         free(b->name);
      71                 :          0 :         free(b->seat);
      72                 :          0 :         free(b);
      73                 :          0 : }
      74                 :            : 
      75                 :          0 : int button_set_seat(Button *b, const char *sn) {
      76                 :            :         char *s;
      77                 :            : 
      78         [ #  # ]:          0 :         assert(b);
      79         [ #  # ]:          0 :         assert(sn);
      80                 :            : 
      81                 :          0 :         s = strdup(sn);
      82         [ #  # ]:          0 :         if (!s)
      83                 :          0 :                 return -ENOMEM;
      84                 :            : 
      85                 :          0 :         free(b->seat);
      86                 :          0 :         b->seat = s;
      87                 :            : 
      88                 :          0 :         return 0;
      89                 :            : }
      90                 :            : 
      91                 :          0 : static void button_lid_switch_handle_action(Manager *manager, bool is_edge) {
      92                 :            :         HandleAction handle_action;
      93                 :            : 
      94         [ #  # ]:          0 :         assert(manager);
      95                 :            : 
      96                 :            :         /* If we are docked or on external power, handle the lid switch
      97                 :            :          * differently */
      98         [ #  # ]:          0 :         if (manager_is_docked_or_external_displays(manager))
      99                 :          0 :                 handle_action = manager->handle_lid_switch_docked;
     100   [ #  #  #  # ]:          0 :         else if (manager->handle_lid_switch_ep != _HANDLE_ACTION_INVALID &&
     101                 :          0 :                  manager_is_on_external_power())
     102                 :          0 :                 handle_action = manager->handle_lid_switch_ep;
     103                 :            :         else
     104                 :          0 :                 handle_action = manager->handle_lid_switch;
     105                 :            : 
     106                 :          0 :         manager_handle_action(manager, INHIBIT_HANDLE_LID_SWITCH, handle_action, manager->lid_switch_ignore_inhibited, is_edge);
     107                 :          0 : }
     108                 :            : 
     109                 :          0 : static int button_recheck(sd_event_source *e, void *userdata) {
     110                 :          0 :         Button *b = userdata;
     111                 :            : 
     112         [ #  # ]:          0 :         assert(b);
     113         [ #  # ]:          0 :         assert(b->lid_closed);
     114                 :            : 
     115                 :          0 :         button_lid_switch_handle_action(b->manager, false);
     116                 :          0 :         return 1;
     117                 :            : }
     118                 :            : 
     119                 :          0 : static int button_install_check_event_source(Button *b) {
     120                 :            :         int r;
     121         [ #  # ]:          0 :         assert(b);
     122                 :            : 
     123                 :            :         /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
     124                 :            : 
     125         [ #  # ]:          0 :         if (b->check_event_source)
     126                 :          0 :                 return 0;
     127                 :            : 
     128                 :          0 :         r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b);
     129         [ #  # ]:          0 :         if (r < 0)
     130                 :          0 :                 return r;
     131                 :            : 
     132                 :          0 :         return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1);
     133                 :            : }
     134                 :            : 
     135                 :          0 : static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     136                 :          0 :         Button *b = userdata;
     137                 :            :         struct input_event ev;
     138                 :            :         ssize_t l;
     139                 :            : 
     140         [ #  # ]:          0 :         assert(s);
     141         [ #  # ]:          0 :         assert(fd == b->fd);
     142         [ #  # ]:          0 :         assert(b);
     143                 :            : 
     144                 :          0 :         l = read(b->fd, &ev, sizeof(ev));
     145         [ #  # ]:          0 :         if (l < 0)
     146         [ #  # ]:          0 :                 return errno != EAGAIN ? -errno : 0;
     147         [ #  # ]:          0 :         if ((size_t) l < sizeof(ev))
     148                 :          0 :                 return -EIO;
     149                 :            : 
     150   [ #  #  #  # ]:          0 :         if (ev.type == EV_KEY && ev.value > 0) {
     151                 :            : 
     152   [ #  #  #  # ]:          0 :                 switch (ev.code) {
     153                 :            : 
     154                 :          0 :                 case KEY_POWER:
     155                 :            :                 case KEY_POWER2:
     156                 :          0 :                         log_struct(LOG_INFO,
     157                 :            :                                    LOG_MESSAGE("Power key pressed."),
     158                 :            :                                    "MESSAGE_ID=" SD_MESSAGE_POWER_KEY_STR);
     159                 :            : 
     160                 :          0 :                         manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
     161                 :          0 :                         break;
     162                 :            : 
     163                 :            :                 /* The kernel is a bit confused here:
     164                 :            : 
     165                 :            :                    KEY_SLEEP   = suspend-to-ram, which everybody else calls "suspend"
     166                 :            :                    KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
     167                 :            :                 */
     168                 :            : 
     169                 :          0 :                 case KEY_SLEEP:
     170                 :          0 :                         log_struct(LOG_INFO,
     171                 :            :                                    LOG_MESSAGE("Suspend key pressed."),
     172                 :            :                                    "MESSAGE_ID=" SD_MESSAGE_SUSPEND_KEY_STR);
     173                 :            : 
     174                 :          0 :                         manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
     175                 :          0 :                         break;
     176                 :            : 
     177                 :          0 :                 case KEY_SUSPEND:
     178                 :          0 :                         log_struct(LOG_INFO,
     179                 :            :                                    LOG_MESSAGE("Hibernate key pressed."),
     180                 :            :                                    "MESSAGE_ID=" SD_MESSAGE_HIBERNATE_KEY_STR);
     181                 :            : 
     182                 :          0 :                         manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
     183                 :          0 :                         break;
     184                 :            :                 }
     185                 :            : 
     186   [ #  #  #  # ]:          0 :         } else if (ev.type == EV_SW && ev.value > 0) {
     187                 :            : 
     188         [ #  # ]:          0 :                 if (ev.code == SW_LID) {
     189                 :          0 :                         log_struct(LOG_INFO,
     190                 :            :                                    LOG_MESSAGE("Lid closed."),
     191                 :            :                                    "MESSAGE_ID=" SD_MESSAGE_LID_CLOSED_STR);
     192                 :            : 
     193                 :          0 :                         b->lid_closed = true;
     194                 :          0 :                         button_lid_switch_handle_action(b->manager, true);
     195                 :          0 :                         button_install_check_event_source(b);
     196                 :            : 
     197         [ #  # ]:          0 :                 } else if (ev.code == SW_DOCK) {
     198                 :          0 :                         log_struct(LOG_INFO,
     199                 :            :                                    LOG_MESSAGE("System docked."),
     200                 :            :                                    "MESSAGE_ID=" SD_MESSAGE_SYSTEM_DOCKED_STR);
     201                 :            : 
     202                 :          0 :                         b->docked = true;
     203                 :            :                 }
     204                 :            : 
     205   [ #  #  #  # ]:          0 :         } else if (ev.type == EV_SW && ev.value == 0) {
     206                 :            : 
     207         [ #  # ]:          0 :                 if (ev.code == SW_LID) {
     208                 :          0 :                         log_struct(LOG_INFO,
     209                 :            :                                    LOG_MESSAGE("Lid opened."),
     210                 :            :                                    "MESSAGE_ID=" SD_MESSAGE_LID_OPENED_STR);
     211                 :            : 
     212                 :          0 :                         b->lid_closed = false;
     213                 :          0 :                         b->check_event_source = sd_event_source_unref(b->check_event_source);
     214                 :            : 
     215         [ #  # ]:          0 :                 } else if (ev.code == SW_DOCK) {
     216                 :          0 :                         log_struct(LOG_INFO,
     217                 :            :                                    LOG_MESSAGE("System undocked."),
     218                 :            :                                    "MESSAGE_ID=" SD_MESSAGE_SYSTEM_UNDOCKED_STR);
     219                 :            : 
     220                 :          0 :                         b->docked = false;
     221                 :            :                 }
     222                 :            :         }
     223                 :            : 
     224                 :          0 :         return 0;
     225                 :            : }
     226                 :            : 
     227                 :          0 : static int button_suitable(int fd) {
     228                 :            :         unsigned long types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1];
     229                 :            : 
     230         [ #  # ]:          0 :         assert(fd >= 0);
     231                 :            : 
     232         [ #  # ]:          0 :         if (ioctl(fd, EVIOCGBIT(EV_SYN, sizeof types), types) < 0)
     233                 :          0 :                 return -errno;
     234                 :            : 
     235         [ #  # ]:          0 :         if (bitset_get(types, EV_KEY)) {
     236                 :            :                 unsigned long keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1];
     237                 :            : 
     238         [ #  # ]:          0 :                 if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof keys), keys) < 0)
     239                 :          0 :                         return -errno;
     240                 :            : 
     241   [ #  #  #  # ]:          0 :                 if (bitset_get(keys, KEY_POWER) ||
     242         [ #  # ]:          0 :                     bitset_get(keys, KEY_POWER2) ||
     243         [ #  # ]:          0 :                     bitset_get(keys, KEY_SLEEP) ||
     244                 :          0 :                     bitset_get(keys, KEY_SUSPEND))
     245                 :          0 :                         return true;
     246                 :            :         }
     247                 :            : 
     248         [ #  # ]:          0 :         if (bitset_get(types, EV_SW)) {
     249                 :            :                 unsigned long switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1];
     250                 :            : 
     251         [ #  # ]:          0 :                 if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof switches), switches) < 0)
     252                 :          0 :                         return -errno;
     253                 :            : 
     254   [ #  #  #  # ]:          0 :                 if (bitset_get(switches, SW_LID) ||
     255                 :          0 :                     bitset_get(switches, SW_DOCK))
     256                 :          0 :                         return true;
     257                 :            :         }
     258                 :            : 
     259                 :          0 :         return false;
     260                 :            : }
     261                 :            : 
     262                 :          0 : static int button_set_mask(const char *name, int fd) {
     263                 :            :         unsigned long
     264                 :          0 :                 types[CONST_MAX(EV_KEY, EV_SW)/ULONG_BITS+1] = {},
     265                 :          0 :                 keys[CONST_MAX4(KEY_POWER, KEY_POWER2, KEY_SLEEP, KEY_SUSPEND)/ULONG_BITS+1] = {},
     266                 :          0 :                 switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1] = {};
     267                 :            :         struct input_mask mask;
     268                 :            : 
     269         [ #  # ]:          0 :         assert(name);
     270         [ #  # ]:          0 :         assert(fd >= 0);
     271                 :            : 
     272                 :          0 :         bitset_put(types, EV_KEY);
     273                 :          0 :         bitset_put(types, EV_SW);
     274                 :            : 
     275                 :          0 :         mask = (struct input_mask) {
     276                 :            :                 .type = EV_SYN,
     277                 :            :                 .codes_size = sizeof(types),
     278                 :          0 :                 .codes_ptr = PTR_TO_UINT64(types),
     279                 :            :         };
     280                 :            : 
     281         [ #  # ]:          0 :         if (ioctl(fd, EVIOCSMASK, &mask) < 0)
     282                 :            :                 /* Log only at debug level if the kernel doesn't do EVIOCSMASK yet */
     283   [ #  #  #  #  :          0 :                 return log_full_errno(IN_SET(errno, ENOTTY, EOPNOTSUPP, EINVAL) ? LOG_DEBUG : LOG_WARNING,
                   #  # ]
     284                 :            :                                       errno, "Failed to set EV_SYN event mask on /dev/input/%s: %m", name);
     285                 :            : 
     286                 :          0 :         bitset_put(keys, KEY_POWER);
     287                 :          0 :         bitset_put(keys, KEY_POWER2);
     288                 :          0 :         bitset_put(keys, KEY_SLEEP);
     289                 :          0 :         bitset_put(keys, KEY_SUSPEND);
     290                 :            : 
     291                 :          0 :         mask = (struct input_mask) {
     292                 :            :                 .type = EV_KEY,
     293                 :            :                 .codes_size = sizeof(keys),
     294                 :          0 :                 .codes_ptr = PTR_TO_UINT64(keys),
     295                 :            :         };
     296                 :            : 
     297         [ #  # ]:          0 :         if (ioctl(fd, EVIOCSMASK, &mask) < 0)
     298         [ #  # ]:          0 :                 return log_warning_errno(errno, "Failed to set EV_KEY event mask on /dev/input/%s: %m", name);
     299                 :            : 
     300                 :          0 :         bitset_put(switches, SW_LID);
     301                 :          0 :         bitset_put(switches, SW_DOCK);
     302                 :            : 
     303                 :          0 :         mask = (struct input_mask) {
     304                 :            :                 .type = EV_SW,
     305                 :            :                 .codes_size = sizeof(switches),
     306                 :          0 :                 .codes_ptr = PTR_TO_UINT64(switches),
     307                 :            :         };
     308                 :            : 
     309         [ #  # ]:          0 :         if (ioctl(fd, EVIOCSMASK, &mask) < 0)
     310         [ #  # ]:          0 :                 return log_warning_errno(errno, "Failed to set EV_SW event mask on /dev/input/%s: %m", name);
     311                 :            : 
     312                 :          0 :         return 0;
     313                 :            : }
     314                 :            : 
     315                 :          0 : int button_open(Button *b) {
     316                 :          0 :         _cleanup_close_ int fd = -1;
     317                 :            :         const char *p;
     318                 :            :         char name[256];
     319                 :            :         int r;
     320                 :            : 
     321         [ #  # ]:          0 :         assert(b);
     322                 :            : 
     323                 :          0 :         b->fd = safe_close(b->fd);
     324                 :            : 
     325   [ #  #  #  #  :          0 :         p = strjoina("/dev/input/", b->name);
          #  #  #  #  #  
                #  #  # ]
     326                 :            : 
     327                 :          0 :         fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
     328         [ #  # ]:          0 :         if (fd < 0)
     329         [ #  # ]:          0 :                 return log_warning_errno(errno, "Failed to open %s: %m", p);
     330                 :            : 
     331                 :          0 :         r = button_suitable(fd);
     332         [ #  # ]:          0 :         if (r < 0)
     333         [ #  # ]:          0 :                 return log_warning_errno(r, "Failed to determine whether input device %s is relevant to us: %m", p);
     334         [ #  # ]:          0 :         if (r == 0)
     335         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL),
     336                 :            :                                        "Device %s does not expose keys or switches relevant to us, ignoring.", p);
     337                 :            : 
     338         [ #  # ]:          0 :         if (ioctl(fd, EVIOCGNAME(sizeof name), name) < 0)
     339         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to get input name for %s: %m", p);
     340                 :            : 
     341                 :          0 :         (void) button_set_mask(b->name, fd);
     342                 :            : 
     343                 :          0 :         b->io_event_source = sd_event_source_unref(b->io_event_source);
     344                 :          0 :         r = sd_event_add_io(b->manager->event, &b->io_event_source, fd, EPOLLIN, button_dispatch, b);
     345         [ #  # ]:          0 :         if (r < 0)
     346         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to add button event for %s: %m", p);
     347                 :            : 
     348                 :          0 :         b->fd = TAKE_FD(fd);
     349         [ #  # ]:          0 :         log_info("Watching system buttons on %s (%s)", p, name);
     350                 :          0 :         return 0;
     351                 :            : }
     352                 :            : 
     353                 :          0 : int button_check_switches(Button *b) {
     354                 :          0 :         unsigned long switches[CONST_MAX(SW_LID, SW_DOCK)/ULONG_BITS+1] = {};
     355         [ #  # ]:          0 :         assert(b);
     356                 :            : 
     357         [ #  # ]:          0 :         if (b->fd < 0)
     358                 :          0 :                 return -EINVAL;
     359                 :            : 
     360         [ #  # ]:          0 :         if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
     361                 :          0 :                 return -errno;
     362                 :            : 
     363                 :          0 :         b->lid_closed = bitset_get(switches, SW_LID);
     364                 :          0 :         b->docked = bitset_get(switches, SW_DOCK);
     365                 :            : 
     366         [ #  # ]:          0 :         if (b->lid_closed)
     367                 :          0 :                 button_install_check_event_source(b);
     368                 :            : 
     369                 :          0 :         return 0;
     370                 :            : }

Generated by: LCOV version 1.14