]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-button.c
treewide: use log_*_errno whenever %m is in the format string
[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 <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>
29
30 #include "sd-messages.h"
31 #include "conf-parser.h"
32 #include "util.h"
33 #include "special.h"
34 #include "logind-button.h"
35
36 Button* 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
64 void button_free(Button *b) {
65 assert(b);
66
67 hashmap_remove(b->manager->buttons, b->name);
68
69 sd_event_source_unref(b->io_event_source);
70 sd_event_source_unref(b->check_event_source);
71
72 if (b->fd >= 0) {
73 /* If the device has been unplugged close() returns
74 * ENODEV, let's ignore this, hence we don't use
75 * safe_close() */
76 close(b->fd);
77 }
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_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
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 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_errno(errno, "Failed to open %s: %m", b->name);
255 return -errno;
256 }
257
258 if (ioctl(b->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
259 log_error_errno(errno, "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_errno(r, "Failed to add button event: %m");
267 goto fail;
268 }
269
270 log_info("Watching system buttons on /dev/input/%s (%s)", b->name, name);
271
272 return 0;
273
274 fail:
275 close(b->fd);
276 b->fd = -1;
277 return r;
278 }
279
280 int 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;
297 }