]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-button.c
Merge pull request #259 from poettering/logind-label-fix
[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 <string.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <sys/ioctl.h>
26 #include <unistd.h>
27 #include <linux/input.h>
28
29 #include "sd-messages.h"
30 #include "util.h"
31 #include "logind-button.h"
32
33 Button* button_new(Manager *m, const char *name) {
34 Button *b;
35
36 assert(m);
37 assert(name);
38
39 b = new0(Button, 1);
40 if (!b)
41 return NULL;
42
43 b->name = strdup(name);
44 if (!b->name) {
45 free(b);
46 return NULL;
47 }
48
49 if (hashmap_put(m->buttons, b->name, b) < 0) {
50 free(b->name);
51 free(b);
52 return NULL;
53 }
54
55 b->manager = m;
56 b->fd = -1;
57
58 return b;
59 }
60
61 void button_free(Button *b) {
62 assert(b);
63
64 hashmap_remove(b->manager->buttons, b->name);
65
66 sd_event_source_unref(b->io_event_source);
67 sd_event_source_unref(b->check_event_source);
68
69 if (b->fd >= 0) {
70 /* If the device has been unplugged close() returns
71 * ENODEV, let's ignore this, hence we don't use
72 * safe_close() */
73 (void) close(b->fd);
74 }
75
76 free(b->name);
77 free(b->seat);
78 free(b);
79 }
80
81 int button_set_seat(Button *b, const char *sn) {
82 char *s;
83
84 assert(b);
85 assert(sn);
86
87 s = strdup(sn);
88 if (!s)
89 return -ENOMEM;
90
91 free(b->seat);
92 b->seat = s;
93
94 return 0;
95 }
96
97 static void button_lid_switch_handle_action(Manager *manager, bool is_edge) {
98 HandleAction handle_action;
99
100 assert(manager);
101
102 /* If we are docked, handle the lid switch differently */
103 if (manager_is_docked_or_external_displays(manager))
104 handle_action = manager->handle_lid_switch_docked;
105 else
106 handle_action = manager->handle_lid_switch;
107
108 manager_handle_action(manager, INHIBIT_HANDLE_LID_SWITCH, handle_action, manager->lid_switch_ignore_inhibited, is_edge);
109 }
110
111 static int button_recheck(sd_event_source *e, void *userdata) {
112 Button *b = userdata;
113
114 assert(b);
115 assert(b->lid_closed);
116
117 button_lid_switch_handle_action(b->manager, false);
118 return 1;
119 }
120
121 static int button_install_check_event_source(Button *b) {
122 int r;
123 assert(b);
124
125 /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
126
127 if (b->check_event_source)
128 return 0;
129
130 r = sd_event_add_post(b->manager->event, &b->check_event_source, button_recheck, b);
131 if (r < 0)
132 return r;
133
134 return sd_event_source_set_priority(b->check_event_source, SD_EVENT_PRIORITY_IDLE+1);
135 }
136
137 static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
138 Button *b = userdata;
139 struct input_event ev;
140 ssize_t l;
141
142 assert(s);
143 assert(fd == b->fd);
144 assert(b);
145
146 l = read(b->fd, &ev, sizeof(ev));
147 if (l < 0)
148 return errno != EAGAIN ? -errno : 0;
149 if ((size_t) l < sizeof(ev))
150 return -EIO;
151
152 if (ev.type == EV_KEY && ev.value > 0) {
153
154 switch (ev.code) {
155
156 case KEY_POWER:
157 case KEY_POWER2:
158 log_struct(LOG_INFO,
159 LOG_MESSAGE("Power key pressed."),
160 LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY),
161 NULL);
162
163 manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
164 break;
165
166 /* The kernel is a bit confused here:
167
168 KEY_SLEEP = suspend-to-ram, which everybody else calls "suspend"
169 KEY_SUSPEND = suspend-to-disk, which everybody else calls "hibernate"
170 */
171
172 case KEY_SLEEP:
173 log_struct(LOG_INFO,
174 LOG_MESSAGE("Suspend key pressed."),
175 LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
176 NULL);
177
178 manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
179 break;
180
181 case KEY_SUSPEND:
182 log_struct(LOG_INFO,
183 LOG_MESSAGE("Hibernate key pressed."),
184 LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
185 NULL);
186
187 manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
188 break;
189 }
190
191 } else if (ev.type == EV_SW && ev.value > 0) {
192
193 if (ev.code == SW_LID) {
194 log_struct(LOG_INFO,
195 LOG_MESSAGE("Lid closed."),
196 LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
197 NULL);
198
199 b->lid_closed = true;
200 button_lid_switch_handle_action(b->manager, true);
201 button_install_check_event_source(b);
202
203 } else if (ev.code == SW_DOCK) {
204 log_struct(LOG_INFO,
205 LOG_MESSAGE("System docked."),
206 LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED),
207 NULL);
208
209 b->docked = true;
210 }
211
212 } else if (ev.type == EV_SW && ev.value == 0) {
213
214 if (ev.code == SW_LID) {
215 log_struct(LOG_INFO,
216 LOG_MESSAGE("Lid opened."),
217 LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED),
218 NULL);
219
220 b->lid_closed = false;
221 b->check_event_source = sd_event_source_unref(b->check_event_source);
222
223 } else if (ev.code == SW_DOCK) {
224 log_struct(LOG_INFO,
225 LOG_MESSAGE("System undocked."),
226 LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED),
227 NULL);
228
229 b->docked = false;
230 }
231 }
232
233 return 0;
234 }
235
236 int button_open(Button *b) {
237 char *p, name[256];
238 int r;
239
240 assert(b);
241
242 if (b->fd >= 0) {
243 close(b->fd);
244 b->fd = -1;
245 }
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 log_error_errno(errno, "Failed to get input name: %m");
255 r = -errno;
256 goto fail;
257 }
258
259 r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
260 if (r < 0) {
261 log_error_errno(r, "Failed to add button event: %m");
262 goto fail;
263 }
264
265 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
266
267 return 0;
268
269 fail:
270 close(b->fd);
271 b->fd = -1;
272 return r;
273 }
274
275 int button_check_switches(Button *b) {
276 uint8_t switches[SW_MAX/8+1] = {};
277 assert(b);
278
279 if (b->fd < 0)
280 return -EINVAL;
281
282 if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
283 return -errno;
284
285 b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
286 b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1;
287
288 if (b->lid_closed)
289 button_install_check_event_source(b);
290
291 return 0;
292 }