]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-button.c
Suspend on lid close based on power status. (#8016)
[thirdparty/systemd.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 }