]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-button.c
treewide: use log_*_errno whenever %m is in the format string
[thirdparty/systemd.git] / src / login / logind-button.c
CommitLineData
e9d21f24
LP
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>
e9d21f24 29
cc377381 30#include "sd-messages.h"
e9d21f24
LP
31#include "conf-parser.h"
32#include "util.h"
e9d21f24 33#include "special.h"
cc377381 34#include "logind-button.h"
e9d21f24
LP
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
ed4ba7e4
LP
69 sd_event_source_unref(b->io_event_source);
70 sd_event_source_unref(b->check_event_source);
c30a0c62 71
cc377381 72 if (b->fd >= 0) {
c30a0c62
LP
73 /* If the device has been unplugged close() returns
74 * ENODEV, let's ignore this, hence we don't use
03e334a1 75 * safe_close() */
c30a0c62 76 close(b->fd);
e9d21f24
LP
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
3c56cab4
BW
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
ed4ba7e4
LP
114static int button_recheck(sd_event_source *e, void *userdata) {
115 Button *b = userdata;
beaafb2e 116
ed4ba7e4
LP
117 assert(b);
118 assert(b->lid_closed);
119
3c56cab4 120 button_lid_switch_handle_action(b->manager, false);
ed4ba7e4
LP
121 return 1;
122}
e9d21f24 123
ed4ba7e4
LP
124static int button_install_check_event_source(Button *b) {
125 int r;
e9d21f24
LP
126 assert(b);
127
ed4ba7e4 128 /* Install a post handler, so that we keep rechecking as long as the lid is closed. */
6de0e0e5 129
ed4ba7e4
LP
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);
e9d21f24
LP
138}
139
cc377381
LP
140static int button_dispatch(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
141 Button *b = userdata;
e9d21f24
LP
142 struct input_event ev;
143 ssize_t l;
144
cc377381
LP
145 assert(s);
146 assert(fd == b->fd);
e9d21f24
LP
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
e9d21f24
LP
155 if (ev.type == EV_KEY && ev.value > 0) {
156
157 switch (ev.code) {
158
159 case KEY_POWER:
160 case KEY_POWER2:
c485437f 161 log_struct(LOG_INFO,
e2cc6eca
LP
162 LOG_MESSAGE("Power key pressed."),
163 LOG_MESSAGE_ID(SD_MESSAGE_POWER_KEY),
c485437f 164 NULL);
7b77ed8c 165
ed4ba7e4 166 manager_handle_action(b->manager, INHIBIT_HANDLE_POWER_KEY, b->manager->handle_power_key, b->manager->power_key_ignore_inhibited, true);
7b77ed8c 167 break;
e9d21f24 168
8e7fd6ad
LP
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
e9d21f24 175 case KEY_SLEEP:
c485437f 176 log_struct(LOG_INFO,
e2cc6eca
LP
177 LOG_MESSAGE("Suspend key pressed."),
178 LOG_MESSAGE_ID(SD_MESSAGE_SUSPEND_KEY),
c485437f 179 NULL);
7b77ed8c 180
ed4ba7e4 181 manager_handle_action(b->manager, INHIBIT_HANDLE_SUSPEND_KEY, b->manager->handle_suspend_key, b->manager->suspend_key_ignore_inhibited, true);
7b77ed8c 182 break;
e9d21f24 183
8e7fd6ad 184 case KEY_SUSPEND:
c485437f 185 log_struct(LOG_INFO,
e2cc6eca
LP
186 LOG_MESSAGE("Hibernate key pressed."),
187 LOG_MESSAGE_ID(SD_MESSAGE_HIBERNATE_KEY),
c485437f 188 NULL);
7b77ed8c 189
ed4ba7e4 190 manager_handle_action(b->manager, INHIBIT_HANDLE_HIBERNATE_KEY, b->manager->handle_hibernate_key, b->manager->hibernate_key_ignore_inhibited, true);
7b77ed8c 191 break;
e9d21f24 192 }
8e7fd6ad 193
e9d21f24
LP
194 } else if (ev.type == EV_SW && ev.value > 0) {
195
7b77ed8c 196 if (ev.code == SW_LID) {
c485437f 197 log_struct(LOG_INFO,
e2cc6eca
LP
198 LOG_MESSAGE("Lid closed."),
199 LOG_MESSAGE_ID(SD_MESSAGE_LID_CLOSED),
c485437f 200 NULL);
65b51162 201
ed4ba7e4 202 b->lid_closed = true;
3c56cab4 203 button_lid_switch_handle_action(b->manager, true);
ed4ba7e4 204 button_install_check_event_source(b);
2d62c530
LP
205
206 } else if (ev.code == SW_DOCK) {
207 log_struct(LOG_INFO,
e2cc6eca
LP
208 LOG_MESSAGE("System docked."),
209 LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_DOCKED),
2d62c530
LP
210 NULL);
211
212 b->docked = true;
65b51162
LP
213 }
214
215 } else if (ev.type == EV_SW && ev.value == 0) {
216
7b77ed8c 217 if (ev.code == SW_LID) {
c485437f 218 log_struct(LOG_INFO,
e2cc6eca
LP
219 LOG_MESSAGE("Lid opened."),
220 LOG_MESSAGE_ID(SD_MESSAGE_LID_OPENED),
c485437f 221 NULL);
7b77ed8c 222
ed4ba7e4
LP
223 b->lid_closed = false;
224 b->check_event_source = sd_event_source_unref(b->check_event_source);
2d62c530
LP
225
226 } else if (ev.code == SW_DOCK) {
227 log_struct(LOG_INFO,
e2cc6eca
LP
228 LOG_MESSAGE("System undocked."),
229 LOG_MESSAGE_ID(SD_MESSAGE_SYSTEM_UNDOCKED),
2d62c530
LP
230 NULL);
231
232 b->docked = false;
e9d21f24
LP
233 }
234 }
235
236 return 0;
237}
238
cc377381
LP
239int button_open(Button *b) {
240 char *p, name[256];
241 int r;
242
243 assert(b);
244
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) {
56f64d95 254 log_warning_errno(errno, "Failed to open %s: %m", b->name);
cc377381
LP
255 return -errno;
256 }
257
258 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
56f64d95 259 log_error_errno(errno, "Failed to get input name: %m");
cc377381
LP
260 r = -errno;
261 goto fail;
262 }
263
ed4ba7e4 264 r = sd_event_add_io(b->manager->event, &b->io_event_source, b->fd, EPOLLIN, button_dispatch, b);
cc377381 265 if (r < 0) {
da927ba9 266 log_error_errno(r, "Failed to add button event: %m");
cc377381
LP
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
2d62c530 280int button_check_switches(Button *b) {
ed4ba7e4 281 uint8_t switches[SW_MAX/8+1] = {};
65b51162
LP
282 assert(b);
283
ed4ba7e4
LP
284 if (b->fd < 0)
285 return -EINVAL;
286
287 if (ioctl(b->fd, EVIOCGSW(sizeof(switches)), switches) < 0)
288 return -errno;
65b51162 289
ed4ba7e4 290 b->lid_closed = (switches[SW_LID/8] >> (SW_LID % 8)) & 1;
2d62c530 291 b->docked = (switches[SW_DOCK/8] >> (SW_DOCK % 8)) & 1;
ed4ba7e4 292
2d62c530 293 if (b->lid_closed)
ed4ba7e4 294 button_install_check_event_source(b);
ed4ba7e4
LP
295
296 return 0;
65b51162 297}