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