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