]> git.proxmox.com Git - systemd.git/blame - src/login/logind-button.c
Imported Upstream version 217
[systemd.git] / src / login / logind-button.c
CommitLineData
663996b3
MS
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 <assert.h>
23#include <string.h>
24#include <errno.h>
25#include <fcntl.h>
26#include <sys/ioctl.h>
27#include <unistd.h>
28#include <linux/input.h>
663996b3 29
60f067b4 30#include "sd-messages.h"
663996b3
MS
31#include "conf-parser.h"
32#include "util.h"
663996b3 33#include "special.h"
60f067b4 34#include "logind-button.h"
663996b3
MS
35
36Button* button_new(Manager *m, const char *name) {
37 Button *b;
38
39 assert(m);
40 assert(name);
41
42 b = new0(Button, 1);
43 if (!b)
44 return NULL;
45
46 b->name = strdup(name);
47 if (!b->name) {
48 free(b);
49 return NULL;
50 }
51
52 if (hashmap_put(m->buttons, b->name, b) < 0) {
53 free(b->name);
54 free(b);
55 return NULL;
56 }
57
58 b->manager = m;
59 b->fd = -1;
60
61 return b;
62}
63
64void button_free(Button *b) {
65 assert(b);
66
67 hashmap_remove(b->manager->buttons, b->name);
68
60f067b4
JS
69 sd_event_source_unref(b->io_event_source);
70 sd_event_source_unref(b->check_event_source);
663996b3 71
60f067b4 72 if (b->fd >= 0) {
663996b3
MS
73 /* If the device has been unplugged close() returns
74 * ENODEV, let's ignore this, hence we don't use
60f067b4 75 * safe_close() */
663996b3
MS
76 close(b->fd);
77 }
78
79 free(b->name);
80 free(b->seat);
81 free(b);
82}
83
84int 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
5eef597e
MP
100static 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_multiple_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
60f067b4
JS
114static int button_recheck(sd_event_source *e, void *userdata) {
115 Button *b = userdata;
663996b3
MS
116
117 assert(b);
60f067b4 118 assert(b->lid_closed);
663996b3 119
5eef597e 120 button_lid_switch_handle_action(b->manager, false);
60f067b4 121 return 1;
663996b3
MS
122}
123
60f067b4 124static int button_install_check_event_source(Button *b) {
663996b3 125 int r;
663996b3
MS
126 assert(b);
127
60f067b4 128 /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
663996b3 129
60f067b4
JS
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);
663996b3
MS
138}
139
60f067b4
JS
140static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
141 Button *b = userdata;
663996b3
MS
142 struct input_event ev;
143 ssize_t l;
144
60f067b4
JS
145 assert(s);
146 assert(fd == b->fd);
663996b3
MS
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 "MESSAGE=Power key pressed.",
163 MESSAGE_ID(SD_MESSAGE_POWER_KEY),
164 NULL);
60f067b4
JS
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;
663996b3
MS
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 "MESSAGE=Suspend key pressed.",
178 MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
179 NULL);
60f067b4
JS
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;
663996b3
MS
183
184 case KEY_SUSPEND:
185 log_struct(LOG_INFO,
186 "MESSAGE=Hibernate key pressed.",
187 MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
188 NULL);
60f067b4
JS
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;
663996b3
MS
192 }
193
194 } else if (ev.type == EV_SW && ev.value > 0) {
195
60f067b4 196 if (ev.code == SW_LID) {
663996b3
MS
197 log_struct(LOG_INFO,
198 "MESSAGE=Lid closed.",
199 MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
200 NULL);
663996b3 201
60f067b4 202 b->lid_closed = true;
5eef597e 203 button_lid_switch_handle_action(b->manager, true);
60f067b4
JS
204 button_install_check_event_source(b);
205
206 } else if (ev.code == SW_DOCK) {
207 log_struct(LOG_INFO,
208 "MESSAGE=System docked.",
209 MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED),
210 NULL);
211
212 b->docked = true;
663996b3
MS
213 }
214
215 } else if (ev.type == EV_SW && ev.value == 0) {
216
60f067b4 217 if (ev.code == SW_LID) {
663996b3
MS
218 log_struct(LOG_INFO,
219 "MESSAGE=Lid opened.",
220 MESSAGE_ID(SD_MESSAGE_LID_OPENED),
221 NULL);
60f067b4
JS
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 "MESSAGE=System undocked.",
229 MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED),
230 NULL);
231
232 b->docked = false;
663996b3
MS
233 }
234 }
235
236 return 0;
237}
238
60f067b4
JS
239int button_open(Button *b) {
240 char *p, name[256];
241 int r;
242
663996b3
MS
243 assert(b);
244
60f067b4
JS
245 if (b->fd >= 0) {
246 close(b->fd);
247 b->fd = -1;
248 }
249
250 p = strappenda("/dev/input/", b->name);
251
252 b->fd = open(p, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
253 if (b->fd < 0) {
254 log_warning("Failed to open %s: %m", b->name);
255 return -errno;
256 }
663996b3 257
60f067b4
JS
258 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
259 log_error("Failed to get input name: %m");
260 r = -errno;
261 goto fail;
262 }
263
264 r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
265 if (r < 0) {
266 log_error("Failed to add button event: %s", strerror(-r));
267 goto fail;
268 }
269
270 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
271
272 return 0;
273
274fail:
275 close(b->fd);
276 b->fd = -1;
277 return r;
278}
279
280int button_check_switches(Button *b) {
281 uint8_t switches[SW_MAX/8+1] = {};
282 assert(b);
283
284 if (b->fd < 0)
285 return -EINVAL;
286
287 if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
288 return -errno;
289
290 b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
291 b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1;
292
293 if (b->lid_closed)
294 button_install_check_event_source(b);
295
296 return 0;
663996b3 297}