]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-button.c
Merge pull request #1695 from evverx/fix-cap-bounding-merging
[thirdparty/systemd.git] / src / login / logind-button.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2012 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <sys/ioctl.h>
26 #include <unistd.h>
27 #include <linux/input.h>
28
29 #include "sd-messages.h"
30
31 #include "alloc-util.h"
32 #include "fd-util.h"
33 #include "logind-button.h"
34 #include "string-util.h"
35 #include "util.h"
36
37 Button* button_new(Manager *m, const char *name) {
38 Button *b;
39
40 assert(m);
41 assert(name);
42
43 b = new0(Button, 1);
44 if (!b)
45 return NULL;
46
47 b->name = strdup(name);
48 if (!b->name) {
49 free(b);
50 return NULL;
51 }
52
53 if (hashmap_put(m->buttons, b->name, b) < 0) {
54 free(b->name);
55 free(b);
56 return NULL;
57 }
58
59 b->manager = m;
60 b->fd = -1;
61
62 return b;
63 }
64
65 void button_free(Button *b) {
66 assert(b);
67
68 hashmap_remove(b->manager->buttons, b->name);
69
70 sd_event_source_unref(b->io_event_source);
71 sd_event_source_unref(b->check_event_source);
72
73 if (b->fd >= 0)
74 /* If the device has been unplugged close() returns
75 * ENODEV, let's ignore this, hence we don't use
76 * safe_close() */
77 (void) close(b->fd);
78
79 free(b->name);
80 free(b->seat);
81 free(b);
82 }
83
84 int button_set_seat(Button *b, const char *sn) {
85 char *s;
86
87 assert(b);
88 assert(sn);
89
90 s = strdup(sn);
91 if (!s)
92 return -ENOMEM;
93
94 free(b->seat);
95 b->seat = s;
96
97 return 0;
98 }
99
100 static void button_lid_switch_handle_action(Manager *manager, bool is_edge) {
101 HandleAction handle_action;
102
103 assert(manager);
104
105 /* If we are docked, handle the lid switch differently */
106 if (manager_is_docked_or_external_displays(manager))
107 handle_action = manager->handle_lid_switch_docked;
108 else
109 handle_action = manager->handle_lid_switch;
110
111 manager_handle_action(manager, INHIBIT_HANDLE_LID_SWITCH, handle_action, manager->lid_switch_ignore_inhibited, is_edge);
112 }
113
114 static int button_recheck(sd_event_source *e, void *userdata) {
115 Button *b = userdata;
116
117 assert(b);
118 assert(b->lid_closed);
119
120 button_lid_switch_handle_action(b->manager, false);
121 return 1;
122 }
123
124 static int button_install_check_event_source(Button *b) {
125 int r;
126 assert(b);
127
128 /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
129
130 if (b->check_event_source)
131 return 0;
132
133 r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b);
134 if (r < 0)
135 return r;
136
137 return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1);
138 }
139
140 static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
141 Button *b = userdata;
142 struct input_event ev;
143 ssize_t l;
144
145 assert(s);
146 assert(fd == b->fd);
147 assert(b);
148
149 l = read(b->fd, &ev, sizeof(ev));
150 if (l < 0)
151 return errno != EAGAIN ? -errno : 0;
152 if ((size_t) l < sizeof(ev))
153 return -EIO;
154
155 if (ev.type == EV_KEY && ev.value > 0) {
156
157 switch (ev.code) {
158
159 case KEY_POWER:
160 case KEY_POWER2:
161 log_struct(LOG_INFO,
162 LOG_MESSAGE("Power key pressed."),
163 LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY),
164 NULL);
165
166 manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
167 break;
168
169 /* The kernel is a bit confused here:
170
171 KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend"
172 KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
173 */
174
175 case KEY_SLEEP:
176 log_struct(LOG_INFO,
177 LOG_MESSAGE("Suspend key pressed."),
178 LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
179 NULL);
180
181 manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
182 break;
183
184 case KEY_SUSPEND:
185 log_struct(LOG_INFO,
186 LOG_MESSAGE("Hibernate key pressed."),
187 LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
188 NULL);
189
190 manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
191 break;
192 }
193
194 } else if (ev.type == EV_SW && ev.value > 0) {
195
196 if (ev.code == SW_LID) {
197 log_struct(LOG_INFO,
198 LOG_MESSAGE("Lid closed."),
199 LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
200 NULL);
201
202 b->lid_closed = true;
203 button_lid_switch_handle_action(b->manager, true);
204 button_install_check_event_source(b);
205
206 } else if (ev.code == SW_DOCK) {
207 log_struct(LOG_INFO,
208 LOG_MESSAGE("System docked."),
209 LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED),
210 NULL);
211
212 b->docked = true;
213 }
214
215 } else if (ev.type == EV_SW && ev.value == 0) {
216
217 if (ev.code == SW_LID) {
218 log_struct(LOG_INFO,
219 LOG_MESSAGE("Lid opened."),
220 LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED),
221 NULL);
222
223 b->lid_closed = false;
224 b->check_event_source = sd_event_source_unref(b->check_event_source);
225
226 } else if (ev.code == SW_DOCK) {
227 log_struct(LOG_INFO,
228 LOG_MESSAGE("System undocked."),
229 LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED),
230 NULL);
231
232 b->docked = false;
233 }
234 }
235
236 return 0;
237 }
238
239 int button_open(Button *b) {
240 char *p, name[256];
241 int r;
242
243 assert(b);
244
245 b->fd = safe_close(b->fd);
246
247 p = strjoina("/dev/input/", b->name);
248
249 b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
250 if (b->fd < 0)
251 return log_warning_errno(errno, "Failed to open %s: %m", b->name);
252
253 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
254 r = log_error_errno(errno, "Failed to get input name: %m");
255 goto fail;
256 }
257
258 r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
259 if (r < 0) {
260 log_error_errno(r, "Failed to add button event: %m");
261 goto fail;
262 }
263
264 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
265
266 return 0;
267
268 fail:
269 b->fd = safe_close(b->fd);
270 return r;
271 }
272
273 int button_check_switches(Button *b) {
274 uint8_t switches[SW_MAX/8+1] = {};
275 assert(b);
276
277 if (b->fd < 0)
278 return -EINVAL;
279
280 if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
281 return -errno;
282
283 b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
284 b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1;
285
286 if (b->lid_closed)
287 button_install_check_event_source(b);
288
289 return 0;
290 }