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